Пример #1
0
    def _init_pswf_func2d(self, c, eps):
        """
        Initialize the whole set of PSWF functions with the input bandlimit and error

        :param c: bandlimit (>0) can be estimated by beta * pi * rcut
        :param eps: error tolerance
        :return:
            alpha_all (list of arrays):
            alpha = alpha_all[i] contains all the eigenvalues for N=i such that
                lambda > eps, where lambda is the normalized  alpha values (i.e.
                lambda is between 0 and 1), given by lambda=sqrt(c*np.absolute(alpha)/(2*pi)).
            d_vec_all (list of 2D lists): the corresponding eigenvectors for alpha_all.
            n_order_length_vec (list of ints): n_order_length_vec[i] = len(alpha_all[i])
        """
        d_vec_all = []
        alpha_all = []
        n_order_length_vec = []

        m = 0
        n = int(np.ceil(2 * c / np.pi))
        r, w = lgwt(n, 0, 1)

        cons = c / 2 / np.pi
        while True:
            alpha, d_vec, a = self.pswf_func2d(m, n, c, eps, r, w)

            lambda_var = np.sqrt(cons * np.absolute(alpha))

            n_end = np.where(lambda_var <= eps)[0]

            if len(n_end) != 0:
                n_end = n_end[0]
                if n_end == 0:
                    break
                n_order_length_vec.append(n_end)
                alpha_all.append(alpha[:n_end])
                d_vec_all.append(d_vec[:, :n_end])
                m += 1
                n = n_end + 1
            else:
                n *= 2
                r, w = lgwt(n, 0, 1)

        return d_vec_all, alpha_all, n_order_length_vec
Пример #2
0
def filter_to_fb_mat(h_fun, fbasis):
    """
    Convert a nonradial function in k space into a basis representation.

    :param h_fun: The function form in k space.
    :param fbasis: The basis object for expanding.

    :return: a BlkDiagMatrix instance representation using the
    `fbasis` expansion.
    """

    # These form a circular dependence, import locally until time to clean up.
    from aspire.basis import FFBBasis2D
    from aspire.basis.basis_utils import lgwt

    if not isinstance(fbasis, FFBBasis2D):
        raise NotImplementedError("Currently only fast FB method is supported")
    # Set same dimensions as basis object
    n_k = fbasis.n_r
    n_theta = fbasis.n_theta
    radial = fbasis.get_radial()

    # get 2D grid in polar coordinate
    k_vals, wts = lgwt(n_k, 0, 0.5, dtype=fbasis.dtype)
    k, theta = np.meshgrid(k_vals,
                           np.arange(n_theta) * 2 * np.pi / (2 * n_theta),
                           indexing="ij")

    # Get function values in polar 2D grid and average out angle contribution
    omegax = k * np.cos(theta)
    omegay = k * np.sin(theta)
    omega = 2 * np.pi * np.vstack((omegax.flatten("C"), omegay.flatten("C")))
    h_vals2d = h_fun(omega).reshape(n_k, n_theta).astype(fbasis.dtype)
    h_vals = np.sum(h_vals2d, axis=1) / n_theta

    # Represent 1D function values in fbasis
    h_fb = BlkDiagMatrix.empty(2 * fbasis.ell_max + 1, dtype=fbasis.dtype)
    ind_ell = 0
    for ell in range(0, fbasis.ell_max + 1):
        k_max = fbasis.k_max[ell]
        rmat = 2 * k_vals.reshape(n_k, 1) * fbasis.r0[0:k_max, ell].T
        fb_vals = np.zeros_like(rmat)
        ind_radial = np.sum(fbasis.k_max[0:ell])
        fb_vals[:, 0:k_max] = radial[ind_radial:ind_radial + k_max].T
        h_fb_vals = fb_vals * h_vals.reshape(n_k, 1)
        h_fb_ell = fb_vals.T @ (h_fb_vals * k_vals.reshape(n_k, 1) *
                                wts.reshape(n_k, 1))
        h_fb[ind_ell] = h_fb_ell
        ind_ell += 1
        if ell > 0:
            h_fb[ind_ell] = h_fb[ind_ell - 1]
            ind_ell += 1

    return h_fb
