Merge pull request #10 from dagss/legendre_table

sharp_legendre_table: Import Legendre table generating code from libpsht
This commit is contained in:
Dag Sverre Seljebotn 2017-06-13 13:07:41 +02:00 committed by GitHub
commit 775601c4ad
9 changed files with 1623 additions and 5 deletions

View file

@ -74,5 +74,7 @@ endif
python: $(all_lib) hdrcopy $(CYTHON_MODULES)
# the following test files are automatic; the sht wrapper test
# must be run manually and requires MPI at the moment..
pytest: python
cd python && nosetests --nocapture libsharp/tests/test_sht.py
cd python && nosetests --nocapture libsharp/tests/test_legendre_table.py libsharp/tests/test_legendre.py

View file

@ -8,7 +8,7 @@ FULL_INCLUDE+= -I$(SD)
HDR_$(PKG):=$(SD)/*.h
LIB_$(PKG):=$(LIBDIR)/libsharp.a
BIN:=sharp_testsuite
LIBOBJ:=sharp_ylmgen_c.o sharp.o sharp_announce.o sharp_geomhelpers.o sharp_almhelpers.o sharp_core.o sharp_legendre.o sharp_legendre_roots.o
LIBOBJ:=sharp_ylmgen_c.o sharp.o sharp_announce.o sharp_geomhelpers.o sharp_almhelpers.o sharp_core.o sharp_legendre.o sharp_legendre_roots.o sharp_legendre_table.o
ALLOBJ:=$(LIBOBJ) sharp_testsuite.o
LIBOBJ:=$(LIBOBJ:%=$(OD)/%)
ALLOBJ:=$(ALLOBJ:%=$(OD)/%)

View file

@ -41,5 +41,6 @@
#include "sharp_lowlevel.h"
#include "sharp_legendre.h"
#include "sharp_legendre_roots.h"
#include "sharp_legendre_table.h"
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,97 @@
/*
* This file is part of libsharp.
*
* Redistribution and use in source and binary forms, with or without
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*! \file sharp_legendre_table.h
* Interface for computing tables of the normalized associated Legendre transform
*
* Copyright (C) 2017 Dag Sverre Seljebotn
* \author Dag Sverre Seljebotn
*
* Note: This code was mainly copied from libpsht; only a small high-level wrapper added
*/
#ifndef SHARP_LEGENDRE_TABLE_H
#define SHARP_LEGENDRE_TABLE_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NO_LEGENDRE_TABLE
/*! Returns a table of the normalized associated Legendre polynomials. m is a single
fixed argument and a table for multiple l and cos(theta) is provided.
(Internally, sin(theta) is also used for part of the computation, making theta
the most convenient argument.)
NOTE: Support for spin-weighted Legendre functions is on the TODO-list. Only spin=0
is supported now.
\param m The m-value to compute a table for; must be >= 0
\param spin The spin parameter; pass 0 for the regular associated Legendre functions.
NOTE: This is present for future compatability, currently only 0 is supported.
\param lmax A table will be provided for l = m .. lmax
\param ntheta How many theta values to evaluate for
\param theta Contiguous 1D array of theta values
\param theta_stride See below
\param l_stride See below
\param spin_stride See below. "ispin" will always be 0 if spin==0, or 0 for positive spin
and 1 for the corresponding negative spin otherwise.
\param out Contiguous 3D array that will receive the output. Each output entry
is assigned to out[itheta * theta_stride + (l - m) * l_stride + ispin * spin_stride].
*/
void sharp_normalized_associated_legendre_table(
ptrdiff_t m,
int spin,
ptrdiff_t lmax,
ptrdiff_t ntheta,
/* contiguous 1D array of theta values to compute for,
contains ntheta values */
double *theta,
/* contiguous 2D array, in "theta-major ordering". Has `ntheta`
rows and `ncols` columns. Indexed as out[itheta * ncols + (l - m)].
If `ncols > lmax - m` then those entries are not accessed.
*/
ptrdiff_t theta_stride,
ptrdiff_t l_stride,
ptrdiff_t spin_stride,
double *out
);
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -59,6 +59,9 @@ cdef extern from "sharp.h":
sharp_alm_info *alm_info, int ntrans, int flags, double *time,
unsigned long long *opcnt) nogil
void sharp_normalized_associated_legendre_table(int m, int spin, int lmax, int ntheta,
double *theta, int theta_stride, int l_stride, int spin_stride, double *out) nogil
cdef extern from "sharp_geomhelpers.h":
void sharp_make_subset_healpix_geom_info(
@ -76,4 +79,3 @@ cdef extern from "sharp_almhelpers.h":
void sharp_make_mmajor_real_packed_alm_info (int lmax, int stride,
int nm, const int *ms, sharp_alm_info **alm_info)

View file

@ -1,8 +1,9 @@
import numpy as np
cimport cython
__all__ = ['legendre_transform', 'legendre_roots', 'sht', 'synthesis', 'adjoint_synthesis',
'analysis', 'adjoint_analysis', 'healpix_grid', 'triangular_order', 'rectangular_order',
'packed_real_order']
'packed_real_order', 'normalized_associated_legendre_table']
def legendre_transform(x, bl, out=None):
@ -254,3 +255,17 @@ cdef class packed_real_order(alm_info):
ms=NULL if ms is None else &ms[0],
alm_info=&self.ainfo)
#
#
#
@cython.boundscheck(False)
def normalized_associated_legendre_table(int lmax, int m, theta):
cdef double[::1] theta_ = np.ascontiguousarray(theta, dtype=np.double)
out = np.zeros((theta_.shape[0], lmax - m + 1), np.double)
cdef double[:, ::1] out_ = out
if lmax < m:
raise ValueError("lmax < m")
with nogil:
sharp_normalized_associated_legendre_table(m, 0, lmax, theta_.shape[0], &theta_[0], lmax - m + 1, 1, 1, &out_[0,0])
return out

View file

@ -56,4 +56,3 @@ def test_legendre_roots():
yield check_legendre_roots, 1
yield check_legendre_roots, 32
yield check_legendre_roots, 33
yield check_legendre_roots, 128

View file

@ -0,0 +1,35 @@
import numpy as np
from numpy.testing import assert_almost_equal
from nose.tools import eq_, ok_
from libsharp import normalized_associated_legendre_table
from scipy.special import sph_harm, p_roots
def test_compare_legendre_table_with_scipy():
def test(theta, m, lmax):
Plm = normalized_associated_legendre_table(lmax, m, theta)
Plm_p = sph_harm(m, np.arange(m, lmax + 1), 0, theta)[None, :]
if not np.allclose(Plm_p, Plm):
print Plm_p
print Plm
return ok_, np.allclose(Plm_p, Plm)
yield test(np.pi/2, 0, 10)
yield test(np.pi/4, 0, 10)
yield test(3 * np.pi/4, 0, 10)
yield test(np.pi/4, 1, 4)
yield test(np.pi/4, 2, 4)
yield test(np.pi/4, 50, 50)
yield test(np.pi/2, 49, 50)
def test_legendre_table_wrapper_logic():
# tests the SSE 2 logic in the high-level wrapper by using an odd number of thetas
theta = np.asarray([np.pi/2, np.pi/4, 3 * np.pi / 4])
m = 3
lmax = 10
Plm = normalized_associated_legendre_table(lmax, m, theta)
assert np.allclose(Plm[1, :], normalized_associated_legendre_table(lmax, m, np.pi/4)[0, :])
assert np.allclose(Plm[2, :], normalized_associated_legendre_table(lmax, m, 3 * np.pi/4)[0, :])