diff --git a/demos/demo_faraday.py b/demos/demo_faraday.py index 0a9679973a8977cdfc2169bf8d2df72ed6358c78..61dd5e3c6df8b8754810603a549e0d31e008d29d 100644 --- a/demos/demo_faraday.py +++ b/demos/demo_faraday.py @@ -101,7 +101,7 @@ def run(projection=False, power=False): z_space = rg_space([768, 384], dist=[1/360, 1/180]) m5 = m1.transform(y_space) m5.cast_domain(z_space) - m5.set_val(np.roll(m5.val[::-1, ::-1], y_space.nlon()//2, axis=1)) # rearrange value array + m5.set_val(np.roll(m5.val[::-1, ::-1], y_space.paradict['nlon']//2, axis=1)) # rearrange value array m5.plot(title=r"$m$ on a regular 2D grid", **nicely) if(power): diff --git a/lm/nifty_lm.py b/lm/nifty_lm.py index 9b74265eda40abace0c8b96fd6bf142efaee36e6..ce2c52ac55052e2cf498b7802814f3cb3de22641 100644 --- a/lm/nifty_lm.py +++ b/lm/nifty_lm.py @@ -35,7 +35,6 @@ from __future__ import division import os import numpy as np -from numpy import pi import pylab as pl from matplotlib.colors import LogNorm as ln from matplotlib.ticker import LogFormatter as lf @@ -123,7 +122,7 @@ class lm_space(point_space): """ def __init__(self, lmax, mmax=None, dtype=np.dtype('complex128'), - datamodel='np'): + datamodel='np', comm=gc['default_comm']): """ Sets the attributes for an lm_space class instance. @@ -176,6 +175,7 @@ class lm_space(point_space): self.discrete = True self.harmonic = True self.distances = (np.float(1),) + self.comm = self._parse_comm(comm) self.power_indices = lm_power_indices( lmax=self.paradict['lmax'], @@ -200,93 +200,11 @@ class lm_space(point_space): mmax=self.paradict['mmax'], dtype=self.dtype) -# def _enforce_shape(self, x): -# """ -# Shapes an array of valid field values correctly, according to the -# specifications of the space instance. -# -# Parameters -# ---------- -# x : numpy.ndarray -# Array containing the field values to be put into shape. -# -# Returns -# ------- -# y : numpy.ndarray -# Correctly shaped array. -# """ -# about.warnings.cprint("WARNING: enforce_shape is deprecated!") -# -# x = np.array(x) -# if(np.size(x) != self.get_dim(split=False)): -# raise ValueError(about._errors.cstring("ERROR: dimension mismatch ( " + -# str(np.size(x)) + " <> " + str(self.get_dim(split=False)) + " ).")) -# elif(not np.all(np.array(np.shape(x))==self.get_dim(split=True))): -# about.warnings.cprint("WARNING: reshaping forced.") -# -# return x.reshape(self.get_dim(split=True), order='C') - -# def lmax(self): -# """ -# Returns the maximum quantum number :math:`\ell`. -# -# Returns -# ------- -# lmax : int -# Maximum quantum number :math:`\ell`. -# """ -# return self.paradict['lmax'] -# -# def mmax(self): -# """ -# Returns the maximum quantum number :math:`m`. -# -# Returns -# ------- -# mmax : int -# Maximum quantum number :math:`m`. -# -# """ -# return self.paradict['mmax'] - def get_shape(self): mmax = self.paradict['mmax'] lmax = self.paradict['lmax'] return (np.int((mmax + 1) * (lmax + 1) - ((lmax + 1) * lmax) // 2),) -# -> inherited -# def get_dim(self, split=False): -# """ -# Computes the dimension of the space, i.e.\ the number of spherical -# harmonics components that are stored. -# -# Parameters -# ---------- -# split : bool, *optional* -# Whether to return the dimension as an array with one component -# or as a scalar (default: False). -# -# Returns -# ------- -# dim : {int, numpy.ndarray} -# Number of spherical harmonics components. -# -# Notes -# ----- -# Due to the symmetry assumption, only the components with -# non-negative :math:`m` are stored and only these components are -# counted here. -# """ -# # dim = (mmax+1)*(lmax-mmax/2+1) -# if(split): -# return self.get_shape() -# # return -# # np.array([(self.para[0]+1)*(self.para[1]+1)-(self.para[1]+1)*self.para[1]//2],dtype=np.int) -# else: -# return np.prod(self.get_shape()) -# # return -# # (self.para[0]+1)*(self.para[1]+1)-(self.para[1]+1)*self.para[1]//2 - def get_dof(self, split=False): """ Computes the number of degrees of freedom of the space, taking into @@ -344,160 +262,30 @@ class lm_space(point_space): mol[self.paradict['lmax'] + 1:] = 2 # redundant: (l,m) and (l,-m) return mol + def _cast_to_d2o(self, x, dtype=None, hermitianize=True, **kwargs): + raise NotImplementedError + + def _cast_to_np(self, x, dtype=None, hermitianize=True, **kwargs): + casted_x = super(lm_space, self)._cast_to_np(x=x, + dtype=dtype, + **kwargs) + complexity_mask = np.iscomplex(casted_x[:self.paradict['lmax']+1]) + if np.any(complexity_mask): + about.warnings.cprint("WARNING: Taking only the real parts for " + + "all complex entries where lmax==0") + casted_x[complexity_mask] = np.abs(casted_x[complexity_mask]) + return casted_x + # TODO: Extend to binning/log - def enforce_power(self, spec): - size = self.paradict['lmax'] + 1 - kindex = np.arange(size, dtype=np.array(self.distances).dtype) + 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 _getlm(self): # > compute all (l,m) - index = np.arange(self.get_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 - - def _enforce_values(self, x, extend=True): - """ - Computes valid field values from a given object, taking into - account data types, size, and hermitian symmetry. - - Parameters - ---------- - x : {float, numpy.ndarray, nifty.field} - Object to be transformed into an array of valid field values. - - Returns - ------- - x : numpy.ndarray - Array containing the valid field values. - - Other parameters - ---------------- - extend : bool, *optional* - Whether a scalar is extented to a constant array or not - (default: True). - """ - if(isinstance(x, field)): - if(self == x.domain): - if(self.dtype is not x.domain.dtype): - raise TypeError(about._errors.cstring("ERROR: inequal data types ( '" + str( - np.result_type(self.dtype)) + "' <> '" + str(np.result_type(x.domain.dtype)) + "' ).")) - else: - x = np.copy(x.val) - else: - raise ValueError(about._errors.cstring( - "ERROR: inequal domains.")) - else: - if(np.size(x) == 1): - if(extend): - x = self.dtype( - x) * np.ones(self.get_dim(split=True), dtype=self.dtype, order='C') - else: - if(np.isscalar(x)): - x = np.array([x], dtype=self.dtype) - else: - x = np.array(x, dtype=self.dtype) - else: - x = self._enforce_shape(np.array(x, dtype=self.dtype)) - - if(np.size(x) != 1)and(np.any(x.imag[:self.para[0] + 1] != 0)): - about.warnings.cprint("WARNING: forbidden values reset.") - x.real[:self.para[0] + 1] = np.absolute(x[:self.para[0] + 1]) * (np.sign(x.real[:self.para[0] + 1]) + ( - np.sign(x.real[:self.para[0] + 1]) == 0) * np.sign(x.imag[:self.para[0] + 1])).astype(np.int) - x.imag[:self.para[0] + 1] = 0 # x.imag[l,m==0] = 0 - - # check finiteness - if(not np.all(np.isfinite(x))): - about.warnings.cprint("WARNING: infinite value(s).") - - return x - - 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): - return np.zeros(self.get_dim(split=True), dtype=self.dtype, order='C') - - elif(arg[0] == "pm1"): - x = random.pm1(dtype=self.dtype, shape=self.get_dim(split=True)) - - elif(arg[0] == "gau"): - x = random.gau(dtype=self.dtype, shape=self.get_dim( - split=True), mean=None, dev=arg[2], var=arg[3]) - - elif(arg[0] == "syn"): - lmax = self.para[0] # lmax - if(self.dtype == np.dtype('complex64')): - if 'libsharp_wrapper_gl' in gdi: # default - x = gl.synalm_f(arg[1], lmax=lmax, mmax=lmax) - else: - x = hp.synalm(arg[1].astype(np.complex128), lmax=lmax, mmax=lmax).astype( - np.complex64) # FIXME: `verbose` kwarg - else: - if 'healpy' in gdi: # default - # FIXME: `verbose` kwarg - x = hp.synalm(arg[1], lmax=lmax, mmax=lmax) - else: - x = gl.synalm(arg[1], lmax=lmax, mmax=lmax) - - return x - - elif(arg[0] == "uni"): - x = random.uni(dtype=self.dtype, shape=self.get_dim( - split=True), vmin=arg[1], vmax=arg[2]) - - else: - raise KeyError(about._errors.cstring( - "ERROR: unsupported random key '" + str(arg[0]) + "'.")) - - if(np.any(x.imag[:self.para[0] + 1] != 0)): - x.real[:self.para[0] + 1] = np.absolute(x[:self.para[0] + 1]) * (np.sign(x.real[:self.para[0] + 1]) + ( - np.sign(x.real[:self.para[0] + 1]) == 0) * np.sign(x.imag[:self.para[0] + 1])).astype(np.int) - x.imag[:self.para[0] + 1] = 0 # x.imag[l,m==0] = 0 - - return x - def check_codomain(self, codomain): """ Checks whether a given codomain is compatible to the @@ -521,29 +309,28 @@ class lm_space(point_space): if codomain is None: return False - if(not isinstance(codomain, space)): - raise TypeError(about._errors.cstring("ERROR: invalid input.")) + if not isinstance(codomain, space): + raise TypeError(about._errors.cstring( + "ERROR: The given codomain must be a nifty lm_space.")) if self.datamodel is not codomain.datamodel: return False - if(self == codomain): - return True - - elif(isinstance(codomain, gl_space)): - # lmax==mmax nlat==lmax+1 + elif isinstance(codomain, gl_space): + # lmax==mmax + # nlat==lmax+1 # nlon==2*lmax+1 - if(self.para[0] == self.para[1])and(codomain.para[0] == self.para[0] + 1)and(codomain.para[1] == 2 * self.para[0] + 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 - else: - about.warnings.cprint("WARNING: unrecommended codomain.") - elif(isinstance(codomain, hp_space)): - # lmax==mmax 3*nside-1==lmax - if(self.para[0] == self.para[1])and(3 * codomain.para[0] - 1 == self.para[0]): + elif isinstance(codomain, hp_space): + # lmax==mmax + # 3*nside-1==lmax + if ((self.paradict['lmax'] == self.paradict['mmax']) and + (3*codomain.paradict['nside']-1 == self.paradict['lmax'])): return True - else: - about.warnings.cprint("WARNING: unrecommended codomain.") return False @@ -573,36 +360,111 @@ class lm_space(point_space): .. [#] K.M. Gorski et al., 2005, "HEALPix: A Framework for High-Resolution Discretization and Fast Analysis of Data Distributed on the Sphere", *ApJ* 622..759G. - .. [#] M. Reinecke and D. Sverre Seljebotn, 2013, "Libsharp - spherical + .. [#] M. Reinecke and D. Sverre Seljebotn, 2013, + "Libsharp - spherical harmonic transforms revisited"; `arXiv:1303.4945 <http://www.arxiv.org/abs/1303.4945>`_ """ - if(coname == "gl")or(coname is None)and(about.lm2gl.status): # order matters - if(self.dtype == np.dtype('complex64')): - # nlat,nlon = lmax+1,2*lmax+1 - return gl_space(self.para[0] + 1, nlon=2 * self.para[0] + 1, dtype=np.float32) + if coname == 'gl' or (coname is None and gc['lm2gl']): + if self.dtype == np.dtype('complex64'): + new_dtype = np.float32 + elif self.dtype == np.dtype('complex128'): + new_dtype = np.float64 else: - # nlat,nlon = lmax+1,2*lmax+1 - return gl_space(self.para[0] + 1, nlon=2 * self.para[0] + 1, dtype=np.float64) - - elif(coname == "hp")or(coname is None)and(not about.lm2gl.status): - return hp_space((self.para[0] + 1) // 3) # nside = (lmax+1)/3 - + raise NotImplementedError + nlat = self.paradict['lmax'] + 1 + nlon = self.paradict['lmax'] * 2 + 1 + return gl_space(nlat=nlat, nlon=nlon, dtype=new_dtype) + elif coname == 'hp' or (coname is None and not gc['lm2gl']): + nside = (self.paradict['lmax']+1) // 3 + return hp_space(nside=nside) else: raise ValueError(about._errors.cstring( - "ERROR: unsupported or incompatible space '" + coname + "'.")) + "ERROR: unsupported or incompatible codomain '"+coname+"'.")) + + 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[) - def _dotlm(self, x, y): # > compute inner product - dot = np.sum(x.real[:self.para[0] + 1] * - y.real[:self.para[0] + 1], axis=None, dtype=None, out=None) - dot += 2 * np.sum(x.real[self.para[0] + 1:] * - y.real[:self.para[0] + 1:], axis=None, dtype=None, out=None) - dot += 2 * np.sum(x.imag[self.para[0] + 1:] * - y.imag[:self.para[0] + 1:], axis=None, dtype=None, out=None) - return dot + (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: + return np.zeros(self.get_shape(), dtype=self.dtype) + + elif arg[0] == "pm1": + x = random.pm1(dtype=self.dtype, shape=self.get_shape()) + return self.cast(x) + + elif arg[0] == "gau": + x = random.gau(dtype=self.dtype, + shape=self.get_shape(), + mean=arg[1], + dev=arg[2], + var=arg[3]) + return self.cast(x) + + elif arg[0] == "syn": + lmax = self.paradict['lmax'] + mmax = self.paradict['mmax'] + if self.dtype == np.dtype('complex64'): + if 'libsharp_wrapper_gl' in gdi: + x = gl.synalm_f(arg[1], lmax=lmax, mmax=mmax) + else: + x = hp.synalm(arg[1].astype(np.complex128), + lmax=lmax, mmax=mmax).astype(np.complex64) + else: + if 'healpy' in gdi: + x = hp.synalm(arg[1], lmax=lmax, mmax=mmax) + else: + x = gl.synalm(arg[1], lmax=lmax, mmax=mmax) + return x + + elif arg[0] == "uni": + x = random.uni(dtype=self.dtype, + shape=self.get_shape(), + vmin=arg[1], + vmax=arg[2]) + return self.cast(x) + + else: + raise KeyError(about._errors.cstring( + "ERROR: unsupported random key '" + str(arg[0]) + "'.")) def calc_dot(self, x, y): """ @@ -621,17 +483,26 @@ class lm_space(point_space): dot : scalar Inner product of the two arrays. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) - y = self._enforce_shape(np.array(y, dtype=self.dtype)) - # inner product - if 'libsharp_wrapper_gl' in gdi: # default - if(self.dtype == np.dtype('complex64')): - return gl.dotlm_f(x, y, lmax=self.para[0], mmax=self.para[1]) + x = self.cast(x) + y = self.cast(y) + + if 'libsharp_wrapper_gl' in gdi: + lmax = self.paradict['lmax'] + mmax = self.paradict['mmax'] + if self.dtype == np.dtype('complex64'): + return gl.dotlm_f(x, y, lmax=lmax, mmax=mmax) else: - return gl.dotlm(x, y, lmax=self.para[0], mmax=self.para[1]) + return gl.dotlm(x, y, lmax=lmax, mmax=mmax) else: return self._dotlm(x, y) + def _dotlm(self, x, y): + lmax = self.paradict['lmax'] + dot = np.sum(x.real[:lmax + 1] * y.real[:lmax + 1]) + dot += 2 * np.sum(x.real[lmax + 1:] * y.real[lmax + 1:]) + dot += 2 * np.sum(x.imag[lmax + 1:] * y.imag[lmax + 1:]) + return dot + def calc_transform(self, x, codomain=None, **kwargs): """ Computes the transform of a given array of field values. @@ -649,34 +520,43 @@ class lm_space(point_space): Tx : numpy.ndarray Transformed array """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) - if(codomain is None): - return x # T == id + if codomain is None: + codomain = self.get_codomain() - # check codomain - self.check_codomain(codomain) # a bit pointless + # Check if the given codomain is suitable for the transformation + if not self.check_codomain(codomain): + raise ValueError(about._errors.cstring( + "ERROR: unsupported codomain.")) - if(self == codomain): - return x # T == id + if isinstance(codomain, gl_space): + nlat = codomain.paradict['nlat'] + nlon = codomain.paradict['nlon'] + lmax = self.paradict['lmax'] + mmax = self.paradict['mmax'] - elif(isinstance(codomain, gl_space)): # transform - if(self.dtype == np.dtype('complex64')): - Tx = gl.alm2map_f(x, nlat=codomain.para[0], nlon=codomain.para[ - 1], lmax=self.para[0], mmax=self.para[1], cl=False) + if self.dtype == np.dtype('complex64'): + Tx = gl.alm2map_f(x, nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax, cl=False) else: - Tx = gl.alm2map(x, nlat=codomain.para[0], nlon=codomain.para[ - 1], lmax=self.para[0], mmax=self.para[1], cl=False) - # weight if discrete - if(codomain.discrete): + Tx = gl.alm2map(x, nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax, cl=False) + # re-weight if discrete + if codomain.discrete: Tx = codomain.calc_weight(Tx, power=0.5) - elif(isinstance(codomain, hp_space)): + elif isinstance(codomain, hp_space): + nside = codomain.paradict['nside'] + lmax = self.paradict['lmax'] + mmax = self.paradict['mmax'] + # transform - Tx = hp.alm2map(x.astype(np.complex128), codomain.para[0], lmax=self.para[0], mmax=self.para[ - 1], pixwin=False, fwhm=0.0, sigma=None, invert=False, pol=True, inplace=False) # FIXME: `verbose` kwarg - # weight if discrete + Tx = hp.alm2map(x.astype(np.complex128), nside, lmax=lmax, + mmax=mmax, pixwin=False, fwhm=0.0, sigma=None, + invert=False, pol=True, inplace=False) + # re-weight if discrete if(codomain.discrete): Tx = codomain.calc_weight(Tx, power=0.5) @@ -705,22 +585,23 @@ class lm_space(point_space): Gx : numpy.ndarray Smoothed array. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) # check sigma - if(sigma == 0): + if sigma == 0: return x - elif(sigma == -1): + elif sigma == -1: about.infos.cprint("INFO: invalid sigma reset.") - sigma = 4.5 / (self.para[0] + 1) # sqrt(2)*pi/(lmax+1) - elif(sigma < 0): + sigma = np.sqrt(2) * np.pi / (self.paradict['lmax'] + 1) + elif sigma < 0: raise ValueError(about._errors.cstring("ERROR: invalid sigma.")) - # smooth - if 'healpy' in gdi: # default - # no overwrite - return hp.smoothalm(x, fwhm=0.0, sigma=sigma, invert=False, pol=True, mmax=self.para[1], verbose=False, inplace=False) + if 'healpy' in gdi: + return hp.smoothalm(x, fwhm=0.0, sigma=sigma, invert=False, + pol=True, mmax=self.paradict['mmax'], + verbose=False, inplace=False) else: - # no overwrite - return gl.smoothalm(x, lmax=self.para[0], mmax=self.para[1], fwhm=0.0, sigma=sigma, overwrite=False) + return gl.smoothalm(x, lmax=self.paradict['lmax'], + mmax=self.paradict['mmax'], + fwhm=0.0, sigma=sigma, overwrite=False) def calc_power(self, x, **kwargs): """ @@ -737,20 +618,28 @@ class lm_space(point_space): spec : numpy.ndarray Power contained in the input array. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) + lmax = self.paradict['lmax'] + mmax = self.paradict['mmax'] + # power spectrum - if(self.dtype == np.dtype('complex64')): - if 'libsharp_wrapper_gl' in gdi: # default - return gl.anaalm_f(x, lmax=self.para[0], mmax=self.para[1]) + if self.dtype == np.dtype('complex64'): + if 'libsharp_wrapper_gl' in gdi: + return gl.anaalm_f(x, lmax=lmax, mmax=mmax) else: - return hp.alm2cl(x.astype(np.complex128), alms2=None, lmax=self.para[0], mmax=self.para[1], lmax_out=self.para[0], nspec=None).astype(np.float32) + return hp.alm2cl(x.astype(np.complex128), alms2=None, + lmax=lmax, mmax=mmax, lmax_out=lmax, + nspec=None).astype(np.float32) else: - if 'healpy' in gdi: # default - return hp.alm2cl(x, alms2=None, lmax=self.para[0], mmax=self.para[1], lmax_out=self.para[0], nspec=None) + if 'healpy' in gdi: + return hp.alm2cl(x, alms2=None, lmax=lmax, mmax=mmax, + lmax_out=lmax, nspec=None) else: - return gl.anaalm(x, lmax=self.para[0], mmax=self.para[1]) + return gl.anaalm(x, lmax=lmax, mmax=mmax) - 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): + 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. @@ -934,12 +823,20 @@ class lm_space(point_space): return "<nifty_lm.lm_space>" def __str__(self): - return "nifty_lm.lm_space instance\n- lmax = " + str(self.para[0]) + "\n- mmax = " + str(self.para[1]) + "\n- dtype = numpy." + str(np.result_type(self.dtype)) - -# ----------------------------------------------------------------------------- + return ("nifty_lm.lm_space instance\n- lmax = " + + str(self.paradict['lmax']) + + "\n- mmax = " + str(self.paradict['mmax']) + + "\n- dtype = " + str(self.dtype)) + def getlm(self): # > compute all (l,m) + index = np.arange(self.get_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 -# ----------------------------------------------------------------------------- class gl_space(point_space): """ @@ -995,7 +892,7 @@ class gl_space(point_space): """ def __init__(self, nlat, nlon=None, dtype=np.dtype('float'), - datamodel='np'): + datamodel='np', comm=gc['default_comm']): """ Sets the attributes for a gl_space class instance. @@ -1046,6 +943,7 @@ class gl_space(point_space): self.distances = tuple(gl.vol(self.paradict['nlat'], nlon=self.paradict['nlon'] ).astype(np.float)) + self.comm = self._parse_comm(comm) @property def para(self): @@ -1058,86 +956,14 @@ class gl_space(point_space): self.paradict['nlat'] = x[0] self.paradict['nlon'] = x[1] - def _enforce_shape(self, x): - """ - Shapes an array of valid field values correctly, according to the - specifications of the space instance. - - Parameters - ---------- - x : numpy.ndarray - Array containing the field values to be put into shape. - - Returns - ------- - y : numpy.ndarray - Correctly shaped array. - """ - about.warnings.cprint("WARNING: enforce_shape is deprecated!") - - x = np.array(x) - if(np.size(x) != self.get_dim(split=False)): - raise ValueError(about._errors.cstring("ERROR: dimension mismatch ( " + - str(np.size(x)) + " <> " + str(self.get_dim(split=False)) + " ).")) -# elif(not np.all(np.array(np.shape(x))==self.get_dim(split=True))): -# about.warnings.cprint("WARNING: reshaping forced.") - - return x.reshape(self.get_dim(split=True), order='C') - def copy(self): return gl_space(nlat=self.paradict['nlat'], nlon=self.paradict['nlon'], dtype=self.dtype) - def nlat(self): - """ - Returns the number of latitudinal bins. - - Returns - ------- - nlat : int - Number of latitudinal bins, or rings. - """ - return self.paradict['nlat'] - - def nlon(self): - """ - Returns the number of longitudinal bins. - - Returns - ------- - nlon : int - Number of longitudinal bins. - """ - return self.paradict['nlon'] - def get_shape(self): return (np.int((self.paradict['nlat'] * self.paradict['nlon'])),) -# -> inherited -# def get_dim(self, split=False): -# """ -# Computes the dimension of the space, i.e.\ the number of pixels. -# -# Parameters -# ---------- -# split : bool, *optional* -# Whether to return the dimension as an array with one component -# or as a scalar (default: False). -# -# Returns -# ------- -# dim : {int, numpy.ndarray} -# Dimension of the space. -# """ -# # dim = nlat*nlon -# if(split): -# return self.get_shape() -# # return np.array([self.para[0]*self.para[1]],dtype=np.int) -# else: -# return np.prod(self.get_shape()) -# # return self.para[0]*self.para[1] - def get_dof(self, split=False): """ Computes the number of degrees of freedom of the space. @@ -1155,14 +981,107 @@ class gl_space(point_space): # dof = dim return self.get_dim(split=split) + def get_meta_volume(self, split=False): + """ + 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 + ----- + For Gauss-Legendre pixelizations, the meta volumes are the pixel + sizes. + """ + if not split: + return np.float(4 * np.pi) + else: + mol = self.cast(1, dtype=np.float) + return self.calc_weight(mol, power=1) + # TODO: Extend to binning/log - def enforce_power(self, spec): - size = self.paradict['nlat'] - kindex = np.arange(size, dtype=np.array(self.distances).dtype) + def enforce_power(self, spec, size=None, kindex=None): + if kindex is None: + kindex_size = self.paradict['nlat'] + 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 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:`gl_space` and + :py:class:`lm_space`. + """ + if not isinstance(codomain, space): + raise TypeError(about._errors.cstring("ERROR: invalid input.")) + + if codomain is None: + return False + + if self.datamodel is not codomain.datamodel: + return False + + if isinstance(codomain, lm_space): + nlat = self.paradict['nlat'] + nlon = self.paradict['nlon'] + lmax = codomain.paradict['lmax'] + mmax = codomain.paradict['mmax'] + # nlon==2*lat-1 + # lmax==nlat-1 + # lmax==mmax + if (nlon == 2*nlat-1) and (lmax == nlat-1) and (lmax == mmax): + return True + + return False + + def get_codomain(self, **kwargs): + """ + Generates a compatible codomain to which transformations are + reasonable, i.e.\ an instance of the :py:class:`lm_space` class. + + Returns + ------- + codomain : nifty.lm_space + A compatible codomain. + """ + nlat = self.paradict['nlat'] + lmax = nlat-1 + mmax = nlat-1 + # lmax,mmax = nlat-1,nlat-1 + if self.dtype == np.dtype('float32'): + return lm_space(lmax=lmax, mmax=mmax, dtype=np.complex64) + else: + return lm_space(lmax=lmax, mmax=mmax, dtype=np.complex128) + def get_random_values(self, **kwargs): """ Generates random field values according to the specifications given @@ -1201,128 +1120,45 @@ class gl_space(point_space): vmax : float, *optional* Upper limit for a uniform distribution (default: 1). """ - arg = random.parse_arguments(self, **kwargs) - - if(arg is None): - x = np.zeros(self.get_dim(split=True), dtype=self.dtype, order='C') - - elif(arg[0] == "pm1"): - x = random.pm1(dtype=self.dtype, shape=self.get_dim(split=True)) - - elif(arg[0] == "gau"): - x = random.gau(dtype=self.dtype, shape=self.get_dim( - split=True), mean=None, dev=arg[2], var=arg[3]) - - elif(arg[0] == "syn"): - lmax = self.para[0] - 1 # nlat-1 - if(self.dtype == np.dtype('float32')): - x = gl.synfast_f(arg[1], nlat=self.para[0], nlon=self.para[ - 1], lmax=lmax, mmax=lmax, alm=False) - else: - x = gl.synfast(arg[1], nlat=self.para[0], nlon=self.para[ - 1], lmax=lmax, mmax=lmax, alm=False) - # weight if discrete - if(self.discrete): - x = self.calc_weight(x, power=0.5) - - elif(arg[0] == "uni"): - x = random.uni(dtype=self.dtype, shape=self.get_dim( - split=True), vmin=arg[1], vmax=arg[2]) - - else: - raise KeyError(about._errors.cstring( - "ERROR: unsupported random key '" + str(arg[0]) + "'.")) - - return x - - def check_codomain(self, codomain): - """ - Checks whether a given codomain is compatible to the 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:`gl_space` and - :py:class:`lm_space`. - """ - if codomain is None: - return False + arg = random.parse_arguments(self, **kwargs) - if(not isinstance(codomain, space)): - raise TypeError(about._errors.cstring("ERROR: invalid input.")) + if(arg is None): + x = np.zeros(self.get_shape(), dtype=self.dtype) - if self.datamodel is not codomain.datamodel: - return False + elif(arg[0] == "pm1"): + x = random.pm1(dtype=self.dtype, shape=self.get_shape()) - if(self == codomain): - return True + elif(arg[0] == "gau"): + x = random.gau(dtype=self.dtype, + shape=self.get_shape(), + mean=arg[1], dev=arg[2], var=arg[3]) - if(isinstance(codomain, lm_space)): - # nlon==2*lat-1 lmax==nlat-1 - # lmax==mmax - if(self.para[1] == 2 * self.para[0] - 1)and(codomain.para[0] == self.para[0] - 1)and(codomain.para[0] == codomain.para[1]): - return True + elif(arg[0] == "syn"): + nlat = self.paradict['nlat'] + nlon = self.paradict['nlon'] + lmax = nlat - 1 + if self.dtype == np.dtype('float32'): + x = gl.synfast_f(arg[1], + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=lmax, alm=False) else: - about.warnings.cprint("WARNING: unrecommended codomain.") - - return False + x = gl.synfast(arg[1], + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=lmax, alm=False) + # weight if discrete + if self.discrete: + x = self.calc_weight(x, power=0.5) - def get_codomain(self, **kwargs): - """ - Generates a compatible codomain to which transformations are - reasonable, i.e.\ an instance of the :py:class:`lm_space` class. + elif(arg[0] == "uni"): + x = random.uni(dtype=self.dtype, + shape=self.get_shape(), + vmin=arg[1], vmax=arg[2]) - Returns - ------- - codomain : nifty.lm_space - A compatible codomain. - """ - if(self.dtype == np.dtype('float32')): - # lmax,mmax = nlat-1,nlat-1 - return lm_space(self.para[0] - 1, mmax=self.para[0] - 1, dtype=np.complex64) else: - # lmax,mmax = nlat-1,nlat-1 - return lm_space(self.para[0] - 1, mmax=self.para[0] - 1, dtype=np.complex128) - - def get_meta_volume(self, split=False): - """ - 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. + raise KeyError(about._errors.cstring( + "ERROR: unsupported random key '" + str(arg[0]) + "'.")) - Notes - ----- - For Gauss-Legendre pixelizations, the meta volumes are the pixel - sizes. - """ - if not split: - return np.float(4 * pi) - else: - mol = self.cast(1, dtype=np.float) - return self.calc_weight(mol, power=1) + return x def calc_weight(self, x, power=1): """ @@ -1340,15 +1176,25 @@ class gl_space(point_space): y : numpy.ndarray Weighted array. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) # weight - if(self.dtype == np.dtype('float32')): - return gl.weight_f(x, np.array(self.distances), p=np.float32(power), nlat=self.para[0], nlon=self.para[1], overwrite=False) + nlat = self.paradict['nlat'] + nlon = self.paradict['nlon'] + if self.dtype == np.dtype('float32'): + return gl.weight_f(x, + np.array(self.distances), + p=np.float32(power), + nlat=nlat, nlon=nlon, + overwrite=False) else: - return gl.weight(x, np.array(self.distances), p=np.float64(power), nlat=self.para[0], nlon=self.para[1], overwrite=False) + return gl.weight(x, + np.array(self.distances), + p=np.float32(power), + nlat=nlat, nlon=nlon, + overwrite=False) def get_weight(self, power=1): - # TODO: Check if this function is compatible to the rest of the nifty code + # TODO: Check if this function is compatible to the rest of nifty # TODO: Can this be done more efficiently? dummy = self.dtype(1) weighted_dummy = self.calc_weight(dummy, power=power) @@ -1376,29 +1222,31 @@ class gl_space(point_space): Only instances of the :py:class:`lm_space` or :py:class:`gl_space` classes are allowed as `codomain`. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) - - if(codomain is None): - return x # T == id - - # check codomain - self.check_codomain(codomain) # a bit pointless + x = self.cast(x) - if(self == codomain): - return x # T == id + # Check if the given codomain is suitable for the transformation + if not self.check_codomain(codomain): + raise ValueError(about._errors.cstring( + "ERROR: unsupported codomain.")) - if(isinstance(codomain, lm_space)): + if isinstance(codomain, lm_space): # weight if discrete - if(self.discrete): + if self.discrete: x = self.calc_weight(x, power=-0.5) # transform - if(self.dtype == np.dtype('float32')): - Tx = gl.map2alm_f(x, nlat=self.para[0], nlon=self.para[ - 1], lmax=codomain.para[0], mmax=codomain.para[1]) + nlat = self.paradict['nlat'] + nlon = self.paradict['nlon'] + lmax = codomain.paradict['lmax'] + mmax = codomain.paradict['mmax'] + + if self.dtype == np.dtype('float32'): + Tx = gl.map2alm_f(x, + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax) else: - Tx = gl.map2alm(x, nlat=self.para[0], nlon=self.para[ - 1], lmax=codomain.para[0], mmax=codomain.para[1]) - + Tx = gl.map2alm(x, + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax) else: raise ValueError(about._errors.cstring( "ERROR: unsupported transformation.")) @@ -1424,17 +1272,21 @@ class gl_space(point_space): Gx : numpy.ndarray Smoothed array. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) # check sigma - if(sigma == 0): + if sigma == 0: return x - elif(sigma == -1): + elif sigma == -1: about.infos.cprint("INFO: invalid sigma reset.") - sigma = 4.5 / self.para[0] # sqrt(2)*pi/(lmax+1) - elif(sigma < 0): + sigma = np.sqrt(2) * np.pi / self.paradict['nlat'] + elif sigma < 0: raise ValueError(about._errors.cstring("ERROR: invalid sigma.")) # smooth - return gl.smoothmap(x, nlat=self.para[0], nlon=self.para[1], lmax=self.para[0] - 1, mmax=self.para[0] - 1, fwhm=0.0, sigma=sigma) + nlat = self.paradict['nlat'] + return gl.smoothmap(x, + nlat=nlat, nlon=self.paradict['nlon'], + lmax=nlat - 1, mmax=nlat - 1, + fwhm=0.0, sigma=sigma) def calc_power(self, x, **kwargs): """ @@ -1451,17 +1303,29 @@ class gl_space(point_space): spec : numpy.ndarray Power contained in the input array. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) # weight if discrete - if(self.discrete): + if self.discrete: x = self.calc_weight(x, power=-0.5) - # power spectrum - if(self.dtype == np.dtype('float32')): - return gl.anafast_f(x, nlat=self.para[0], nlon=self.para[1], lmax=self.para[0] - 1, mmax=self.para[0] - 1, alm=False) + # calculate the power spectrum + nlat = self.paradict['nlat'] + nlon = self.paradict['nlon'] + lmax = nlat - 1 + mmax = nlat - 1 + if self.dtype == np.dtype('float32'): + return gl.anafast_f(x, + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax, + alm=False) else: - return gl.anafast(x, nlat=self.para[0], nlon=self.para[1], lmax=self.para[0] - 1, mmax=self.para[0] - 1, alm=False) + return gl.anafast(x, + nlat=nlat, nlon=nlon, + lmax=lmax, mmax=mmax, + alm=False) - def get_plot(self, x, title="", vmin=None, vmax=None, power=False, unit="", norm=None, cmap=None, cbar=True, other=None, legend=False, mono=True, **kwargs): + def get_plot(self, x, title="", vmin=None, vmax=None, power=False, + unit="", 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. @@ -1572,8 +1436,8 @@ class gl_space(point_space): ax0 = fig.add_axes([0.02, 0.05, 0.96, 0.9]) lon, lat = gl.bounds(self.para[0], nlon=self.para[1]) - lon = (lon - pi) * 180 / pi - lat = (lat - pi / 2) * 180 / pi + lon = (lon - np.pi) * 180 / np.pi + lat = (lat - np.pi / 2) * 180 / np.pi if(norm == "log"): n_ = ln(vmin=vmin, vmax=vmax) else: @@ -1612,10 +1476,10 @@ class gl_space(point_space): def __str__(self): return "nifty_lm.gl_space instance\n- nlat = " + str(self.para[0]) + "\n- nlon = " + str(self.para[1]) + "\n- dtype = numpy." + str(np.result_type(self.dtype)) -# ----------------------------------------------------------------------------- -# ----------------------------------------------------------------------------- + + class hp_space(point_space): """ @@ -1666,9 +1530,8 @@ class hp_space(point_space): vol : numpy.ndarray An array with one element containing the pixel size. """ - niter = 0 # default number of iterations used for transformations - def __init__(self, nside, datamodel='np'): + def __init__(self, nside, datamodel='np', comm=gc['default_comm']): """ Sets the attributes for a hp_space class instance. @@ -1694,6 +1557,7 @@ class hp_space(point_space): if 'healpy' not in gdi: raise ImportError(about._errors.cstring( "ERROR: healpy not available.")) + # check parameters self.paradict = hp_space_paradict(nside=nside) @@ -1708,7 +1572,8 @@ class hp_space(point_space): self.discrete = False self.harmonic = False - self.distances = (np.float(4 * pi / (12 * self.paradict['nside']**2)),) + self.distances = (np.float(4*np.pi / (12*self.paradict['nside']**2)),) + self.comm = self._parse_comm(comm) @property def para(self): @@ -1719,71 +1584,12 @@ class hp_space(point_space): def para(self, x): self.paradict['nside'] = x[0] - def _enforce_shape(self, x): - """ - Shapes an array of valid field values correctly, according to the - specifications of the space instance. - - Parameters - ---------- - x : numpy.ndarray - Array containing the field values to be put into shape. - - Returns - ------- - y : numpy.ndarray - Correctly shaped array. - """ - about.warnings.cprint("WARNING: enforce_shape is deprecated!") - - x = np.array(x) - if(np.size(x) != self.get_dim(split=False)): - raise ValueError(about._errors.cstring("ERROR: dimension mismatch ( " + - str(np.size(x)) + " <> " + str(self.get_dim(split=False)) + " ).")) -# elif(not np.all(np.array(np.shape(x))==self.get_dim(split=True))): -# about.warnings.cprint("WARNING: reshaping forced.") - - return x.reshape(self.get_dim(split=True), order='C') - def copy(self): return hp_space(nside=self.paradict['nside']) - def nside(self): - """ - Returns the resolution parameter. - - Returns - ------- - nside : int - HEALPix resolution parameter. - """ - return self.paradict['nside'] - def get_shape(self): return (np.int(12 * self.paradict['nside']**2),) - def get_dim(self, split=False): - """ - Computes the dimension of the space, i.e.\ the number of pixels. - - Parameters - ---------- - split : bool, *optional* - Whether to return the dimension as an array with one component - or as a scalar (default: False). - - Returns - ------- - dim : {int, numpy.ndarray} - Dimension of the space. - """ - if split: - return self.get_shape() - # return np.array([12*self.para[0]**2],dtype=np.int) - else: - return np.prod(self.get_shape()) - # return 12*self.para[0]**2 - def get_dof(self, split=False): """ Computes the number of degrees of freedom of the space. @@ -1801,81 +1607,45 @@ class hp_space(point_space): # dof = dim return self.get_dim(split=split) - # TODO: Extend to binning/log - def enforce_power(self, spec): - size = self.paradict['nside'] * 3 - kindex = np.arange(size, dtype=np.array(self.distances).dtype) - return self._enforce_power_helper(spec=spec, - size=size, - kindex=kindex) - - def get_random_values(self, **kwargs): + def get_meta_volume(self, split=False): """ - Generates random field values according to the specifications given - by the parameters. + Calculates the meta volumes. - Returns - ------- - x : numpy.ndarray - Valid field values. + 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. - Other parameters - ---------------- - random : string, *optional* - Specifies the probability distribution from which the random - numbers are to be drawn. - Supported distributions are: + Parameters + ---------- + total : bool, *optional* + Whether to return the total meta volume of the space or the + individual ones of each field component (default: False). - - "pm1" (uniform distribution over {+1,-1} - - "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[) + Returns + ------- + mol : {numpy.ndarray, float} + Meta volume of the field components or the complete space. - (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). - codomain : nifty.lm_space, *optional* - A compatible codomain for power indexing (default: None). - vmin : float, *optional* - Lower limit for a uniform distribution (default: 0). - vmax : float, *optional* - Upper limit for a uniform distribution (default: 1). + Notes + ----- + For HEALpix discretizations, the meta volumes are the pixel sizes. """ - arg = random.parse_arguments(self, **kwargs) - - if(arg is None): - x = np.zeros(self.get_dim(split=True), dtype=self.dtype, order='C') - - elif(arg[0] == "pm1"): - x = random.pm1(dtype=self.dtype, shape=self.get_dim(split=True)) - - elif(arg[0] == "gau"): - x = random.gau(dtype=self.dtype, shape=self.get_dim( - split=True), mean=None, dev=arg[2], var=arg[3]) - - elif(arg[0] == "syn"): - lmax = 3 * self.para[0] - 1 # 3*nside-1 - x = hp.synfast(arg[1], self.para[0], lmax=lmax, mmax=lmax, alm=False, - pol=True, pixwin=False, fwhm=0.0, sigma=None) # FIXME: `verbose` kwarg - # weight if discrete - if(self.discrete): - x = self.calc_weight(x, power=0.5) - - elif(arg[0] == "uni"): - x = random.uni(dtype=self.dtype, shape=self.get_dim( - split=True), vmin=arg[1], vmax=arg[2]) - + if not split: + return np.float(4 * np.pi) else: - raise KeyError(about._errors.cstring( - "ERROR: unsupported random key '" + str(arg[0]) + "'.")) + mol = self.cast(1, dtype=np.float) + return self.calc_weight(mol, power=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['nside'] * 3 + 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): """ @@ -1896,24 +1666,23 @@ class hp_space(point_space): Compatible codomains are instances of :py:class:`hp_space` and :py:class:`lm_space`. """ + if not isinstance(codomain, space): + raise TypeError(about._errors.cstring("ERROR: invalid input.")) + if codomain is None: return False - if(not isinstance(codomain, space)): - raise TypeError(about._errors.cstring("ERROR: invalid input.")) - if self.datamodel is not codomain.datamodel: return False - if(self == codomain): - return True - - if(isinstance(codomain, lm_space)): - # 3*nside-1==lmax lmax==mmax - if(3 * self.para[0] - 1 == codomain.para[0])and(codomain.para[0] == codomain.para[1]): + if isinstance(codomain, lm_space): + nside = self.paradict['nside'] + lmax = codomain.paradict['lmax'] + mmax = codomain.paradict['mmax'] + # 3*nside-1==lmax + # lmax==mmax + if (3*nside-1 == lmax) and (lmax == mmax): return True - else: - about.warnings.cprint("WARNING: unrecommended codomain.") return False @@ -1927,39 +1696,82 @@ class hp_space(point_space): codomain : nifty.lm_space A compatible codomain. """ - return lm_space(3 * self.para[0] - 1, mmax=3 * self.para[0] - 1, dtype=np.complex128) # lmax,mmax = 3*nside-1,3*nside-1 + lmax = 3*self.paradict['nside'] - 1 + mmax = lmax + return lm_space(lmax=lmax, mmax=mmax, dtype=np.dtype('complex128')) - def get_meta_volume(self, split=False): + def get_random_values(self, **kwargs): """ - 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). + Generates random field values according to the specifications given + by the parameters. Returns ------- - mol : {numpy.ndarray, float} - Meta volume of the field components or the complete space. + x : numpy.ndarray + Valid field values. - Notes - ----- - For HEALpix discretizations, the meta volumes are the pixel sizes. + 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} + - "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). + codomain : nifty.lm_space, *optional* + A compatible codomain for power indexing (default: None). + vmin : float, *optional* + Lower limit for a uniform distribution (default: 0). + vmax : float, *optional* + Upper limit for a uniform distribution (default: 1). """ - if not split: - return np.float(4 * pi) + arg = random.parse_arguments(self, **kwargs) + + if arg is None: + x = np.zeros(self.get_shape(), dtype=self.dtype) + + elif arg[0] == "pm1": + x = random.pm1(dtype=self.dtype, shape=self.get_shape()) + + elif arg[0] == "gau": + x = random.gau(dtype=self.dtype, shape=self.get_shape(), + mean=arg[1], dev=arg[2], var=arg[3]) + + elif arg[0] == "syn": + nside = self.paradict['nside'] + lmax = 3*nside-1 + x = hp.synfast(arg[1], nside, lmax=lmax, mmax=lmax, alm=False, + pol=True, pixwin=False, fwhm=0.0, sigma=None) + # weight if discrete + if self.discrete: + x = self.calc_weight(x, power=0.5) + + elif arg[0] == "uni": + x = random.uni(dtype=self.dtype, shape=self.get_shape(), + vmin=arg[1], vmax=arg[2]) + else: - mol = self.cast(1, dtype=np.float) - return self.calc_weight(mol, power=1) + raise KeyError(about._errors.cstring( + "ERROR: unsupported random key '" + str(arg[0]) + "'.")) - def calc_transform(self, x, codomain=None, **kwargs): + return x + + def calc_transform(self, x, codomain=None, niter=0, **kwargs): """ Computes the transform of a given array of field values. @@ -1987,24 +1799,23 @@ class hp_space(point_space): Only instances of the :py:class:`lm_space` or :py:class:`hp_space` classes are allowed as `codomain`. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) - if(codomain is None): - return x # T == id - - # check codomain - self.check_codomain(codomain) # a bit pointless - - if(self == codomain): - return x # T == id + # Check if the given codomain is suitable for the transformation + if not self.check_codomain(codomain): + raise ValueError(about._errors.cstring( + "ERROR: unsupported codomain.")) - if(isinstance(codomain, lm_space)): + if isinstance(codomain, lm_space): # weight if discrete - if(self.discrete): + if self.discrete: x = self.calc_weight(x, power=-0.5) # transform - Tx = hp.map2alm(x.astype(np.float64), lmax=codomain.para[0], mmax=codomain.para[ - 1], iter=kwargs.get("iter", self.niter), pol=True, use_weights=False, datapath=None) + Tx = hp.map2alm(x.astype(np.float64, copy=False), + lmax=codomain.paradict['lmax'], + mmax=codomain.paradict['mmax'], + iter=niter, pol=True, use_weights=False, + datapath=None) else: raise ValueError(about._errors.cstring( @@ -2012,7 +1823,7 @@ class hp_space(point_space): return Tx.astype(codomain.dtype) - def calc_smooth(self, x, sigma=0, **kwargs): + def calc_smooth(self, x, sigma=0, niter=0, **kwargs): """ Smoothes an array of field values by convolution with a Gaussian kernel. @@ -2037,19 +1848,26 @@ class hp_space(point_space): Number of iterations performed in the HEALPix basis transformation. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + nside = self.paradict['nside'] + + x = self.cast(x) # check sigma - if(sigma == 0): + if sigma == 0: return x - elif(sigma == -1): + elif sigma == -1: about.infos.cprint("INFO: invalid sigma reset.") - sigma = 1.5 / self.para[0] # sqrt(2)*pi/(lmax+1) - elif(sigma < 0): + # sqrt(2)*pi/(lmax+1) + sigma = np.sqrt(2) * np.pi / (3. * nside) + elif sigma < 0: raise ValueError(about._errors.cstring("ERROR: invalid sigma.")) # smooth - return hp.smoothing(x, fwhm=0.0, sigma=sigma, invert=False, pol=True, iter=kwargs.get("iter", self.niter), lmax=3 * self.para[0] - 1, mmax=3 * self.para[0] - 1, use_weights=False, datapath=None) + lmax = 3*nside-1 + mmax = lmax + return hp.smoothing(x, fwhm=0.0, sigma=sigma, invert=False, pol=True, + iter=niter, lmax=lmax, mmax=mmax, + use_weights=False, datapath=None) - def calc_power(self, x, **kwargs): + def calc_power(self, x, niter=0, **kwargs): """ Computes the power of an array of field values. @@ -2070,14 +1888,22 @@ class hp_space(point_space): Number of iterations performed in the HEALPix basis transformation. """ - x = self._enforce_shape(np.array(x, dtype=self.dtype)) + x = self.cast(x) # weight if discrete - if(self.discrete): + if self.discrete: x = self.calc_weight(x, power=-0.5) + + nside = self.paradict['nside'] + lmax = 3*nside-1 + mmax = lmax # power spectrum - return hp.anafast(x, map2=None, nspec=None, lmax=3 * self.para[0] - 1, mmax=3 * self.para[0] - 1, iter=kwargs.get("iter", self.niter), alm=False, pol=True, use_weights=False, datapath=None) + return hp.anafast(x, map2=None, nspec=None, lmax=lmax, mmax=mmax, + iter=niter, alm=False, pol=True, use_weights=False, + datapath=None) - def get_plot(self, x, title="", vmin=None, vmax=None, power=False, unit="", norm=None, cmap=None, cbar=True, other=None, legend=False, mono=True, **kwargs): + def get_plot(self, x, title="", vmin=None, vmax=None, power=False, unit="", + 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. @@ -2205,4 +2031,4 @@ class hp_space(point_space): def __str__(self): return "nifty_lm.hp_space instance\n- nside = " + str(self.para[0]) -# ----------------------------------------------------------------------------- + diff --git a/nifty_core.py b/nifty_core.py index ead855d5ced5ccc3121478b2e6fa0219e3da55a9..276816a19b916b4f5305e4eca07e55610f37b4fd 100644 --- a/nifty_core.py +++ b/nifty_core.py @@ -1551,7 +1551,7 @@ class point_space(space): # deviation or variance elif arg[0] == 'gau': var = arg[3] - if np.isscalar(var) == True or var is None: + if np.isscalar(var) or var is None: processed_var = var else: try: diff --git a/rg/nifty_rg.py b/rg/nifty_rg.py index 068c99a83219d5c011ef0016272bf1120abb557b..469d0a3f2975c61ac88d086e65772fc65cb299cf 100644 --- a/rg/nifty_rg.py +++ b/rg/nifty_rg.py @@ -360,10 +360,12 @@ class rg_space(point_space): return False if not isinstance(codomain, rg_space): - raise TypeError(about._errors.cstring("ERROR: invalid input.")) + raise TypeError(about._errors.cstring( + "ERROR: The given codomain must be a nifty rg_space.")) if self.datamodel is not codomain.datamodel: return False + # check number of number and size of axes if not np.all(np.array(self.paradict['num']) == np.array(codomain.paradict['num'])): @@ -413,7 +415,7 @@ class rg_space(point_space): return True - def get_codomain(self, coname=None, cozerocenter=None, **kwargs): + def get_codomain(self, cozerocenter=None, **kwargs): """ Generates a compatible codomain to which transformations are reasonable, i.e.\ either a shifted grid or a Fourier conjugate @@ -466,16 +468,7 @@ class rg_space(point_space): datamodel = self.datamodel complexity = {0: 1, 1: 0, 2: 2}[self.paradict['complexity']] - - if coname is None: - harmonic = bool(not self.harmonic) - elif coname[0] == 'f': - harmonic = True - elif coname[0] == 'i': - harmonic = False - else: - raise ValueError(about._errors.cstring( - "ERROR: Unknown coname keyword")) + harmonic = bool(not self.harmonic) new_space = rg_space(num, zerocenter=cozerocenter, @@ -869,18 +862,14 @@ class rg_space(point_space): codomain = self.get_codomain() # Check if the given codomain is suitable for the transformation - if (not isinstance(codomain, rg_space)) or \ - (not self.check_codomain(codomain)): + if not self.check_codomain(codomain): raise ValueError(about._errors.cstring( "ERROR: unsupported codomain.")) + if codomain.harmonic: # correct for forward fft x = self.calc_weight(x, power=1) -# ## correct for inverse fft -# x = self.calc_weight(x, power=1) -# x *= self.get_dim(split=False) - # Perform the transformation Tx = self.fft_machine.transform(val=x, domain=self, codomain=codomain, **kwargs)