def setup_legendre_transform(b): """ Compute a set of matrices containing coefficients to be used in a discrete Legendre transform. The discrete Legendre transform of a data vector s[k] (k=0, ..., 2b-1) is defined as s_hat(l, m) = sum_{k=0}^{2b-1} P_l^m(cos(beta_k)) s[k] for l = 0, ..., b-1 and -l <= m <= l, where P_l^m is the associated Legendre function of degree l and order m, beta_k = ... Computing Fourier Transforms and Convolutions on the 2-Sphere J.R. Driscoll, D.M. Healy FFTs for the 2-Sphere–Improvements and Variations D.M. Healy, Jr., D.N. Rockmore, P.J. Kostelec, and S. Moore :param b: bandwidth of the transform :return: lt, an array of shape (N, 2b), containing samples of the Legendre functions, where N is the number of spectral points for a signal of bandwidth b. """ dim = np.sum(np.arange(b) * 2 + 1) lt = np.empty((2 * b, dim)) beta, _ = S2.linspace(b, grid_type='Driscoll-Healy') sample_points = np.cos(beta) # TODO move quadrature weight computation to S2.py weights = [(1. / b) * np.sin(np.pi * j * 0.5 / b) * np.sum([ 1. / (2 * l + 1) * np.sin((2 * l + 1) * np.pi * j * 0.5 / b) for l in range(b) ]) for j in range(2 * b)] weights = np.array(weights) zeros = np.zeros_like(sample_points) i = 0 for l in range(b): for m in range(-l, l + 1): # Z = np.sqrt(((2 * l + 1) * factorial(l - m)) / float(4 * np.pi * factorial(l + m))) * np.pi / 2 # lt[i, :] = lpmv(m, l, sample_points) * weights * Z # The spherical harmonics code appears to be more stable than the (unnormalized) associated Legendre # function code. # (Note: the spherical harmonics evaluated at alpha=0 is the associated Legendre function)) lt[:, i] = csh(l, m, beta, zeros, normalization='seismology').real * weights * np.pi / 2 i += 1 return lt
def linspace(b, grid_type='SOFT'): """ Compute a linspace on the 3-sphere. Since S3 is ismorphic to SO(3), we use the grid grid_type from: FFTs on the Rotation Group Peter J. Kostelec and Daniel N. Rockmore http://www.cs.dartmouth.edu/~geelong/soft/03-11-060.pdf :param b: :return: """ # alpha = 2 * np.pi * np.arange(2 * b) / (2. * b) # beta = np.pi * (2 * np.arange(2 * b) + 1) / (4. * b) # gamma = 2 * np.pi * np.arange(2 * b) / (2. * b) beta, alpha = S2.linspace(b, grid_type) # According to this paper: # "Sampling sets and quadrature formulae on the rotation group" # We can just tack a sampling grid for S^1 to a sampling grid for S^2 to get a sampling grid for SO(3). gamma = 2 * np.pi * np.arange(2 * b) / (2. * b) return alpha, beta, gamma