diff --git a/ci/generate_test_data.sh b/ci/generate_test_data.sh new file mode 100755 index 0000000000000000000000000000000000000000..8e55efd8b8eecc82d65c8bc7ef32bcd788028d50 --- /dev/null +++ b/ci/generate_test_data.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd test/data +for filename in *.py; do + ./$filename +done +cd ../.. diff --git a/nifty/spaces/hp_space/hp_space.py b/nifty/spaces/hp_space/hp_space.py index cf8ae570fe50da34617f98fc592a8b146e240758..e5012c60cda62ea1866602778d0a209173328a17 100644 --- a/nifty/spaces/hp_space/hp_space.py +++ b/nifty/spaces/hp_space/hp_space.py @@ -149,7 +149,7 @@ class HPSpace(Space): dtype=self.dtype) def weight(self, x, power=1, axes=None, inplace=False): - weight = ((4*np.pi) / (12 * self.nside**2)) ** power + weight = ((4 * np.pi) / (12 * self.nside**2))**power if inplace: x *= weight diff --git a/test/common.py b/test/common.py index 5908aef4ccdeb35f33fe37bdd25675baa0059f82..cb711e8556ab7aebeb249e463d54f700327039f4 100644 --- a/test/common.py +++ b/test/common.py @@ -1,10 +1,39 @@ +import d2o +import numpy as np from nose_parameterized import parameterized +from nifty import RGSpace, LMSpace, HPSpace, GLSpace +from string import strip + + +def pretty_str(obj): + if type(obj) == list: + return " ".join(pretty_str(x) for x in obj) + if type(obj) == RGSpace: + return type(obj).__name__ + elif type(obj) == LMSpace: + return type(obj).__name__ + elif type(obj) == HPSpace: + return type(obj).__name__ + elif type(obj) == GLSpace: + return type(obj).__name__ + elif isinstance(obj, d2o.distributed_data_object): + return 'd2o' + elif type(obj) == dict: + if 'error' in obj: + return 'error_' + obj['error'].__name__ + else: + return '' + elif type(obj) == np.ndarray: + return 'DATA' + else: + return str(obj) def custom_name_func(testcase_func, param_num, param): return "%s_%s" % ( testcase_func.__name__, - parameterized.to_safe_name("_".join(str(x) for x in param.args)), + strip(parameterized.to_safe_name( + " ".join(pretty_str(x) for x in param.args)), '_') ) diff --git a/test/data/gen_gl_space_data.py b/test/data/gen_gl_space_data.py new file mode 100644 index 0000000000000000000000000000000000000000..79efce8467a38d0938454f17d3a946305022d685 --- /dev/null +++ b/test/data/gen_gl_space_data.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import itertools +import numpy as np +import libsharp_wrapper_gl as gl + +# deterministic +np.random.seed(42) + + +def distance_array(nlat, nlon, latitude, longitude): + lat = latitude * (np.pi / (nlat - 1)) + lon = longitude * (2 * np.pi / (nlon - 1)) + # Vincenty formula: https://en.wikipedia.org/wiki/Great-circle_distance + # phi_1, lambda_1 = lat, lon + # phi_2, lambda_2 = 0 + numerator = np.sqrt((np.cos(0) * np.sin(lon - 0))**2 + + ((np.cos(lat) * np.sin(0)) - + (np.sin(lat) * np.cos(0) * np.cos(lon - 0)))**2) + denominator = ( + np.sin(lat) * np.sin(0)) + (np.cos(lat) * np.cos(0) * np.cos(lon - 0)) + return np.arctan(numerator/denominator) + + +# for GLSpace(nlat=2, nlon=3) +da_0 = np.array( + [distance_array(2, 3, *divmod(idx, 3)) for idx in np.arange(6)]) + +# for GLSpace(nlat=2, nlon=3) +weight_0 = np.array(list(itertools.chain.from_iterable( + itertools.repeat(x, 3) for x in gl.vol(2)))) +w_0_x = np.random.rand(6) +w_0_res = w_0_x * weight_0 + +weight_1 = np.array(list(itertools.chain.from_iterable( + itertools.repeat(x, 3) for x in gl.vol(2)))) +weight_1 = weight_1.reshape([1, 1, 6]) +w_1_x = np.random.rand(32, 16, 6) +w_1_res = w_1_x * weight_1 + +# write everything to disk +np.savez( + 'gl_space', da_0=da_0, w_0_x=w_0_x, w_0_res=w_0_res, w_1_x=w_1_x, + w_1_res=w_1_res) diff --git a/test/data/gen_hp_space_data.py b/test/data/gen_hp_space_data.py new file mode 100755 index 0000000000000000000000000000000000000000..b3e6a804fd4122a91242a7a5cbb18bfde6002d19 --- /dev/null +++ b/test/data/gen_hp_space_data.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import numpy as np +import healpy as hp + +# deterministic +np.random.seed(42) + +# for HPSpace(nside=2) +da_0 = np.array([np.arccos(hp.pix2vec(2, idx)[0]) for idx in np.arange(48)]) + +# for HPSpace(nside=2) +w_0_x = np.random.rand(48) +w_0_res = w_0_x * ((4 * np.pi) / 48) +w_1_res = w_0_x * (((4 * np.pi) / 48)**2) + +# write everything to disk +np.savez('hp_space', da_0=da_0, w_0_x=w_0_x, w_0_res=w_0_res, w_1_x=w_0_x, + w_1_res=w_1_res) diff --git a/test/data/gl_space.npz b/test/data/gl_space.npz new file mode 100644 index 0000000000000000000000000000000000000000..dca748e000573e798df7083a536383a075385646 Binary files /dev/null and b/test/data/gl_space.npz differ diff --git a/test/data/hp_space.npz b/test/data/hp_space.npz new file mode 100644 index 0000000000000000000000000000000000000000..944116e089962472f11a6b9cc4739d2e0557228c Binary files /dev/null and b/test/data/hp_space.npz differ diff --git a/test/test_nifty.py b/test/test_nifty.py deleted file mode 100644 index eb9109689e2ffa429939fec6d6939b062a9de8bc..0000000000000000000000000000000000000000 --- a/test/test_nifty.py +++ /dev/null @@ -1,30 +0,0 @@ -import nifty as nt -import numpy as np - -import unittest - -def weighted_np_transform(val, domain, codomain, axes=None): - if codomain.harmonic: - # correct for forward fft - val = domain.weight(val, power=1, axes=axes) - # Perform the transformation - - Tval = np.fft.fftn(val, axes=axes) - - if not codomain.harmonic: - # correct for inverse fft - Tval = codomain.weight(Tval, power=-1, axes=axes) - - return Tval - -def test_simple_fft(): - x = nt.RGSpace((16,)) - x_p = nt.FFTOperator.get_default_codomain(x) - f = nt.Field((x, x), val=1) - val_p = np.ones((16,16)) - fft = nt.FFTOperator(x) - - assert np.allclose( - fft(f, spaces=(1,)).val, - weighted_np_transform(val_p, x, x_p, axes=(1,)) - ) diff --git a/test/test_spaces/test_gl_space.py b/test/test_spaces/test_gl_space.py new file mode 100644 index 0000000000000000000000000000000000000000..5ed5cf27caf2e065afda7ef80419c3118dd0ec28 --- /dev/null +++ b/test/test_spaces/test_gl_space.py @@ -0,0 +1,87 @@ +import unittest +import numpy as np + +from numpy.testing import assert_, assert_equal, assert_raises,\ + assert_almost_equal +from nose.plugins.skip import SkipTest +from nifty import GLSpace +from nifty.config import dependency_injector as di +from test.common import expand + +# [nlat, nlon, dtype, expected] +CONSTRUCTOR_CONFIGS = [ + [2, None, None, { + 'nlat': 2, + 'nlon': 3, + 'harmonic': False, + 'shape': (6,), + 'dim': 6, + 'total_volume': 4 * np.pi, + 'dtype': np.dtype('float64') + }], + [1, None, None, { + 'error': ValueError + }], + [5, None, None, { + 'error': ValueError + }] + ] + + +def get_distance_array_configs(): + npzfile = np.load('test/data/gl_space.npz') + return [[2, None, None, npzfile['da_0']]] + + +def get_weight_configs(): + npzfile = np.load('test/data/gl_space.npz') + return [ + [npzfile['w_0_x'], 1, None, False, npzfile['w_0_res']], + [npzfile['w_0_x'], 1, None, True, npzfile['w_0_res']], + [npzfile['w_1_x'], 1, None, True, npzfile['w_1_res']], + ] + + +class GLSpaceInterfaceTests(unittest.TestCase): + @expand([['nlat', int], + ['nlon', int]]) + def test_property_ret_type(self, attribute, expected_type): + try: + g = GLSpace() + except ImportError: + raise SkipTest + assert_(isinstance(getattr(g, attribute), expected_type)) + + +class GLSpaceFunctionalityTests(unittest.TestCase): + @expand(CONSTRUCTOR_CONFIGS) + def test_constructor(self, nlat, nlon, dtype, expected): + if 'libsharp_wrapper_gl' not in di: + raise SkipTest + else: + if 'error' in expected: + with assert_raises(expected['error']): + GLSpace(nlat, nlon, dtype) + else: + g = GLSpace(nlat, nlon, dtype) + for key, value in expected.iteritems(): + assert_equal(getattr(g, key), value) + + @expand(get_weight_configs()) + def test_weight(self, x, power, axes, inplace, expected): + if 'libsharp_wrapper_gl' not in di: + raise SkipTest + else: + g = GLSpace(2) + res = g.weight(x, power, axes, inplace) + assert_almost_equal(res, expected) + if inplace: + assert_(x is res) + + @expand(get_distance_array_configs()) + def test_distance_array(self, nlat, nlon, dtype, expected): + if 'libsharp_wrapper_gl' not in di: + raise SkipTest + else: + g = GLSpace(nlat, nlon, dtype) + assert_almost_equal(g.get_distance_array('not').data, expected) diff --git a/test/test_spaces/test_hp_space.py b/test/test_spaces/test_hp_space.py new file mode 100644 index 0000000000000000000000000000000000000000..fa7555cb531e3433f7197bdd17a0b6862d7bd4f1 --- /dev/null +++ b/test/test_spaces/test_hp_space.py @@ -0,0 +1,85 @@ +import unittest +import numpy as np + +from numpy.testing import assert_, assert_equal, assert_raises,\ + assert_almost_equal +from nose.plugins.skip import SkipTest +from nifty import HPSpace +from nifty.config import dependency_injector as di +from test.common import expand + +# [nside, dtype, expected] +CONSTRUCTOR_CONFIGS = [ + [2, None, { + 'nside': 2, + 'harmonic': False, + 'shape': (48,), + 'dim': 48, + 'total_volume': 4 * np.pi, + 'dtype': np.dtype('float64') + }], + [5, None, { + 'error': ValueError + }], + [1, None, { + 'error': ValueError + }] + ] + + +def get_distance_array_configs(): + npzfile = np.load('test/data/hp_space.npz') + return [[2, None, npzfile['da_0']]] + + +def get_weight_configs(): + npzfile = np.load('test/data/hp_space.npz') + return [ + [npzfile['w_0_x'], 1, None, False, npzfile['w_0_res']], + [npzfile['w_0_x'], 1, None, True, npzfile['w_0_res']], + [npzfile['w_1_x'], 2, None, False, npzfile['w_1_res']], + ] + + +class HPSpaceInterfaceTests(unittest.TestCase): + @expand([['nside', int]]) + def test_property_ret_type(self, attribute, expected_type): + try: + x = HPSpace() + except ImportError: + raise SkipTest + assert_(isinstance(getattr(x, attribute), expected_type)) + + +class HPSpaceFunctionalityTests(unittest.TestCase): + @expand(CONSTRUCTOR_CONFIGS) + def test_constructor(self, nside, dtype, expected): + if 'healpy' not in di: + raise SkipTest + else: + if 'error' in expected: + with assert_raises(expected['error']): + HPSpace(nside, dtype) + else: + h = HPSpace(nside, dtype) + for key, value in expected.iteritems(): + assert_equal(getattr(h, key), value) + + @expand(get_weight_configs()) + def test_weight(self, x, power, axes, inplace, expected): + if 'healpy' not in di: + raise SkipTest + else: + h = HPSpace(2) + res = h.weight(x, power, axes, inplace) + assert_almost_equal(res, expected) + if inplace: + assert_(x is res) + + @expand(get_distance_array_configs()) + def test_distance_array(self, nside, dtype, expected): + if 'healpy' not in di: + raise SkipTest + else: + h = HPSpace(nside, dtype) + assert_almost_equal(h.get_distance_array('not').data, expected) diff --git a/test/test_spaces/test_interface.py b/test/test_spaces/test_interface.py index fd6d7cbef90d3ddd5697bfbece623016b5c91f66..03be5c80edfda612e57d57030bee94b36d9006d4 100644 --- a/test/test_spaces/test_interface.py +++ b/test/test_spaces/test_interface.py @@ -2,20 +2,62 @@ import unittest import numpy as np from itertools import product -from numpy.testing import assert_ +from d2o import distributed_data_object +from types import LambdaType +from numpy.testing import assert_, assert_raises, assert_equal from nifty import RGSpace, LMSpace, GLSpace, HPSpace +from nifty.config import dependency_injector as di from test.common import expand -SPACES = [RGSpace((4,)), LMSpace(5), GLSpace(4), HPSpace(4)] +def generate_spaces(): + spaces = [RGSpace(4)] + if 'healpy' in di: + spaces.append(HPSpace(4)) + if 'libsharp_wrapper_gl' in di: + spaces.append(GLSpace(4)) + if 'healpy' in di or 'libsharp_wrapper_gl' in di: + spaces.append(LMSpace(5)) -class SpaceInterfaceTestCase(unittest.TestCase): - @expand(product(SPACES, [['dtype', np.dtype], - ['harmonic', bool], - ['shape', tuple], - ['dim', int], - ['total_volume', np.float]])) - def test_return_types(self, space, attr_expected_type): - assert_(isinstance(getattr( - space, attr_expected_type[0]), attr_expected_type[1])) + return spaces + + +class SpaceInterfaceTests(unittest.TestCase): + def test_dependency_handling(self): + if 'healpy' not in di and 'libsharp_wrapper_gl' not in di: + with assert_raises(ImportError): + LMSpace(5) + elif 'healpy' not in di: + with assert_raises(ImportError): + HPSpace(4) + elif 'libsharp_wrapper_gl' not in di: + with assert_raises(ImportError): + GLSpace(4) + + @expand(product(generate_spaces(), [['dtype', np.dtype], + ['harmonic', bool], + ['shape', tuple], + ['dim', int], + ['total_volume', np.float]])) + def test_property_ret_type(self, space, attr_expected_type): + assert_( + isinstance(getattr( + space, + attr_expected_type[0] + ), attr_expected_type[1]) + ) + + @expand(product(generate_spaces(), [ + ['get_fft_smoothing_kernel_function', None, LambdaType], + ['get_fft_smoothing_kernel_function', 2.0, LambdaType], + ])) + def test_method_ret_type(self, space, method_expected_type): + + assert_equal( + type(getattr( + space, + method_expected_type[0])(*method_expected_type[1:-1]) + ), + method_expected_type[-1] + ) diff --git a/test/test_spaces/test_lm_space.py b/test/test_spaces/test_lm_space.py new file mode 100644 index 0000000000000000000000000000000000000000..e4fa08597673aea51889ff689d7eb5957dcd27ad --- /dev/null +++ b/test/test_spaces/test_lm_space.py @@ -0,0 +1,69 @@ +import unittest +import numpy as np + +from numpy.testing import assert_, assert_equal, assert_raises +from nose.plugins.skip import SkipTest +from nifty import LMSpace +from nifty.config import dependency_injector as di +from test.common import expand + +# [lmax, dtype, expected] +INIT_CONFIGS = [ + [5, None, { + 'lmax': 5, + 'mmax': 5, + 'shape': (36,), + 'harmonic': True, + 'dim': 36, + 'total_volume': 36.0, + 'dtype': np.dtype('float64') + }], + [7, np.dtype('float64'), { + 'lmax': 7, + 'mmax': 7, + 'shape': (64,), + 'harmonic': True, + 'dim': 64, + 'total_volume': 64.0, + 'dtype': np.dtype('float64') + }], + [-1, None, { + 'error': ValueError + }] + ] + + +class LMSpaceIntefaceTests(unittest.TestCase): + @expand([['lmax', int], + ['mmax', int], + ['dim', int]]) + def test_properties(self, attribute, expected_type): + try: + x = LMSpace(7) + except ImportError: + raise SkipTest + assert_(isinstance(getattr(x, attribute), expected_type)) + + +class LMSpaceFunctionalityTests(unittest.TestCase): + @expand(INIT_CONFIGS) + def test_constructor(self, lmax, dtype, expected): + if 'libsharp_wrapper_gl' not in di or 'healpy' not in di: + raise SkipTest + else: + if 'error' in expected: + with assert_raises(expected['error']): + LMSpace(lmax, dtype) + else: + l = LMSpace(lmax, dtype) + for key, value in expected.iteritems(): + assert_equal(getattr(l, key), value) + + def test_hermitian_decomposition(self): + pass + + def test_weight(self): + pass + + def test_distance_array(self): + pass diff --git a/test/test_spaces/test_rg_space.py b/test/test_spaces/test_rg_space.py new file mode 100644 index 0000000000000000000000000000000000000000..423d5c8e72ff5b95d10c74497fd4831c9455619a --- /dev/null +++ b/test/test_spaces/test_rg_space.py @@ -0,0 +1,99 @@ +from __future__ import division + +import unittest +import numpy as np + +from numpy.testing import assert_, assert_equal +from nifty import RGSpace +from test.common import expand + +# [shape, zerocenter, distances, harmonic, dtype, expected] +INIT_CONFIGS = [ + [(8,), False, None, False, None, + { + 'shape': (8,), + 'zerocenter': (False,), + 'distances': (0.125,), + 'harmonic': False, + 'dtype': np.dtype('float'), + 'dim': 8, + 'total_volume': 1.0 + }], + [(8,), True, None, False, None, + { + 'shape': (8,), + 'zerocenter': (True,), + 'distances': (0.125,), + 'harmonic': False, + 'dtype': np.dtype('float'), + 'dim': 8, + 'total_volume': 1.0 + }], + [(8,), False, None, True, None, + { + 'shape': (8,), + 'zerocenter': (False,), + 'distances': (1.0,), + 'harmonic': True, + 'dtype': np.dtype('complex'), + 'dim': 8, + 'total_volume': 8.0 + }], + [(8,), False, (12,), True, None, + { + 'shape': (8,), + 'zerocenter': (False,), + 'distances': (12.0,), + 'harmonic': True, + 'dtype': np.dtype('complex'), + 'dim': 8, + 'total_volume': 96.0 + }], + [(11, 11), (False, True), None, False, None, + { + 'shape': (11, 11), + 'zerocenter': (False, True), + 'distances': (1/11, 1/11), + 'harmonic': False, + 'dtype': np.dtype('float'), + 'dim': 121, + 'total_volume': 1.0 + }], + [(11, 11), True, (1.3, 1.3), True, None, + { + 'shape': (11, 11), + 'zerocenter': (True, True), + 'distances': (1.3, 1.3), + 'harmonic': True, + 'dtype': np.dtype('complex'), + 'dim': 121, + 'total_volume': 204.49 + }] + + ] + + +class RGSpaceInterfaceTests(unittest.TestCase): + @expand([['distances', tuple], + ['zerocenter', tuple]]) + def test_properties(self, attribute, expected_type): + x = RGSpace() + assert_(isinstance(getattr(x, attribute), expected_type)) + + +class RGSpaceFunctionalityTests(unittest.TestCase): + @expand(INIT_CONFIGS) + def test_constructor(self, shape, zerocenter, distances, + harmonic, dtype, expected): + x = RGSpace(shape, zerocenter, distances, harmonic, dtype) + for key, value in expected.iteritems(): + assert_equal(getattr(x, key), value) + + def test_hermitian_decomposition(self): + pass + + def test_weight(self): + pass + + def test_distance_array(self): + pass