diff --git a/nifty/domain_object.py b/nifty/domain_object.py index e4f2fe11a4b5ab043aedefaecc2c9708751ed129..b73a4bbc2a6f9c5d33aca058662869d0c121ff91 100644 --- a/nifty/domain_object.py +++ b/nifty/domain_object.py @@ -40,6 +40,15 @@ class DomainObject(Versionable, Loggable, object): return result_hash def __eq__(self, x): + """Checks if this domain_object represents the same thing as another domain_object. + Parameters + ---------- + x: domain_object + The domain_object it is compared to. + Returns + ------- + bool : True if they this and x represent the same thing. + """ if isinstance(x, type(self)): for key in vars(self).keys(): item1 = vars(self)[key] @@ -57,23 +66,76 @@ class DomainObject(Versionable, Loggable, object): @abc.abstractproperty def shape(self): + """Returns the shape of the underlying array-like object. + Returns + ------- + (int, tuple) : A tuple representing the shape of the underlying array-like object + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes should + implement this. + """ raise NotImplementedError( "There is no generic shape for DomainObject.") @abc.abstractproperty def dim(self): + """Returns the number of pixel-dimensions the object has. + Returns + ------- + int : An Integer representing the number of pixels the discretized space has. + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes should + implement this. + """ raise NotImplementedError( "There is no generic dim for DomainObject.") @abc.abstractmethod def weight(self, x, power=1, axes=None, inplace=False): + """ Weights a field living on this domain with a specified amount of volume-weights. + + Weights hereby refer to integration weights, as they appear in discretized integrals. + Per default, this function mutliplies each bin of the field x by its volume, which lets + it behave like a density (top form). However, different powers of the volume can be applied + with the power parameter. The axes parameter specifies which of the field indices represent this + domain. + Parameters + ---------- + x : Field + A field with this space as domain to be weighted. + power : int, *optional* + The power to which the volume-weight is raised. + (default: 1). + axes : {int, tuple}, *optional* + Specifies the axes of x which represent this domain. + (default: None). + If axes==None: + weighting is applied with respect to all axes + inplace : bool, *optional* + If this is True, the weighting is done on the values of x, + if it is False, x is not modified and this method returns a + weighted copy of x + (default: False). + Returns + ------- + Field + A weighted version of x, with volume-weights raised to power. + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes should + implement this. + """ raise NotImplementedError( "There is no generic weight-method for DomainObject.") def pre_cast(self, x, axes=None): + # FIXME This does nothing and non of the children override this. Why does this exist?! return x def post_cast(self, x, axes=None): + # FIXME This does nothing and non of the children override this. Why does this exist?! return x # ---Serialization--- diff --git a/nifty/spaces/gl_space/gl_space.py b/nifty/spaces/gl_space/gl_space.py index 4ca1b860ed7e9f606030d048503fc477523c6e75..2a3cc3634ae1e278290e6714612469ff855b2641 100644 --- a/nifty/spaces/gl_space/gl_space.py +++ b/nifty/spaces/gl_space/gl_space.py @@ -43,8 +43,8 @@ class GLSpace(Space): ---------- nlat : int Number of latitudinal bins, or rings. - nlon : int - Number of longitudinal bins. + nlon : int, *optional* + Number of longitudinal bins (default: ``2*nlat - 1``). See Also -------- @@ -82,9 +82,7 @@ class GLSpace(Space): Raises ------ ValueError - If input `nlat` is invalid. - ImportError - If the pyHealpix module is not available + If input `nlat` or `nlon` is invalid. """ if 'pyHealpix' not in gdi: @@ -100,6 +98,12 @@ class GLSpace(Space): @property def harmonic(self): + """True if this can be regarded as a harmonic space. + Returns + ------- + bool : False + Always returns False as the GLSpace cannot be regarded as harmonic space. + """ return False @property @@ -119,6 +123,39 @@ class GLSpace(Space): nlon=self.nlon) def weight(self, x, power=1, axes=None, inplace=False): + """ Weights a field living on this space with a specified amount of volume-weights. + + Weights hereby refer to integration weights, as they appear in discretized integrals. + Per default, this function mutliplies each bin of the field x by its volume, which lets + it behave like a density (top form). However, different powers of the volume can be applied + with the power parameter. If only certain axes are specified via the axes parameter, + the weights are only applied with respect to these dimensions, yielding an object that + behaves like a lower degree form. + Parameters + ---------- + x : Field + A field with this space as domain to be weighted. + power : int, *optional* + The power to which the volume-weight is raised. + (default: 1). + axes : {int, tuple}, *optional* + Specifies for which axes the weights should be applied. + (default: None). + If axes==None: + weighting is applied with respect to all axes + inplace : bool, *optional* + If this is True, the weighting is done on the values of x, + if it is False, x is not modified and this method returns a + weighted copy of x + (default: False). + + Returns + ------- + Field + A weighted version of x, with volume-weights raised to power. + + """ + nlon = self.nlon nlat = self.nlat vol = pyHealpix.GL_weights(nlat, nlon) ** power @@ -141,9 +178,23 @@ class GLSpace(Space): return result_x def get_distance_array(self, distribution_strategy): + """This should not be used, it just raises an error when called. + + Raises + ------ + NotImplementedError + Always when called. + """ raise NotImplementedError def get_fft_smoothing_kernel_function(self, sigma): + """This should not be used, it just raises an error when called. + + Raises + ------ + NotImplementedError + Always when called. + """ raise NotImplementedError # ---Added properties and methods--- diff --git a/nifty/spaces/hp_space/hp_space.py b/nifty/spaces/hp_space/hp_space.py index 9630d7ff9aa96fff8d85a05d5aa5bf88a7619510..e9454d876e0bab19e067c8f73e3814ee502d3a68 100644 --- a/nifty/spaces/hp_space/hp_space.py +++ b/nifty/spaces/hp_space/hp_space.py @@ -60,7 +60,7 @@ class HPSpace(Space): def __init__(self, nside): """ - Sets the attributes for a HPSpace class instance. + Sets the attributes for a hp_space class instance. Parameters ---------- @@ -102,9 +102,43 @@ class HPSpace(Space): return 4 * np.pi def copy(self): + """Returns a copied version of this HPSpace. + + Returns + ------- + HPSpace : A copy of this object. + """ return self.__class__(nside=self.nside) def weight(self, x, power=1, axes=None, inplace=False): + """ Weights a field living on this space with a specified amount of volume-weights. + + Weights hereby refer to integration weights, as they appear in discretized integrals. + Per default, this function mutliplies each bin of the field x by its volume, which lets + it behave like a density (top form). However, different powers of the volume can be applied + with the power parameter. + Parameters + ---------- + x : Field + A field with this space as domain to be weighted. + power : int, *optional* + The power to which the volume-weight is raised. + (default: 1). + axes : {int, tuple}, *optional* + This should not be used. It does nothing. + inplace : bool, *optional* + If this is True, the weighting is done on the values of x, + if it is False, x is not modified and this method returns a + weighted copy of x + (default: False). + + Returns + ------- + Field + A weighted version of x, with volume-weights raised to power. + + """ + weight = ((4 * np.pi) / (12 * self.nside**2))**power if inplace: @@ -116,9 +150,23 @@ class HPSpace(Space): return result_x def get_distance_array(self, distribution_strategy): + """This should not be used, it just raises an error when called. + + Raises + ------ + NotImplementedError + Always when called. + """ raise NotImplementedError def get_fft_smoothing_kernel_function(self, sigma): + """This should not be used, it just raises an error when called. + + Raises + ------ + NotImplementedError + Always when called. + """ raise NotImplementedError # ---Added properties and methods--- diff --git a/nifty/spaces/lm_space/lm_space.py b/nifty/spaces/lm_space/lm_space.py index 61f3b04a7d95198ed24988e91552878b6df91df1..79919ea44b8ac6c441ce5f5512c120f6d46525a3 100644 --- a/nifty/spaces/lm_space/lm_space.py +++ b/nifty/spaces/lm_space/lm_space.py @@ -43,17 +43,19 @@ class LMSpace(Space): Maximum :math:`\ell`-value up to which the spherical harmonics coefficients are to be used. - - Notes: - ------ - This implementation implicitly sets the mmax parameter to lmax. - See Also -------- hp_space : A class for the HEALPix discretization of the sphere [#]_. gl_space : A class for the Gauss-Legendre discretization of the sphere [#]_. + Notes + ----- + Hermitian symmetry, i.e. :math:`a_{\ell -m} = \overline{a}_{\ell m}` is + always assumed for the spherical harmonics components, i.e. only fields + on the two-sphere with real-valued representations in position space + can be handled. + References ---------- .. [#] K.M. Gorski et al., 2005, "HEALPix: A Framework for @@ -77,6 +79,11 @@ class LMSpace(Space): Returns ------- None. + + Raises + ------ + ValueError + If lmax is negative. """ @@ -117,15 +124,65 @@ class LMSpace(Space): return np.float64(self.dim) def copy(self): + """Returns a copied version of this LMSpace. + + Returns + ------- + LMSpace : A copy of this object. + """ return self.__class__(lmax=self.lmax) def weight(self, x, power=1, axes=None, inplace=False): + """ Weights a field living on this space with a specified amount of volume-weights. + + Weights hereby refer to integration weights, as they appear in discretized integrals. + Per default, this function mutliplies each bin of the field x by its volume, which lets + it behave like a density (top form). All volume-weights are 1, thus nothing happens. + Parameters + ---------- + x : Field + A field with this space as domain to be weighted. + power : int, *optional* + The power to which the volume-weight is raised. It does nothing. + (default: 1). + axes : {int, tuple}, *optional* + This should not be used. It does nothing. + inplace : bool, *optional* + If this is True, the weighting is done on the values of x, + if it is False, x is not modified and this method returns a + weighted copy of x + (default: False). + + Returns + ------- + Field + x or a copy of x. + + """ if inplace: return x else: return x.copy() def get_distance_array(self, distribution_strategy): + """Returns the distance of the bins to zero. + + Calculates an 2-dimensional array with its entries being the + lengths of the k-vectors from the zero point of the grid. + + Parameters + ---------- + distribution_strategy : + + Returns + ------- + nkdict : distributed_data_object + + Raises + ------ + ValueError + The distribution_strategy is neither slicing nor not. + """ dists = arange(start=0, stop=self.shape[0], distribution_strategy=distribution_strategy) @@ -144,6 +201,7 @@ class LMSpace(Space): return res def get_fft_smoothing_kernel_function(self, sigma): + # FIXME why x(x+1) ? add reference to paper! return lambda x: np.exp(-0.5 * x * (x + 1) * sigma**2) # ---Added properties and methods--- diff --git a/nifty/spaces/power_space/power_indices.py b/nifty/spaces/power_space/power_indices.py index 78f24b7ea84c0b6aefa71039b823c3a5fd494b79..2a4fe8a610d919baa937d411bffe14a26fc41978 100644 --- a/nifty/spaces/power_space/power_indices.py +++ b/nifty/spaces/power_space/power_indices.py @@ -16,13 +16,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import sys + import numpy as np from d2o import distributed_data_object,\ STRATEGIES as DISTRIBUTION_STRATEGIES -from d2o.config import dependency_injector as d2o_di -from d2o.config import configuration as d2o_config - class PowerIndices(object): def __init__(self, domain, distribution_strategy, @@ -313,7 +312,8 @@ class PowerIndices(object): # Store the individual pundices in the local_pundex array local_pundex[temp_uniqued_pindex] = local_temp_pundex # Extract the MPI module from the global_pindex d2o - MPI = d2o_di[d2o_config['mpi_module']] + MPI_name = global_pindex.comm.__class__.__module__ + MPI = sys.modules[MPI_name] # Use Allreduce to find the first occurences/smallest pundices global_pindex.comm.Allreduce(local_pundex, global_pundex, diff --git a/nifty/spaces/rg_space/rg_space.py b/nifty/spaces/rg_space/rg_space.py index 2d0571c28accbc2d027fcb954d2fb0a5edc16acb..bb9dad794f0c4b1653631d27b1a9831981b6a10b 100644 --- a/nifty/spaces/rg_space/rg_space.py +++ b/nifty/spaces/rg_space/rg_space.py @@ -101,6 +101,24 @@ class RGSpace(Space): def hermitian_decomposition(self, x, axes=None, preserve_gaussian_variance=False): + """Separates the hermitian and antihermitian part of a field. + + This is a function which is called by the field in order to separate itself for + each of its domains. + + Parameters + ---------- + x: Field + Field to be decomposed. + axes: {int, tuple}, *optional* + Specifies which indices of the field belongs to this RGSpace. If None, it + takes the first dimensions of the field. + (default: None) + preserve_gaussian_variance: bool, *optional* + + (default: False) + + """ # compute the hermitian part flipped_x = self._hermitianize_inverter(x, axes=axes) flipped_x = flipped_x.conjugate() @@ -190,12 +208,50 @@ class RGSpace(Space): return self.dim * reduce(lambda x, y: x*y, self.distances) def copy(self): + """Returns a copied version of this RGSpace. + + Returns + ------- + RGSpace : A copy of this object. + """ return self.__class__(shape=self.shape, zerocenter=self.zerocenter, distances=self.distances, harmonic=self.harmonic) def weight(self, x, power=1, axes=None, inplace=False): + """ Weights a field living on this space with a specified amount of volume-weights. + + Weights hereby refer to integration weights, as they appear in discretized integrals. + Per default, this function mutliplies each bin of the field x by its volume, which lets + it behave like a density (top form). However, different powers of the volume can be applied + with the power parameter. If only certain axes are specified via the axes parameter, + the weights are only applied with respect to these dimensions, yielding an object that + behaves like a lower degree form. + Parameters + ---------- + x : Field + A field with this space as domain to be weighted. + power : int, *optional* + The power to which the volume-weight is raised. + (default: 1). + axes : {int, tuple}, *optional* + Specifies for which axes the weights should be applied. + (default: None). + If axes==None: + weighting is applied with respect to all axes + inplace : bool, *optional* + If this is True, the weighting is done on the values of x, + if it is False, x is not modified and this method returns a + weighted copy of x + (default: False). + + Returns + ------- + Field + A weighted version of x, with volume-weights raised to power. + + """ weight = reduce(lambda x, y: x*y, self.distances)**power if inplace: x *= weight @@ -205,19 +261,25 @@ class RGSpace(Space): return result_x def get_distance_array(self, distribution_strategy): - """ - Calculates an n-dimensional array with its entries being the - lengths of the k-vectors from the zero point of the grid. - MR FIXME: Since this is about k-vectors, it might make sense to - throw NotImplementedError if harmonic==False. - - Parameters - ---------- - None : All information is taken from the parent object. - - Returns - ------- - nkdict : distributed_data_object + """Returns the distance of the bins to zero. + + Calculates an n-dimensional array with its entries being the + lengths of the k-vectors from the zero point of the grid. + MR FIXME: Since this is about k-vectors, it might make sense to + throw NotImplementedError if harmonic==False. + + Parameters + ---------- + None : All information is taken from the parent object. + + Returns + ------- + nkdict : distributed_data_object + + Raises + ------ + ValueError + The distribution_strategy is neither slicing nor not. """ shape = self.shape # prepare the distributed_data_object @@ -263,11 +325,12 @@ class RGSpace(Space): dists = np.sqrt(dists) return dists - def get_fft_smoothing_kernel_function(self, sigma): + def get_fft_smoothing_kernel_function(self, sigma): + if sigma is None: sigma = np.sqrt(2) * np.max(self.distances) - return lambda x: np.exp(-2. * np.pi**2 * x**2 * sigma**2) + return lambda x: np.exp(-0.5 * np.pi**2 * x**2 * sigma**2) # ---Added properties and methods--- diff --git a/nifty/spaces/space/space.py b/nifty/spaces/space/space.py index 5c6c686d49c37f2a21e7ba8baa07ea9424c2e686..5d42dcfb3afe3bf9af25fc91ca19d786de95b644 100644 --- a/nifty/spaces/space/space.py +++ b/nifty/spaces/space/space.py @@ -16,49 +16,146 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +""" + .. __ ____ __ + .. /__/ / _/ / /_ + .. __ ___ __ / /_ / _/ __ __ + .. / _ | / / / _/ / / / / / / + .. / / / / / / / / / /_ / /_/ / + .. /__/ /__/ /__/ /__/ \___/ \___ / core + .. /______/ + + .. The NIFTY project homepage is http://www.mpa-garching.mpg.de/ift/nifty/ + + NIFTY [#]_, "Numerical Information Field Theory", is a versatile + library designed to enable the development of signal inference algorithms + that operate regardless of the underlying spatial grid and its resolution. + Its object-oriented framework is written in Python, although it accesses + libraries written in Cython, C++, and C for efficiency. + + NIFTY offers a toolkit that abstracts discretized representations of + continuous spaces, fields in these spaces, and operators acting on fields + into classes. Thereby, the correct normalization of operations on fields is + taken care of automatically without concerning the user. This allows for an + abstract formulation and programming of inference algorithms, including + those derived within information field theory. Thus, NIFTY permits its user + to rapidly prototype algorithms in 1D and then apply the developed code in + higher-dimensional settings of real world problems. The set of spaces on + which NIFTY operates comprises point sets, n-dimensional regular grids, + spherical spaces, their harmonic counterparts, and product spaces + constructed as combinations of those. + + References + ---------- + .. [#] Selig et al., "NIFTY -- Numerical Information Field Theory -- + a versatile Python library for signal inference", + `A&A, vol. 554, id. A26 <http://dx.doi.org/10.1051/0004-6361/201321236>`_, + 2013; `arXiv:1301.4499 <http://www.arxiv.org/abs/1301.4499>`_ + + Class & Feature Overview + ------------------------ + The NIFTY library features three main classes: **spaces** that represent + certain grids, **fields** that are defined on spaces, and **operators** + that apply to fields. + + .. Overview of all (core) classes: + .. + .. - switch + .. - notification + .. - _about + .. - random + .. - space + .. - point_space + .. - rg_space + .. - lm_space + .. - gl_space + .. - hp_space + .. - nested_space + .. - field + .. - operator + .. - diagonal_operator + .. - power_operator + .. - projection_operator + .. - vecvec_operator + .. - response_operator + .. - probing + .. - trace_probing + .. - diagonal_probing + + Overview of the main classes and functions: + + .. automodule:: nifty + + - :py:class:`space` + - :py:class:`point_space` + - :py:class:`rg_space` + - :py:class:`lm_space` + - :py:class:`gl_space` + - :py:class:`hp_space` + - :py:class:`nested_space` + - :py:class:`field` + - :py:class:`operator` + - :py:class:`diagonal_operator` + - :py:class:`power_operator` + - :py:class:`projection_operator` + - :py:class:`vecvec_operator` + - :py:class:`response_operator` + + .. currentmodule:: nifty.nifty_tools + + - :py:class:`invertible_operator` + - :py:class:`propagator_operator` + + .. currentmodule:: nifty.nifty_explicit + + - :py:class:`explicit_operator` + + .. automodule:: nifty + + - :py:class:`probing` + - :py:class:`trace_probing` + - :py:class:`diagonal_probing` + + .. currentmodule:: nifty.nifty_explicit + + - :py:class:`explicit_probing` + + .. currentmodule:: nifty.nifty_tools + + - :py:class:`conjugate_gradient` + - :py:class:`steepest_descent` + + .. currentmodule:: nifty.nifty_explicit + + - :py:func:`explicify` + + .. currentmodule:: nifty.nifty_power + + - :py:func:`weight_power`, + :py:func:`smooth_power`, + :py:func:`infer_power`, + :py:func:`interpolate_power` + +""" +from __future__ import division + import abc from nifty.domain_object import DomainObject class Space(DomainObject): - """The abstract base class for all NIFTy spaces. - - An instance of a space contains information about the manifolds geometry - and enhances the functionality of DomainObject by methods that are needed - for powerspectrum analysis and smoothing. - + def __init__(self): + """ Parameters ---------- - None + None. - Attributes - ---------- - dim : np.int - Total number of dimensionality, i.e. the number of pixels. - harmonic : bool - Specifies whether the space is a signal or harmonic space. - total_volume : np.float - The total volume of the space. - shape : tuple of np.ints - The shape of the space's data array. - - Raises - ------ - TypeError - Raised if instantiated directly. - - Notes - ----- - `Space` is an abstract base class. In order to allow for instantiation the - methods `get_distance_array`, `total_volume` and `copy` must be implemented - as well as the abstract methods inherited from `DomainObject`. - - See Also - -------- - distributor + Returns + ------- + None. """ - def __init__(self): + super(Space, self).__init__() @abc.abstractproperty @@ -67,23 +164,101 @@ class Space(DomainObject): @abc.abstractproperty def total_volume(self): + """Returns the total volume of the space + Returns + ------- + Floating Point : An real number representing the sum of all pixel volumes. + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes should + implement this. + """ raise NotImplementedError( "There is no generic volume for the Space base class.") @abc.abstractmethod def copy(self): + """Returns a copied version of this Space. + + Returns + ------- + Space : A copy of this object. + """ return self.__class__() def get_distance_array(self, distribution_strategy): + """The distances of the pixel to zero. + + In a harmonic space, this return an array that gives for each pixel its + distance to the zero-mode + Returns + ------- + array-like : An array representing the distances of each pixel to the zero-mode + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes + that can be regarded as harmonic space should implement this. + """ raise NotImplementedError( "There is no generic distance structure for Space base class.") def get_fft_smoothing_kernel_function(self, sigma): + """This method returns a function applying a smoothing kernel. + + This method, which is only implemented for harmonic spaces, + helps smoothing functions that live in a position space that has this space as its harmonic space. + The returned function multiplies field values of a field with a zero centered Gaussian which corresponds to convolution with a + Gaussian kernel and sigma standard deviation in position space. + + Parameters + ---------- + sigma : Floating Point + A real number representing a physical scale on which the smoothing takes place. The smoothing is defined with respect to + the real physical field and points that are closer together than one sigma are blurred together. Mathematically + sigma is the standard deviation of a convolution with a normalized, zero-centered Gaussian that takes place in position space. + + Returns + ------- + function Field -> Field : A smoothing operation that multiplies values with a Gaussian kernel. + + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes + that can be regarded as harmonic space should implement this. + """ raise NotImplementedError( "There is no generic co-smoothing kernel for Space base class.") def hermitian_decomposition(self, x, axes=None, preserve_gaussian_variance=False): + """ + Decomposes the field x into its hermitian and anti-hermitian constituents. + + If the space is harmonic, this method decomposes a field x into + a hermitian and an antihermitian part, which corresponds to a real and imaginary part + in a corresponding position space. This is an internal function that is mainly used for + drawing real fields. + + Parameters + ---------- + x : Field + A field with this space as domain to be decomposed. + axes : {int, tuple}, *optional* + Specifies the axes of x which represent this domain. + (default: None). + If axes==None: + weighting is applied with respect to all axes + preserve_gaussian_variance : bool *optional* + FIXME: figure out what this does + Returns + ------- + (Field, Field) : A tuple of two fields, the first field being the hermitian and the second the anti-hermitian part of x. + + Raises + ------ + NotImplementedError : If it is called for an abstract class, all non-abstract child-classes + that can be regarded as harmonic space should implement this. + """ raise NotImplementedError def __repr__(self):