Пример #3
0
def filter_to_fb_mat(h_fun, fbasis):
    """
    Convert a nonradial function in k space into a basis representation.

    :param h_fun: The function form in k space.
    :param fbasis: The basis object for expanding.

    :return: a BlkDiagMatrix instance representation using the
    `fbasis` expansion.
    """

    if not isinstance(fbasis, FFBBasis2D):
        raise NotImplementedError('Currently only fast FB method is supported')
    # Set same dimensions as basis object
    n_k = int(np.ceil(4 * fbasis.rcut * fbasis.kcut))
    n_theta = np.ceil(16 * fbasis.kcut * fbasis.rcut)
    n_theta = int((n_theta + np.mod(n_theta, 2)) / 2)

    # get 2D grid in polar coordinate
    k_vals, wts = lgwt(n_k, 0, 0.5)
    k, theta = np.meshgrid(
        k_vals, np.arange(n_theta) * 2 * np.pi / (2 * n_theta), indexing='ij')

    # Get function values in polar 2D grid and average out angle contribution
    omegax = k * np.cos(theta)
    omegay = k * np.sin(theta)
    omega = 2 * np.pi * np.vstack((omegax.flatten('C'), omegay.flatten('C')))
    h_vals2d = h_fun(omega).reshape(n_k, n_theta)
    h_vals = np.sum(h_vals2d, axis=1)/n_theta

    # Represent 1D function values in fbasis
    h_fb = BlkDiagMatrix.empty(2 * fbasis.ell_max + 1, dtype=fbasis.dtype)
    ind = 0
    for ell in range(0, fbasis.ell_max+1):
        k_max = fbasis.k_max[ell]
        rmat = 2*k_vals.reshape(n_k, 1)*fbasis.r0[0:k_max, ell].T
        fb_vals = np.zeros_like(rmat)
        for ik in range(0, k_max):
            fb_vals[:, ik] = jv(ell, rmat[:, ik])
        fb_nrms = 1/np.sqrt(2)*abs(jv(ell+1, fbasis.r0[0:k_max, ell].T))/2
        fb_vals = fb_vals/fb_nrms
        h_fb_vals = fb_vals*h_vals.reshape(n_k, 1)
        h_fb_ell = fb_vals.T @ (
            h_fb_vals * k_vals.reshape(n_k, 1) * wts.reshape(n_k, 1))
        h_fb[ind] = h_fb_ell
        ind += 1
        if ell > 0:
            h_fb[ind] = h_fb[ind-1]
            ind += 1

    return h_fb
Пример #4
0
    def _precomp(self):
        """
        Precomute the basis functions on a polar Fourier grid

        Gaussian quadrature points and weights are also generated.
        The sampling criterion requires n_r=4*c*R and n_theta= 16*c*R.

        """
        n_r = self.n_r
        n_theta = self.n_theta
        r, w = lgwt(n_r, 0.0, self.kcut, dtype=self.dtype)

        radial = np.zeros(shape=(np.sum(self.k_max), n_r), dtype=self.dtype)
        ind_radial = 0
        for ell in range(0, self.ell_max + 1):
            for k in range(1, self.k_max[ell] + 1):
                radial[ind_radial] = jv(ell,
                                        self.r0[k - 1, ell] * r / self.kcut)
                # NOTE: We need to remove the factor due to the discretization here
                # since it is already included in our quadrature weights
                # Only normalized by the radial part of basis function
                nrm = 1 / (np.sqrt(np.prod(
                    self.sz))) * self.radial_norms[ind_radial]
                radial[ind_radial] /= nrm
                ind_radial += 1

        # Only calculate "positive" frequencies in one half-plane.
        freqs_x = m_reshape(r, (n_r, 1)) @ m_reshape(
            np.cos(
                np.arange(n_theta, dtype=self.dtype) * 2 * pi / (2 * n_theta)),
            (1, n_theta),
        )
        freqs_y = m_reshape(r, (n_r, 1)) @ m_reshape(
            np.sin(
                np.arange(n_theta, dtype=self.dtype) * 2 * pi / (2 * n_theta)),
            (1, n_theta),
        )
        freqs = np.vstack((freqs_y[np.newaxis, ...], freqs_x[np.newaxis, ...]))

        return {
            "gl_nodes": r,
            "gl_weights": w,
            "radial": radial,
            "freqs": freqs
        }
Пример #5
0
    def testLGQuad(self):
        resx, resw = lgwt(
            ndeg=10,  # degree
            a=0.0,  # start x
            b=0.5  # end x
        )
        self.assertTrue(np.allclose(
            resx,
            [0.00652337, 0.03373416, 0.08014761, 0.14165115, 0.21278142,
             0.28721858, 0.35834885, 0.41985239, 0.46626584, 0.49347663
             ]
        ))

        self.assertTrue(np.allclose(
            resw,
            [0.01666784, 0.03736284, 0.05477159, 0.06731668, 0.07388106,
             0.07388106, 0.06731668, 0.05477159, 0.03736284, 0.01666784
             ]
        ))
Пример #6
0
    def _precomp(self):
        """
        Precomute the basis functions on a polar Fourier grid

        Gaussian quadrature points and weights are also generated.
        The sampling criterion requires n_r=4*c*R and n_theta= 16*c*R.
        
        """
        n_r = int(np.ceil(4 * self.rcut * self.kcut))
        r, w = lgwt(n_r, 0.0, self.kcut)

        radial = np.zeros(shape=(n_r, np.sum(self.k_max)))
        ind_radial = 0
        for ell in range(0, self.ell_max + 1):
            for k in range(1, self.k_max[ell] + 1):
                radial[:, ind_radial] = jv(ell, self.r0[k - 1, ell] * r / self.kcut)
                # NOTE: We need to remove the factor due to the discretization here
                # since it is already included in our quadrature weights
                nrm = 1 / (np.sqrt(np.prod(self.sz))) * self.basis_norm_2d(ell, k)
                radial[:, ind_radial] /= nrm
                ind_radial += 1

        n_theta = np.ceil(16 * self.kcut * self.rcut)
        n_theta = int((n_theta + np.mod(n_theta, 2)) / 2)

        # Only calculate "positive" frequencies in one half-plane.
        freqs_x = m_reshape(r, (n_r, 1)) @ m_reshape(
            np.cos(np.arange(n_theta) * 2 * pi / (2 * n_theta)), (1, n_theta))
        freqs_y = m_reshape(r, (n_r, 1)) @ m_reshape(
            np.sin(np.arange(n_theta) * 2 * pi / (2 * n_theta)), (1, n_theta))
        freqs = np.vstack((freqs_x[np.newaxis, ...], freqs_y[np.newaxis, ...]))

        return {
            'gl_nodes': r,
            'gl_weights': w,
            'radial': radial,
            'freqs': freqs
        }
Пример #7
0
    def _generate_pswf_radial_quad(self, n, bandlimit, phi_approximate_error,
                                   lambda_max):
        """
        Generate Gaussian quadrature points and weights for the radical parts of 2D PSWFs
        """
        x, w = lgwt(20 * n, 0, 1)

        big_n = 0

        x_as_mat = x.reshape((len(x), 1))

        alpha_n, d_vec, approx_length = self.pswf_func2d(
            big_n, n, bandlimit, phi_approximate_error, x, w)

        cut_indices = np.where(
            bandlimit / 2 / np.pi * np.absolute(alpha_n) < lambda_max)[0]

        if len(cut_indices) == 0:
            k = len(alpha_n)
        else:
            k = cut_indices[0]

        if k % 2 == 0:
            k = k + 1

        range_array = np.arange(approx_length).reshape((1, approx_length))

        idx_for_quad_nodes = int((k + 1) / 2)
        num_quad_pts = idx_for_quad_nodes - 1

        phi_zeros = self._find_initial_nodes(x, n, bandlimit / 2,
                                             phi_approximate_error,
                                             idx_for_quad_nodes)

        def phi_for_quad_weights(t):
            return np.dot(t_x_mat_dot(t, big_n, range_array, approx_length),
                          d_vec[:, :k - 1])

        b = np.dot(w * np.sqrt(x), phi_for_quad_weights(x_as_mat))

        a = phi_for_quad_weights(phi_zeros.reshape(
            (len(phi_zeros), 1))).transpose() * np.sqrt(phi_zeros)
        init_quad_weights = lstsq(a, b, rcond=None)
        init_quad_weights = init_quad_weights[0]
        tolerance = np.spacing(1)

        def obj_func(quad_rule):
            q = quad_rule.reshape((len(quad_rule), 1))
            temp = np.dot((phi_for_quad_weights(q[:num_quad_pts]) *
                           np.sqrt(q[:num_quad_pts])).transpose(),
                          q[num_quad_pts:])
            temp = temp.reshape(temp.shape[0])
            return temp - b

        arr_to_send = np.concatenate((phi_zeros, init_quad_weights))
        quad_rule_final = least_squares(obj_func,
                                        arr_to_send,
                                        xtol=tolerance,
                                        ftol=tolerance,
                                        max_nfev=1000)
        quad_rule_final = quad_rule_final.x
        quad_rule_pts = quad_rule_final[:num_quad_pts]
        quad_rule_weights = quad_rule_final[num_quad_pts:]
        return quad_rule_pts, quad_rule_weights
