From a929944b2661f2f75db8585eb690c20e4f24f26f Mon Sep 17 00:00:00 2001 From: Martin Reinecke <martin@mpa-garching.mpg.de> Date: Fri, 2 Jun 2017 21:55:14 +0200 Subject: [PATCH] still one failing test --- nifty/spaces/power_space/power_indices.py | 146 ---------------------- nifty/spaces/power_space/power_space.py | 141 ++++++++++++--------- test/test_spaces/test_power_space.py | 8 +- 3 files changed, 82 insertions(+), 213 deletions(-) delete mode 100644 nifty/spaces/power_space/power_indices.py diff --git a/nifty/spaces/power_space/power_indices.py b/nifty/spaces/power_space/power_indices.py deleted file mode 100644 index df67b9bae..000000000 --- a/nifty/spaces/power_space/power_indices.py +++ /dev/null @@ -1,146 +0,0 @@ -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Copyright(C) 2013-2017 Max-Planck-Society -# -# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik -# and financially supported by the Studienstiftung des deutschen Volkes. - -import numpy as np -from d2o import distributed_data_object - -class PowerIndices(object): - """Computes helpful quantities to deal with power spectra. - - Given the shape and the density of a underlying grid this class provides - the user with the pindex, kindex and rho. The indices are binned - according to the supplied parameter scheme. - """ - @staticmethod - def get_arrays(domain, distribution_strategy, logarithmic, nbin, binbounds): - """ - Returns a dictionary containing the pindex, kindex and rho - binned according to the supplied parameter scheme and a - configuration dict containing this scheme. - - Parameters - ---------- - domain : NIFTy harmonic space - The space for which the power indices get computed - distribution_strategy : str - The distribution_strategy that will be used for the k_array and - pindex distributed_data_object. - logarithmic : bool - Flag specifying if the binning is performed on logarithmic - scale. - nbin : integer - Number of used bins. - binbounds : {list, array} - Array-like inner boundaries of the used bins. - - """ - # if no binning is requested, compute the indices, build the dict, - # and return it straight. - if not logarithmic and nbin is None and binbounds is None: - k_array = domain.get_distance_array(distribution_strategy) - temp_kindex = k_array.unique() - # compute the local pindex slice on basis of the local k_array data - local_pindex = np.searchsorted(temp_kindex, k_array.get_local_data()) - # prepare the distributed_data_object - temp_pindex = distributed_data_object( - global_shape=k_array.shape, - dtype=local_pindex.dtype, - distribution_strategy=distribution_strategy) - # store the local pindex data in the global_pindex d2o - temp_pindex.set_local_data(local_pindex) - temp_rho = temp_pindex.bincount().get_full_data() - temp_k_array = k_array - - # if binning is required, make a recursive call to get the unbinned - # indices, bin them, and then return everything. - else: - # Get the unbinned indices - pindex, kindex, rho, dummy = PowerIndices.get_arrays(domain,distribution_strategy,nbin=None, - binbounds=None, - logarithmic=False) - # Bin them - (temp_pindex, temp_kindex, temp_rho) = \ - PowerIndices._bin_power_indices( - pindex, kindex, rho, logarithmic, nbin, binbounds) - # Make a binned version of k_array - tempindex = temp_pindex.copy(dtype=temp_kindex.dtype) - temp_k_array = tempindex.apply_scalar_function( - lambda x: temp_kindex[x.astype(np.dtype('int'))]) - - return temp_pindex, temp_kindex, temp_rho, temp_k_array - - @staticmethod - def _bin_power_indices(pindex, kindex, rho, logarithmic, nbin, binbounds): - """ - Returns the binned power indices associated with the Fourier grid. - - Parameters - ---------- - pindex : distributed_data_object - Index of the Fourier grid points in a distributed_data_object. - kindex : ndarray - Array of all k-vector lengths. - rho : ndarray - Degeneracy factor of the individual k-vectors. - logarithmic : bool - Flag specifying if the binning is performed on logarithmic - scale. - nbin : integer - Number of used bins. - binbounds : {list, array} - Array-like inner boundaries of the used bins. - - Returns - ------- - pindex : distributed_data_object - kindex, rho : ndarrays - The (re)binned power indices. - - """ - - # boundaries - if(binbounds is not None): - binbounds = np.sort(binbounds) - # equal binning - else: - if(logarithmic is None): - logarithmic = False - if(logarithmic): - k = np.r_[0, np.log(kindex[1:])] - else: - k = kindex - dk = np.max(k[2:] - k[1:-1]) # maximal dk - if(nbin is None): - nbin = int((k[-1] - 0.5 * (k[2] + k[1])) / - dk - 0.5) # maximal nbin - else: - nbin = min(int(nbin), int( - (k[-1] - 0.5 * (k[2] + k[1])) / dk + 2.5)) - dk = (k[-1] - 0.5 * (k[2] + k[1])) / (nbin - 2.5) - binbounds = np.r_[0.5 * (3 * k[1] - k[2]), - 0.5 * (k[1] + k[2]) + dk * np.arange(nbin - 2)] - if(logarithmic): - binbounds = np.exp(binbounds) - # reordering - reorder = np.searchsorted(binbounds, kindex) - rho_ = np.bincount(reorder,weights=rho).astype(rho.dtype) - kindex_ = np.bincount(reorder,weights=kindex*rho)/rho_ - pindex_ = pindex.copy_empty() - pindex_.set_local_data(reorder[pindex.get_local_data()]) - - return pindex_, kindex_, rho_ diff --git a/nifty/spaces/power_space/power_space.py b/nifty/spaces/power_space/power_space.py index b6244f4a6..3e0d5f851 100644 --- a/nifty/spaces/power_space/power_space.py +++ b/nifty/spaces/power_space/power_space.py @@ -18,10 +18,9 @@ import numpy as np -import d2o +from d2o import distributed_data_object from nifty.spaces.space import Space -from power_indices import PowerIndices class PowerSpace(Space): @@ -42,8 +41,8 @@ class PowerSpace(Space): (default : None). if nbin == None, then nbin is set to the length of kindex. binbounds : {list, array-like} *optional* - Array-like inner boundaries of the used bins of the default - indices. + Boundaries between the power spectrum bins. + (If binbounds has n entries, there will be n+1 bins.) (default : None) if binbounds == None : Calculates the bounds from the kindex while applying the @@ -67,19 +66,8 @@ class PowerSpace(Space): The total volume of the space. shape : tuple of np.ints The shape of the space's data array. - logarithmic : bool - True if logarithmic binning should be used. - nbin : {int, None} - The number of bins that should be used for power spectrum binning - (default : None). - if nbin == None, then nbin is set to the length of kindex. binbounds : {list, array-like} - Array-like inner boundaries of the used bins of the default - indices. - (default : None) - if binbounds == None : - Calculates the bounds from the kindex while applying the - logarithmic and nbin keywords. + Boundaries between the power spectrum bins Notes ----- @@ -96,34 +84,15 @@ class PowerSpace(Space): super(PowerSpace, self).__init__() self._ignore_for_hash += ['_pindex', '_kindex', '_rho', '_k_array'] - if not isinstance(harmonic_partner, Space): - raise ValueError( - "harmonic_partner must be a Space.") - if not harmonic_partner.harmonic: - raise ValueError( - "harmonic_partner must be a harmonic space.") + if not (isinstance(harmonic_partner, Space) and \ + harmonic_partner.harmonic): + raise ValueError("harmonic_partner must be a harmonic space.") self._harmonic_partner = harmonic_partner - try: - self._logarithmic = bool(logarithmic) - except(TypeError): - self._logarithmic = False - try: - self._nbin = int(nbin) - except(TypeError): - self._nbin = None - try: - self._binbounds = tuple(np.array(binbounds)) - except(TypeError): - self._binbounds = None -# tmp = PowerIndices(self.harmonic_partner, distribution_strategy) - self._pindex, self._kindex, self._rho, self._k_array = PowerIndices.get_arrays(self.harmonic_partner, distribution_strategy, self._logarithmic, - self._nbin, self._binbounds) + self._k_array = self._harmonic_partner.get_distance_array(distribution_strategy) + self._compute_binbounds (binbounds, logarithmic, nbin) - if self._nbin is not None: - if self._nbin > len(self.kindex): - self.logger.warn("nbin was set to a value being larger than " - "the length of kindex!") + self._do_binning() def pre_cast(self, x, axes): """ Casts power spectrum functions to discretized power spectra. @@ -154,9 +123,9 @@ class PowerSpace(Space): def __repr__(self): return ("PowerSpace(harmonic_partner=%r, distribution_strategy=%r, " - "logarithmic=%r, nbin=%r, binbounds=%r)" + "binbounds=%r)" % (self.harmonic_partner, self.pindex.distribution_strategy, - self._logarithmic, self._nbin, self._binbounds)) + self._binbounds)) @property def harmonic(self): @@ -179,8 +148,6 @@ class PowerSpace(Space): distribution_strategy = self.pindex.distribution_strategy return self.__class__(harmonic_partner=self.harmonic_partner, distribution_strategy=distribution_strategy, - logarithmic=self._logarithmic, - nbin=self._nbin, binbounds=self._binbounds) def weight(self, x, power=1, axes=None, inplace=False): @@ -201,10 +168,9 @@ class PowerSpace(Space): return result_x def get_distance_array(self, distribution_strategy): - result = d2o.distributed_data_object( + return distributed_data_object( self.kindex, dtype=np.float64, distribution_strategy=distribution_strategy) - return result def get_fft_smoothing_kernel_function(self, sigma): raise NotImplementedError( @@ -218,14 +184,6 @@ class PowerSpace(Space): """ return self._harmonic_partner - @property - def logarithmic(self): - return self._logarithmic - - @property - def nbin(self): - return self._nbin - @property def binbounds(self): return self._binbounds @@ -261,9 +219,6 @@ class PowerSpace(Space): def _to_hdf5(self, hdf5_group): hdf5_group['kindex'] = self.kindex hdf5_group['rho'] = self.rho - hdf5_group['logarithmic'] = self._logarithmic - # Store nbin as string, since it can be None - hdf5_group.attrs['nbin'] = str(self._nbin) hdf5_group.attrs['binbounds'] = str(self._binbounds) #MR FIXME: why not "return None" as happens everywhere else? @@ -285,8 +240,6 @@ class PowerSpace(Space): new_ps._harmonic_partner = repository.get('harmonic_partner', hdf5_group) - new_ps._logarithmic = hdf5_group['logarithmic'][()] - exec("new_ps._nbin = " + hdf5_group.attrs['nbin']) exec("new_ps._binbounds = " + hdf5_group.attrs['binbounds']) new_ps._pindex = repository.get('pindex', hdf5_group) @@ -297,6 +250,74 @@ class PowerSpace(Space): return new_ps + def _do_binning(self): + """ Computes pindex, kindex and rho according to self._binbounds. + """ + # if no binning is requested, compute the "natural" binning, i.e. there + # is one bin for every distinct k-vector length + if self._binbounds is None: + self._kindex = self._k_array.unique() + # prepare the distributed_data_object + self._pindex = distributed_data_object( + global_shape=self._k_array.shape, + dtype=np.int, + distribution_strategy=self._k_array.distribution_strategy) + # store the local pindex data in the global_pindex d2o + self._pindex.set_local_data( + np.searchsorted(self._kindex, self._k_array.get_local_data())) + self._rho = self._pindex.bincount().get_full_data() + + # use the provided binbounds + else: + self._pindex = distributed_data_object( + global_shape=self._k_array.shape, + dtype=np.int, + distribution_strategy=self._k_array.distribution_strategy) + self._pindex.set_local_data(np.searchsorted( + self._binbounds, self._k_array.get_local_data())) + self._rho = self._pindex.bincount().get_full_data() + self._kindex = self._pindex.bincount( + weights=self._k_array).get_full_data()/self._rho + + def _compute_binbounds (self, binbounds, logarithmic, nbin): + try: + self._binbounds = np.sort(tuple(np.array(binbounds))) + except(TypeError): + self._binbounds = None + + if(self._binbounds is None): + try: + logarithmic = bool(logarithmic) + except(TypeError): + logarithmic = False + try: + nbin = int(nbin) + except(TypeError): + nbin = None + + if not logarithmic and nbin is None: + return # no binbounds, use "natural" binning + + # equal binning + # MR FIXME: this needs to improve + kindex = self._k_array.unique() + if (logarithmic): + k = np.r_[0, np.log(kindex[1:])] + else: + k = kindex + dk = np.max(k[2:] - k[1:-1]) # minimum dk to avoid empty bins + if(nbin is None): + nbin = int((k[-1] - 0.5 * (k[2] + k[1])) / + dk - 0.5) # maximal nbin + else: + nbin = min(int(nbin), int( + (k[-1] - 0.5 * (k[2] + k[1])) / dk + 2.5)) + dk = (k[-1] - 0.5 * (k[2] + k[1])) / (nbin - 2.5) + self._binbounds = np.r_[0.5 * (3 * k[1] - k[2]), + 0.5 * (k[1] + k[2]) + dk * np.arange(nbin - 2)] + if(logarithmic): + self._binbounds = np.exp(self._binbounds) + class EmptyPowerSpace(PowerSpace): def __init__(self): diff --git a/test/test_spaces/test_power_space.py b/test/test_spaces/test_power_space.py index 94e21bc3f..ea704dd24 100644 --- a/test/test_spaces/test_power_space.py +++ b/test/test_spaces/test_power_space.py @@ -64,8 +64,6 @@ CONSTRUCTOR_CONFIGS = [ 'dim': 5, 'total_volume': 8.0, 'harmonic_partner': RGSpace((8,), harmonic=True), - 'logarithmic': False, - 'nbin': None, 'binbounds': None, 'pindex': distributed_data_object([0, 1, 2, 3, 4, 3, 2, 1]), 'kindex': np.array([0., 1., 2., 3., 4.]), @@ -78,8 +76,6 @@ CONSTRUCTOR_CONFIGS = [ 'dim': 2, 'total_volume': 8.0, 'harmonic_partner': RGSpace((8,), harmonic=True), - 'logarithmic': True, - 'nbin': None, 'binbounds': None, 'pindex': distributed_data_object([0, 1, 1, 1, 1, 1, 1, 1]), 'kindex': np.array([0., 2.28571429]), @@ -118,8 +114,6 @@ def get_weight_configs(): class PowerSpaceInterfaceTest(unittest.TestCase): @expand([ ['harmonic_partner', Space], - ['logarithmic', bool], - ['nbin', NoneType], ['binbounds', NoneType], ['pindex', distributed_data_object], ['kindex', np.ndarray], @@ -143,7 +137,7 @@ class PowerSpaceConsistencyCheck(unittest.TestCase): err_msg='rho is not equal to pindex degeneracy') class PowerSpaceFunctionalityTest(unittest.TestCase): - @expand(CONSISTENCY_CONFIGS) + @expand(CONSTRUCTOR_CONFIGS) def test_constructor(self, harmonic_partner, distribution_strategy, logarithmic, nbin, binbounds, expected): if 'error' in expected: -- GitLab