diff --git a/nifty/spaces/lm_space/__init__.py b/nifty/spaces/lm_space/__init__.py index c8a98d575a0eef81590bd40d3a4ba2e5f6069df1..6204a41f41428d8fa5ff1858a112e02ca496fdb1 100644 --- a/nifty/spaces/lm_space/__init__.py +++ b/nifty/spaces/lm_space/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- - from lm_space import LMSpace diff --git a/nifty/spaces/lm_space/lm_space.py b/nifty/spaces/lm_space/lm_space.py index 3b44031854cb69769912c8c404194d9b9284c441..8246815a586dfcfc96187c3aeb9b7ff08425f598 100644 --- a/nifty/spaces/lm_space/lm_space.py +++ b/nifty/spaces/lm_space/lm_space.py @@ -1,13 +1,7 @@ from __future__ import division -import os import numpy as np -import pylab as pl -from matplotlib.colors import LogNorm as ln -from matplotlib.ticker import LogFormatter as lf - -from d2o import STRATEGIES as DISTRIBUTION_STRATEGIES from nifty.spaces.space import Space @@ -15,13 +9,9 @@ from nifty.config import about,\ nifty_configuration as gc,\ dependency_injector as gdi -from nifty.nifty_random import random - gl = gdi.get('libsharp_wrapper_gl') hp = gdi.get('healpy') -LM_DISTRIBUTION_STRATEGIES = DISTRIBUTION_STRATEGIES['global'] - class LMSpace(Space): """ @@ -120,439 +110,45 @@ class LMSpace(Space): self._lmax = self._parse_lmax(lmax) self._mmax = self._parse_mmax(mmax) - def _identifier(self): - # Extract the identifying parts from the vars(self) dict. - temp = [(ii[0], - ((lambda x: tuple(x) if - isinstance(x, np.ndarray) else x)(ii[1]))) - for ii in vars(self).iteritems() - if ii[0] not in ['_cache_dict', 'power_indices']] - # Return the sorted identifiers as a tuple. - return tuple(sorted(temp)) - - def copy(self): - return self.__class__(lmax=self.lmax, - mmax=self.mmax, - dtype=self.dtype) + def compute_k_array(self, distribution_strategy): + # return a d2o with the l-value at the individual pixels + # e.g. for 0<=l<=2: [0,1,2, 1,1,2,2, 2,2] + pass - @property - def shape(self): - return (np.int((self.mmax + 1) * (self.lmax + 1)),) - - @property - def dim(self): - return np.int((self.mmax + 1) * (self.lmax + 1)) + # ---Mandatory properties and methods--- @property def harmonic(self): return True @property - def meta_volume(self): - """ - Calculates the meta volumes. - - The meta volumes are the volumes associated with each component of - a field, taking into account field components that are not - explicitly included in the array of field values but are determined - by symmetry conditions. - - Parameters - ---------- - total : bool, *optional* - Whether to return the total meta volume of the space or the - individual ones of each field component (default: False). - - Returns - ------- - mol : {numpy.ndarray, float} - Meta volume of the field components or the complete space. - - Notes - ----- - The spherical harmonics components with :math:`m=0` have meta - volume 1, the ones with :math:`m>0` have meta volume 2, sinnce they - each determine another component with negative :math:`m`. - """ - return np.float(self.dof()) + def shape(self): + return (self.dim, ) @property - def meta_volume_split(self): - mol = self.cast(1, dtype=np.float) - mol[self.paradict['lmax'] + 1:] = 2 # redundant: (l,m) and (l,-m) - return mol - - def complement_cast(self, x, axis=None, **kwargs): - if axis is None: - lmax = self.paradict['lmax'] - complexity_mask = x[:lmax+1].iscomplex() - if complexity_mask.any(): - about.warnings.cprint("WARNING: Taking the absolute values for " + - "all complex entries where lmax==0") - x[:lmax+1] = abs(x[:lmax+1]) - else: - # TODO hermitianize only on specific axis - lmax = self.paradict['lmax'] - complexity_mask = x[:lmax+1].iscomplex() - if complexity_mask.any(): - about.warnings.cprint("WARNING: Taking the absolute values for " + - "all complex entries where lmax==0") - x[:lmax+1] = abs(x[:lmax+1]) - return x - - # TODO: Extend to binning/log - def enforce_power(self, spec, size=None, kindex=None): - if kindex is None: - kindex_size = self.paradict['lmax'] + 1 - kindex = np.arange(kindex_size, - dtype=np.array(self.distances).dtype) - return self._enforce_power_helper(spec=spec, - size=size, - kindex=kindex) - - def _check_codomain(self, codomain): - """ - Checks whether a given codomain is compatible to the - :py:class:`lm_space` or not. - - Parameters - ---------- - codomain : nifty.space - Space to be checked for compatibility. - - Returns - ------- - check : bool - Whether or not the given codomain is compatible to the space. - - Notes - ----- - Compatible codomains are instances of :py:class:`lm_space`, - :py:class:`gl_space`, and :py:class:`hp_space`. - """ - if codomain is None: - return False - - from nifty.spaces.hp_space import HPSpace - from nifty.spaces.gl_space import GLSpace - if not isinstance(codomain, Space): - raise TypeError(about._errors.cstring( - "ERROR: The given codomain must be a nifty lm_space.")) - - elif isinstance(codomain, GLSpace): - # lmax==mmax - # nlat==lmax+1 - # nlon==2*lmax+1 - if ((self.paradict['lmax'] == self.paradict['mmax']) and - (codomain.paradict['nlat'] == self.paradict['lmax']+1) and - (codomain.paradict['nlon'] == 2*self.paradict['lmax']+1)): - return True - - elif isinstance(codomain, HPSpace): - # lmax==mmax - # 3*nside-1==lmax - if ((self.paradict['lmax'] == self.paradict['mmax']) and - (3*codomain.paradict['nside']-1 == self.paradict['lmax'])): - return True - - return False - - def get_random_values(self, **kwargs): - """ - Generates random field values according to the specifications given - by the parameters, taking into account complex-valuedness and - hermitian symmetry. - - Returns - ------- - x : numpy.ndarray - Valid field values. - - Other parameters - ---------------- - random : string, *optional* - Specifies the probability distribution from which the random - numbers are to be drawn. - Supported distributions are: - - - "pm1" (uniform distribution over {+1,-1} or {+1,+i,-1,-i} - - "gau" (normal distribution with zero-mean and a given - standard - deviation or variance) - - "syn" (synthesizes from a given power spectrum) - - "uni" (uniform distribution over [vmin,vmax[) - - (default: None). - dev : float, *optional* - Standard deviation (default: 1). - var : float, *optional* - Variance, overriding `dev` if both are specified - (default: 1). - spec : {scalar, list, numpy.array, nifty.field, function}, - *optional* - Power spectrum (default: 1). - vmin : float, *optional* - Lower limit for a uniform distribution (default: 0). - vmax : float, *optional* - Upper limit for a uniform distribution (default: 1). - """ - arg = random.parse_arguments(self, **kwargs) - -# if arg is None: -# x = 0 -# -# elif arg['random'] == "pm1": -# x = random.pm1(dtype=self.dtype, shape=self.shape) -# -# elif arg['random'] == "gau": -# x = random.gau(dtype=self.dtype, -# shape=self.shape, -# mean=arg['mean'], -# std=arg['std']) - - if arg['random'] == "syn": - lmax = self.paradict['lmax'] - mmax = self.paradict['mmax'] - if self.dtype == np.dtype('complex64'): - if gc['use_libsharp']: - sample = gl.synalm_f(arg['spec'], lmax=lmax, mmax=mmax) - else: - sample = hp.synalm( - arg['spec'].astype(np.complex128), - lmax=lmax, mmax=mmax).astype(np.complex64, - copy=False) - else: - if gc['use_healpy']: - sample = hp.synalm(arg['spec'], lmax=lmax, mmax=mmax) - else: - sample = gl.synalm(arg['spec'], lmax=lmax, mmax=mmax) - - else: - sample = super(LMSpace, self).get_random_values(**arg) - -# elif arg['random'] == "uni": -# x = random.uni(dtype=self.dtype, -# shape=self.shape, -# vmin=arg['vmin'], -# vmax=arg['vmax']) -# -# else: -# raise KeyError(about._errors.cstring( -# "ERROR: unsupported random key '" + str(arg['random']) + "'.")) - sample = self.cast(sample) - return sample - - - def dot_contraction(self, x, axes): - assert len(axes) == 1 - axis = axes[0] - lmax = self.paradict['lmax'] - - # extract the low and high parts of x - extractor = () - extractor += (slice(None),)*axis - low_extractor = extractor + (slice(None, lmax+1), ) - high_extractor = extractor + (slice(lmax+1), ) - - result = x[low_extractor].sum(axes) + 2 * x[high_extractor].sum(axes) - return result - - def get_plot(self, x, title="", vmin=None, vmax=None, power=True, - norm=None, cmap=None, cbar=True, other=None, legend=False, - mono=True, **kwargs): - """ - Creates a plot of field values according to the specifications - given by the parameters. - - Parameters - ---------- - x : numpy.ndarray - Array containing the field values. + def dim(self): + l = self.lmax + m = self.mmax + # the LMSpace consist of the full triangle, minus two little triangles + # if mmax < lmax + # dim = l(l+1)/2 - 2 * (l-m)(l-m+1)/2 + return np.int(l*(l+1.)/2. - 2.*(l-m)*(l-m+1.)/2.) - Returns - ------- - None - - Other parameters - ---------------- - title : string, *optional* - Title of the plot (default: ""). - vmin : float, *optional* - Minimum value to be displayed (default: ``min(x)``). - vmax : float, *optional* - Maximum value to be displayed (default: ``max(x)``). - power : bool, *optional* - Whether to plot the power contained in the field or the field - values themselves (default: True). - unit : string, *optional* - Unit of the field values (default: ""). - norm : string, *optional* - Scaling of the field values before plotting (default: None). - cmap : matplotlib.colors.LinearSegmentedColormap, *optional* - Color map to be used for two-dimensional plots (default: None). - cbar : bool, *optional* - Whether to show the color bar or not (default: True). - other : {single object, tuple of objects}, *optional* - Object or tuple of objects to be added, where objects can be - scalars, arrays, or fields (default: None). - legend : bool, *optional* - Whether to show the legend or not (default: False). - mono : bool, *optional* - Whether to plot the monopole or not (default: True). - save : string, *optional* - Valid file name where the figure is to be stored, by default - the figure is not saved (default: False). + @property + def total_volume(self): + # the individual pixels have a fixed volume of 1. + return np.float(self.dim) - """ - from nifty.field import Field - - try: - x = x.get_full_data() - except AttributeError: - pass - - if(not pl.isinteractive())and(not bool(kwargs.get("save", False))): - about.warnings.cprint("WARNING: interactive mode off.") - - if(power): - x = self.calc_power(x) - - fig = pl.figure(num=None, figsize=(6.4, 4.8), dpi=None, facecolor="none", - edgecolor="none", frameon=False, FigureClass=pl.Figure) - ax0 = fig.add_axes([0.12, 0.12, 0.82, 0.76]) - - xaxes = np.arange(self.para[0] + 1, dtype=np.int) - if(vmin is None): - vmin = np.min(x[:mono].tolist( - ) + (xaxes * (2 * xaxes + 1) * x)[1:].tolist(), axis=None, out=None) - if(vmax is None): - vmax = np.max(x[:mono].tolist( - ) + (xaxes * (2 * xaxes + 1) * x)[1:].tolist(), axis=None, out=None) - ax0.loglog(xaxes[1:], (xaxes * (2 * xaxes + 1) * x)[1:], color=[0.0, - 0.5, 0.0], label="graph 0", linestyle='-', linewidth=2.0, zorder=1) - if(mono): - ax0.scatter(0.5 * (xaxes[1] + xaxes[2]), x[0], s=20, color=[0.0, 0.5, 0.0], marker='o', - cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, zorder=1) - - if(other is not None): - if(isinstance(other, tuple)): - other = list(other) - for ii in xrange(len(other)): - if(isinstance(other[ii], Field)): - other[ii] = other[ii].power(**kwargs) - else: - other[ii] = self.enforce_power(other[ii]) - elif(isinstance(other, Field)): - other = [other.power(**kwargs)] - else: - other = [self.enforce_power(other)] - imax = max(1, len(other) - 1) - for ii in xrange(len(other)): - ax0.loglog(xaxes[1:], (xaxes * (2 * xaxes + 1) * other[ii])[1:], color=[max(0.0, 1.0 - (2 * ii / imax)**2), 0.5 * ((2 * ii - imax) / imax) - ** 2, max(0.0, 1.0 - (2 * (ii - imax) / imax)**2)], label="graph " + str(ii + 1), linestyle='-', linewidth=1.0, zorder=-ii) - if(mono): - ax0.scatter(0.5 * (xaxes[1] + xaxes[2]), other[ii][0], s=20, color=[max(0.0, 1.0 - (2 * ii / imax)**2), 0.5 * ((2 * ii - imax) / imax)**2, max( - 0.0, 1.0 - (2 * (ii - imax) / imax)**2)], marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, zorder=-ii) - if(legend): - ax0.legend() - - ax0.set_xlim(xaxes[1], xaxes[-1]) - ax0.set_xlabel(r"$\ell$") - ax0.set_ylim(vmin, vmax) - ax0.set_ylabel(r"$\ell(2\ell+1) C_\ell$") - ax0.set_title(title) + def copy(self): + return self.__class__(lmax=self.lmax, + mmax=self.mmax, + dtype=self.dtype) + def weight(self, x, power=1, axes=None, inplace=False): + if inplace: + return x else: - if(np.iscomplexobj(x)): - if(title): - title += " " - if(bool(kwargs.get("save", False))): - save_ = os.path.splitext( - os.path.basename(str(kwargs.get("save")))) - kwargs.update(save=save_[0] + "_absolute" + save_[1]) - self.get_plot(np.absolute(x), title=title + "(absolute)", vmin=vmin, vmax=vmax, - power=False, norm=norm, cmap=cmap, cbar=cbar, other=None, legend=False, **kwargs) -# self.get_plot(np.real(x),title=title+"(real part)",vmin=vmin,vmax=vmax,power=False,norm=norm,cmap=cmap,cbar=cbar,other=None,legend=False,**kwargs) -# self.get_plot(np.imag(x),title=title+"(imaginary part)",vmin=vmin,vmax=vmax,power=False,norm=norm,cmap=cmap,cbar=cbar,other=None,legend=False,**kwargs) - if(cmap is None): - cmap = pl.cm.hsv_r - if(bool(kwargs.get("save", False))): - kwargs.update(save=save_[0] + "_phase" + save_[1]) - self.get_plot(np.angle(x, deg=False), title=title + "(phase)", vmin=-3.1416, vmax=3.1416, power=False, - norm=None, cmap=cmap, cbar=cbar, other=None, legend=False, **kwargs) # values in [-pi,pi] - return None # leave method - else: - if(vmin is None): - vmin = np.min(x, axis=None, out=None) - if(vmax is None): - vmax = np.max(x, axis=None, out=None) - if(norm == "log")and(vmin <= 0): - raise ValueError(about._errors.cstring( - "ERROR: nonpositive value(s).")) - - # not a number - xmesh = np.nan * \ - np.empty(self.para[::-1] + 1, dtype=np.float16, order='C') - xmesh[4, 1] = None - xmesh[1, 4] = None - lm = 0 - for mm in xrange(self.para[1] + 1): - xmesh[mm][mm:] = x[lm:lm + self.para[0] + 1 - mm] - lm += self.para[0] + 1 - mm - - s_ = np.array([1, self.para[1] / self.para[0] - * (1.0 + 0.159 * bool(cbar))]) - fig = pl.figure(num=None, figsize=( - 6.4 * s_[0], 6.4 * s_[1]), dpi=None, facecolor="none", edgecolor="none", frameon=False, FigureClass=pl.Figure) - ax0 = fig.add_axes( - [0.06 / s_[0], 0.06 / s_[1], 1.0 - 0.12 / s_[0], 1.0 - 0.12 / s_[1]]) - ax0.set_axis_bgcolor([0.0, 0.0, 0.0, 0.0]) - - xaxes = np.arange(self.para[0] + 2, dtype=np.int) - 0.5 - yaxes = np.arange(self.para[1] + 2, dtype=np.int) - 0.5 - if(norm == "log"): - n_ = ln(vmin=vmin, vmax=vmax) - else: - n_ = None - sub = ax0.pcolormesh(xaxes, yaxes, np.ma.masked_where(np.isnan( - xmesh), xmesh), cmap=cmap, norm=n_, vmin=vmin, vmax=vmax, clim=(vmin, vmax)) - ax0.set_xlim(xaxes[0], xaxes[-1]) - ax0.set_xticks([0], minor=False) - ax0.set_xlabel(r"$\ell$") - ax0.set_ylim(yaxes[0], yaxes[-1]) - ax0.set_yticks([0], minor=False) - ax0.set_ylabel(r"$m$") - ax0.set_aspect("equal") - if(cbar): - if(norm == "log"): - f_ = lf(10, labelOnlyBase=False) - b_ = sub.norm.inverse( - np.linspace(0, 1, sub.cmap.N + 1)) - v_ = np.linspace( - sub.norm.vmin, sub.norm.vmax, sub.cmap.N) - else: - f_ = None - b_ = None - v_ = None - fig.colorbar(sub, ax=ax0, orientation="horizontal", fraction=0.1, pad=0.05, shrink=0.75, aspect=20, ticks=[ - vmin, vmax], format=f_, drawedges=False, boundaries=b_, values=v_) - ax0.set_title(title) - - if(bool(kwargs.get("save", False))): - fig.savefig(str(kwargs.get("save")), dpi=None, facecolor="none", edgecolor="none", orientation="portrait", - papertype=None, format=None, transparent=False, bbox_inches=None, pad_inches=0.1) - pl.close(fig) - else: - fig.canvas.draw() - - def getlm(self): # > compute all (l,m) - index = np.arange(self.dim) - n = 2 * self.paradict['lmax'] + 1 - m = np.ceil( - (n - np.sqrt(n**2 - 8 * (index - self.paradict['lmax']))) / 2 - ).astype(np.int) - l = index - self.paradict['lmax'] * m + m * (m - 1) // 2 - return l, m + return x.copy() # ---Added properties and methods--- @@ -568,7 +164,7 @@ class LMSpace(Space): lmax = np.int(lmax) if lmax < 1: raise ValueError(about._errors.cstring( - "ERROR: lmax: nonpositive number.")) + "ERROR: negative lmax is not allowed.")) # exception lmax == 2 (nside == 1) if (lmax % 2 == 0) and (lmax > 2): about.warnings.cprint( @@ -576,13 +172,18 @@ class LMSpace(Space): return lmax def _parse_mmax(self, mmax): - mmax = int(mmax) - if (mmax < 1) or(mmax > self.lmax): - about.warnings.cprint( - "WARNING: mmax parameter set to default.") + if mmax is None: mmax = self.lmax - if(mmax != self.lmax): + else: + mmax = int(mmax) + + if mmax < 1: + raise ValueError(about._errors.cstring( + "ERROR: mmax < 1 is not allowed.")) + if mmax > self.lmax: + raise ValueError(about._errors.cstring( + "ERROR: mmax > lmax is not allowed.")) + if mmax != self.lmax: about.warnings.cprint( - "WARNING: unrecommended parameter set (mmax <> lmax).") + "WARNING: unrecommended parameter combination (mmax <> lmax).") return mmax -