Skip to content
Snippets Groups Projects
Commit a929944b authored by Martin Reinecke's avatar Martin Reinecke
Browse files

still one failing test

parent 4758127e
No related branches found
No related tags found
1 merge request!120Working on demos
# 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_
......@@ -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):
......
......@@ -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:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment