Commit 49910e53 authored by Pumpe, Daniel (dpumpe)'s avatar Pumpe, Daniel (dpumpe)
Browse files

Docstrings for Operators

parents 8426e638 48e001f9
......@@ -41,6 +41,20 @@ class DomainObject(Versionable, Loggable, object):
return result_hash
def __eq__(self, x):
""" Checks if two domain_objects are equal.
Parameters
----------
x: domain_object
The domain_object `self` is compared to.
Returns
-------
bool
True if `self` and x describe the same manifold.
"""
if isinstance(x, type(self)):
for key in vars(self).keys():
item1 = vars(self)[key]
......@@ -58,23 +72,137 @@ class DomainObject(Versionable, Loggable, object):
@abc.abstractproperty
def shape(self):
""" Returns the shape of the underlying array-like object.
Returns
-------
tuple of ints
The shape of the underlying array-like object.
Raises
------
NotImplementedError
If called for this abstract class.
"""
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
manifold has.
Raises
------
NotImplementedError
If called for this abstract class.
"""
raise NotImplementedError(
"There is no generic dim for DomainObject.")
@abc.abstractmethod
def weight(self, x, power=1, axes=None, inplace=False):
""" Weights the field on this domain with the space's 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 array's indices correspond to this domain.
Parameters
----------
x : distributed_data_object
The fields data array.
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
-------
distributed_data_object
A weighted version of x, with volume-weights raised to the
given power.
Raises
------
NotImplementedError
If called for this abstract class.
"""
raise NotImplementedError(
"There is no generic weight-method for DomainObject.")
def pre_cast(self, x, axes=None):
def pre_cast(self, x, axes):
""" Casts input for Field.val before Field performs the cast.
Parameters
----------
x : {array-like, castable}
an array-like object or anything that can be cast to arrays.
axes : tuple of ints
Specifies the axes of x which correspond to this domain.
Returns
-------
{array-like, castable}
Processed input where casting that needs Space-specific knowledge
(for example location of pixels on the manifold) was performed.
See Also
--------
post_cast
Notes
-----
Usually returns x, except if a power spectrum is given to a
PowerSpace, where this spectrum is evaluated at the power indices.
"""
return x
def post_cast(self, x, axes=None):
def post_cast(self, x, axes):
""" Performs casting operations that are done after Field's cast.
Parameters
----------
x : {array-like, castable}
an array-like object or anything that can be cast to arrays.
axes : tuple of ints
Specifies the axes of x which correspond to this domain.
See Also
--------
pre_cast
Returns
-------
distributed_data_object
Processed input where casting that needs Space-specific knowledge
(for example location of pixels on the manifold) was performed.
"""
return x
# ---Serialization---
......
......@@ -39,10 +39,6 @@ class Energy(Loggable, object):
def position(self):
return self._position
@position.setter
def position(self, position):
self._position = position
@property
def value(self):
raise NotImplementedError
......
......@@ -167,7 +167,7 @@ class Field(Loggable, Versionable, object):
# ---Powerspectral methods---
def power_analyze(self, spaces=None, log=False, nbin=None, binbounds=None,
def power_analyze(self, spaces=None, logarithmic=False, nbin=None, binbounds=None,
decompose_power=False):
# check if all spaces in `self.domain` are either harmonic or
# power_space instances
......@@ -210,10 +210,10 @@ class Field(Loggable, Versionable, object):
self.val.get_axes_local_distribution_strategy(
self.domain_axes[space_index])
harmonic_domain = self.domain[space_index]
power_domain = PowerSpace(harmonic_domain=harmonic_domain,
harmonic_partner = self.domain[space_index]
power_domain = PowerSpace(harmonic_partner=harmonic_partner,
distribution_strategy=distribution_strategy,
log=log, nbin=nbin, binbounds=binbounds)
logarithmic=logarithmic, nbin=nbin, binbounds=binbounds)
# extract pindex and rho from power_domain
pindex = power_domain.pindex
......@@ -221,7 +221,7 @@ class Field(Loggable, Versionable, object):
if decompose_power:
hermitian_part, anti_hermitian_part = \
harmonic_domain.hermitian_decomposition(
harmonic_partner.hermitian_decomposition(
self.val,
axes=self.domain_axes[space_index])
......@@ -322,8 +322,8 @@ class Field(Loggable, Versionable, object):
result_domain = list(self.domain)
for power_space_index in spaces:
power_space = self.domain[power_space_index]
harmonic_domain = power_space.harmonic_domain
result_domain[power_space_index] = harmonic_domain
harmonic_partner = power_space.harmonic_partner
result_domain[power_space_index] = harmonic_partner
# create random samples: one or two, depending on whether the
# power spectrum is real or complex
......@@ -365,8 +365,8 @@ class Field(Loggable, Versionable, object):
if decompose_power:
for power_space_index in spaces:
harmonic_domain = result_domain[power_space_index]
result_val_list = [harmonic_domain.hermitian_decomposition(
harmonic_partner = result_domain[power_space_index]
result_val_list = [harmonic_partner.hermitian_decomposition(
result_val,
axes=result.domain_axes[power_space_index],
preserve_gaussian_variance=True)[0]
......
......@@ -18,7 +18,7 @@
from line_searching import *
from conjugate_gradient import ConjugateGradient
from quasi_newton_minimizer import QuasiNewtonMinimizer
from descent_minimizer import DescentMinimizer
from steepest_descent import SteepestDescent
from vl_bfgs import VL_BFGS
from relaxed_newton import RelaxedNewton
......@@ -91,7 +91,7 @@ class ConjugateGradient(Loggable, object):
Latest convergence level indicating whether the minimization
has converged or not.
"""
"""
r = b - A(x0)
d = self.preconditioner(r)
previous_gamma = r.dot(d)
......
......@@ -26,7 +26,7 @@ from keepers import Loggable
from .line_searching import LineSearchStrongWolfe
class QuasiNewtonMinimizer(Loggable, object):
class DescentMinimizer(Loggable, object):
__metaclass__ = NiftyMeta
def __init__(self, line_searcher=LineSearchStrongWolfe(), callback=None,
......
......@@ -148,8 +148,9 @@ class LineSearchStrongWolfe(LineSearch):
# extract the full energy from the line_energy
energy_star = energy_star.energy
return alpha_star, phi_star, energy_star
direction_length = pk.norm()
step_length = alpha_star * direction_length
return step_length, phi_star, energy_star
def _zoom(self, alpha_lo, alpha_hi, phi_0, phiprime_0,
phi_lo, phiprime_lo, phi_hi, c1, c2):
......
......@@ -16,11 +16,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .quasi_newton_minimizer import QuasiNewtonMinimizer
from .descent_minimizer import DescentMinimizer
from .line_searching import LineSearchStrongWolfe
class RelaxedNewton(QuasiNewtonMinimizer):
class RelaxedNewton(DescentMinimizer):
def __init__(self, line_searcher=LineSearchStrongWolfe(), callback=None,
convergence_tolerance=1E-4, convergence_level=3,
iteration_limit=None):
......@@ -38,8 +38,3 @@ class RelaxedNewton(QuasiNewtonMinimizer):
curvature = energy.curvature
descend_direction = curvature.inverse_times(gradient)
return descend_direction * -1
#norm = descend_direction.norm()
# if norm != 1:
# return descend_direction / -norm
# else:
# return descend_direction * -1
......@@ -16,10 +16,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .quasi_newton_minimizer import QuasiNewtonMinimizer
from .descent_minimizer import DescentMinimizer
class SteepestDescent(QuasiNewtonMinimizer):
class SteepestDescent(DescentMinimizer):
def _get_descend_direction(self, energy):
descend_direction = energy.gradient
norm = descend_direction.norm()
......
......@@ -18,11 +18,11 @@
import numpy as np
from .quasi_newton_minimizer import QuasiNewtonMinimizer
from .descent_minimizer import DescentMinimizer
from .line_searching import LineSearchStrongWolfe
class VL_BFGS(QuasiNewtonMinimizer):
class VL_BFGS(DescentMinimizer):
def __init__(self, line_searcher=LineSearchStrongWolfe(), callback=None,
convergence_tolerance=1E-4, convergence_level=3,
iteration_limit=None, max_history_length=10):
......
......@@ -21,6 +21,7 @@ import numpy as np
from d2o import distributed_data_object,\
STRATEGIES as DISTRIBUTION_STRATEGIES
from nifty.basic_arithmetics import log as nifty_log
from nifty.config import nifty_configuration as gc
from nifty.field import Field
from nifty.operators.endomorphic_operator import EndomorphicOperator
......@@ -159,7 +160,7 @@ class DiagonalOperator(EndomorphicOperator):
See Also
--------
"""
return 1/self.diagonal(bare=bare, copy=False)
return 1./self.diagonal(bare=bare, copy=False)
def trace(self, bare=False):
""" Returns the trace the operator.
......@@ -195,7 +196,7 @@ class DiagonalOperator(EndomorphicOperator):
See Also
--------
"""
return self.inverse_diagonal(bare=bare, copy=False).sum()
return self.inverse_diagonal(bare=bare).sum()
def trace_log(self):
""" Returns the trave-log of the operator.
......@@ -210,7 +211,7 @@ class DiagonalOperator(EndomorphicOperator):
See Also
--------
"""
log_diagonal = self.diagonal(copy=False).apply_scalar_function(np.log)
log_diagonal = nifty_log(self.diagonal(copy=False))
return log_diagonal.sum()
def determinant(self):
......
......@@ -43,8 +43,15 @@ 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``).
Raises
------
ValueError
If input `nlat` or `nlon` is invalid.
ImportError
If the pyHealpix module is not available
See Also
--------
......@@ -65,28 +72,6 @@ class GLSpace(Space):
# ---Overwritten properties and methods---
def __init__(self, nlat, nlon=None):
"""
Sets the attributes for a gl_space class instance.
Parameters
----------
nlat : int
Number of latitudinal bins, or rings.
nlon : int, *optional*
Number of longitudinal bins (default: ``2*nlat - 1``).
Returns
-------
None
Raises
------
ValueError
If input `nlat` is invalid.
ImportError
If the pyHealpix module is not available
"""
if 'pyHealpix' not in gdi:
raise ImportError(
"The module pyHealpix is needed but not available.")
......@@ -121,7 +106,7 @@ class GLSpace(Space):
def weight(self, x, power=1, axes=None, inplace=False):
nlon = self.nlon
nlat = self.nlat
vol = pyHealpix.GL_weights(nlat, nlon) ** power
vol = pyHealpix.GL_weights(nlat, nlon) ** np.float(power)
weight = np.array(list(itertools.chain.from_iterable(
itertools.repeat(x, nlon) for x in vol)))
......@@ -150,10 +135,17 @@ class GLSpace(Space):
@property
def nlat(self):
""" Number of latitudinal bins (or rings) that are used for this
pixelization.
"""
return self._nlat
@property
def nlon(self):
""" Number of longditudinal bins that are used for this pixelization.
"""
return self._nlon
def _parse_nlat(self, nlat):
......
......@@ -39,6 +39,11 @@ class HPSpace(Space):
Resolution parameter for the HEALPix discretization, resulting in
``12*nside**2`` pixels. Must be positive.
Raises
------
ValueError
If given `nside` < 1.
See Also
--------
gl_space : A class for the Gauss-Legendre discretization of the
......@@ -59,26 +64,6 @@ class HPSpace(Space):
# ---Overwritten properties and methods---
def __init__(self, nside):
"""
Sets the attributes for a HPSpace class instance.
Parameters
----------
nside : int
Resolution parameter for the HEALPix discretization, resulting
in ``12*nside**2`` pixels. Must be positive.
Returns
-------
None
Raises
------
ValueError
If input `nside` is invalid.
"""
super(HPSpace, self).__init__()
self._nside = self._parse_nside(nside)
......@@ -105,7 +90,8 @@ class HPSpace(Space):
return self.__class__(nside=self.nside)
def weight(self, x, power=1, axes=None, inplace=False):
weight = ((4 * np.pi) / (12 * self.nside**2))**power
weight = ((4 * np.pi) / (12 * self.nside**2)) ** np.float(power)
if inplace:
x *= weight
......@@ -125,6 +111,9 @@ class HPSpace(Space):
@property
def nside(self):
""" Returns the nside of the corresponding HEALPix pixelization.
The total number of pixels is 12*nside**2
"""
return self._nside
def _parse_nside(self, nside):
......
......@@ -43,17 +43,21 @@ 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 [#]_.
Raises
------
ValueError
If given lmax is negative.
Notes
-----
This implementation implicitly sets the mmax parameter to lmax.
References
----------
.. [#] K.M. Gorski et al., 2005, "HEALPix: A Framework for
......@@ -65,21 +69,6 @@ class LMSpace(Space):
"""
def __init__(self, lmax):
"""
Sets the attributes for an lm_space class instance.
Parameters
----------
lmax : int
Maximum :math:`\ell`-value up to which the spherical harmonics
coefficients are to be used.
Returns
-------
None.
"""
super(LMSpace, self).__init__()
self._lmax = self._parse_lmax(lmax)
......@@ -144,16 +133,32 @@ 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---
@property
def lmax(self):
""" Returns the maximal :math:`l` value of any spherical harmonics
:math:`Y_{lm}` that is represented in this Space.
"""
return self._lmax
@property
def mmax(self):
""" Returns the maximal :math:`m` value of any spherical harmonic
:math:`Y_{lm}` that is represented in this Space. As :math:`m` goes
from :math:`-l` to :math:`l` for every :math:`l` this just returns the
same as lmax.
See Also
--------
lmax : Returns the maximal :math:`l`-value of the spherical harmonics
being used.
"""
return self._lmax
def _parse_lmax(self, lmax):
......
......@@ -24,15 +24,17 @@ class _PowerIndexFactory(object):
self.power_indices_storage = {}
def get_power_index(self, domain, distribution_strategy,
log=False, nbin=None, binbounds=None):
logarithmic=False, nbin=None, binbounds=None):
key = (domain, distribution_strategy)
if key not in self.power_indices_storage:
self.power_indices_storage[key] = \
PowerIndices(domain, distribution_strategy,
log=log, nbin=nbin, binbounds=binbounds)
logarithmic=logarithmic,
nbin=nbin,
binbounds=binbounds)
power_indices = self.power_indices_storage[key]
power_index = power_indices.get_index_dict(log=log,
power_index = power_indices.get_index_dict(logarithmic=logarithmic,
nbin=nbin,
binbounds=binbounds)
return power_index
......
......@@ -26,7 +26,7 @@ from d2o.config import configuration as d2o_config
class PowerIndices(object):
def __init__(self, domain, distribution_strategy,
log=False, nbin=None, binbounds=None):
logarithmic=False, nbin=None, binbounds=None):
"""
Returns an instance of the PowerIndices class. Given the shape and
the density of a underlying rectangular grid it provides the user
......@@ -42,7 +42,7 @@ class PowerIndices(object):
dgrid : tuple, list, ndarray
Array-like object which specifies the step-width of the
underlying grid
log : bool *optional*
logarithmic : bool *optional*
Flag specifying if the binning of the default indices is
performed on logarithmic scale.
nbin : integer *optional*
......@@ -59,7 +59,7 @@ class PowerIndices(object):
# Initialize the dictionary which stores all individual index-dicts
self.global_dict = {}
# Set self.default_parameters
self.set_default(config_dict={'log': log,
self.set_default(config_dict={'logarithmic': logarithmic,
'nbin': nbin,
'binbounds': binbounds})
......@@ -88,7 +88,7 @@ class PowerIndices(object):
Parameters
----------
log : bool
logarithmic : bool
Flag specifying if the binning is performed on logarithmic
scale.
nbin : integer
......@@ -113,24 +113,25 @@ class PowerIndices(object):
return self._cast_config_helper(**temp_config_dict)
else:
defaults = self.default_parameters