Пример #8
0
    def _precomp(self):
        """
        Precomute the basis functions on a polar Fourier 3D grid

        Gaussian quadrature points and weights are also generated
        in radical and phi dimensions.
        """
        n_r = int(self.ell_max + 1)
        n_theta = int(2 * self.sz[0])
        n_phi = int(self.ell_max + 1)

        r, wt_r = lgwt(n_r, 0.0, self.kcut)
        z, wt_z = lgwt(n_phi, -1, 1)
        r = m_reshape(r, (n_r, 1))
        wt_r = m_reshape(wt_r, (n_r, 1))
        z = m_reshape(z, (n_phi, 1))
        wt_z = m_reshape(wt_z, (n_phi, 1))
        phi = np.arccos(z)
        wt_phi = wt_z
        theta = 2 * pi * np.arange(n_theta).T / (2 * n_theta)
        theta = m_reshape(theta, (n_theta, 1))

        # evaluate basis function in the radial dimension
        radial_wtd = np.zeros(shape=(n_r, np.max(self.k_max),
                                     self.ell_max + 1))
        for ell in range(0, self.ell_max + 1):
            k_max_ell = self.k_max[ell]
            rmat = r * self.r0[0:k_max_ell, ell].T / self.kcut
            radial_ell = np.zeros_like(rmat)
            for ik in range(0, k_max_ell):
                radial_ell[:, ik] = sph_bessel(ell, rmat[:, ik])
            nrm = np.abs(sph_bessel(ell + 1, self.r0[0:k_max_ell, ell].T) / 4)
            radial_ell = radial_ell / nrm
            radial_ell_wtd = r**2 * wt_r * radial_ell
            radial_wtd[:, 0:k_max_ell, ell] = radial_ell_wtd

        # evaluate basis function in the phi dimension
        ang_phi_wtd_even = []
        ang_phi_wtd_odd = []
        for m in range(0, self.ell_max + 1):
            n_even_ell = int(
                np.floor((self.ell_max - m) / 2) + 1 -
                np.mod(self.ell_max, 2) * np.mod(m, 2))
            n_odd_ell = int(self.ell_max - m + 1 - n_even_ell)
            phi_wtd_m_even = np.zeros((n_phi, n_even_ell), dtype=phi.dtype)
            phi_wtd_m_odd = np.zeros((n_phi, n_odd_ell), dtype=phi.dtype)

            ind_even = 0
            ind_odd = 0
            for ell in range(m, self.ell_max + 1):
                phi_m_ell = norm_assoc_legendre(ell, m, z)
                nrm_inv = np.sqrt(0.5 / pi)
                phi_m_ell = nrm_inv * phi_m_ell
                phi_wtd_m_ell = wt_phi * phi_m_ell
                if np.mod(ell, 2) == 0:
                    phi_wtd_m_even[:, ind_even] = phi_wtd_m_ell[:, 0]
                    ind_even = ind_even + 1
                else:
                    phi_wtd_m_odd[:, ind_odd] = phi_wtd_m_ell[:, 0]
                    ind_odd = ind_odd + 1

            ang_phi_wtd_even.append(phi_wtd_m_even)
            ang_phi_wtd_odd.append(phi_wtd_m_odd)

        # evaluate basis function in the theta dimension
        ang_theta = np.zeros((n_theta, 2 * self.ell_max + 1),
                             dtype=theta.dtype)

        ang_theta[:, 0:self.ell_max] = np.sqrt(2) * np.sin(
            theta @ m_reshape(np.arange(self.ell_max, 0, -1),
                              (1, self.ell_max)))
        ang_theta[:, self.ell_max] = np.ones(n_theta, dtype=theta.dtype)
        ang_theta[:,
                  self.ell_max + 1:2 * self.ell_max + 1] = np.sqrt(2) * np.cos(
                      theta @ m_reshape(np.arange(1, self.ell_max + 1),
                                        (1, self.ell_max)))

        ang_theta_wtd = (2 * pi / n_theta) * ang_theta

        theta_grid, phi_grid, r_grid = np.meshgrid(theta,
                                                   phi,
                                                   r,
                                                   sparse=False,
                                                   indexing='ij')
        fourier_x = m_flatten(r_grid * np.cos(theta_grid) * np.sin(phi_grid))
        fourier_y = m_flatten(r_grid * np.sin(theta_grid) * np.sin(phi_grid))
        fourier_z = m_flatten(r_grid * np.cos(phi_grid))
        fourier_pts = 2 * pi * np.vstack(
            (fourier_x[np.newaxis, ...], fourier_y[np.newaxis, ...],
             fourier_z[np.newaxis, ...]))

        return {
            'radial_wtd': radial_wtd,
            'ang_phi_wtd_even': ang_phi_wtd_even,
            'ang_phi_wtd_odd': ang_phi_wtd_odd,
            'ang_theta_wtd': ang_theta_wtd,
            'fourier_pts': fourier_pts
        }