Commit 6ed60511 authored by Theo Steininger's avatar Theo Steininger
Browse files

Merge branch 'master' into plotting

parents 6dc74c68 54f203dd
Pipeline #12261 passed with stage
in 5 minutes and 23 seconds
...@@ -101,7 +101,7 @@ Starting with a fresh Ubuntu installation move to a folder like ...@@ -101,7 +101,7 @@ Starting with a fresh Ubuntu installation move to a folder like
git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git
cd pyHealpix cd pyHealpix
autoreconf -i && ./configure && sudo make install autoreconf -i && ./configure && make -j4 && sudo make install
cd .. cd ..
- Finally, NIFTy: - Finally, NIFTy:
...@@ -135,7 +135,7 @@ For pyHealpix, use: ...@@ -135,7 +135,7 @@ For pyHealpix, use:
git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git
cd pyHealpix cd pyHealpix
autoreconf -i && ./configure --prefix=$HOME/.local && make install autoreconf -i && ./configure --prefix=$HOME/.local && make -j4 && make install
cd .. cd ..
### Installation on OS X 10.11 ### Installation on OS X 10.11
...@@ -156,7 +156,7 @@ may cause trouble. ...@@ -156,7 +156,7 @@ may cause trouble.
git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git git clone https://gitlab.mpcdf.mpg.de/ift/pyHealpix.git
cd pyHealpix cd pyHealpix
autoreconf -i && ./configure && sudo make install autoreconf -i && ./configure --prefix=`python-config --prefix` && make -j4 && sudo make install
cd .. cd ..
- Install NIFTy: - Install NIFTy:
......
...@@ -107,7 +107,7 @@ if __name__ == "__main__": ...@@ -107,7 +107,7 @@ if __name__ == "__main__":
# callback=distance_measure, # callback=distance_measure,
# max_history_length=3) # max_history_length=3)
m0 = Field(s_space, val=1) m0 = Field(s_space, val=1.)
energy = WienerFilterEnergy(position=m0, D=D, j=j) energy = WienerFilterEnergy(position=m0, D=D, j=j)
......
...@@ -17,13 +17,14 @@ ...@@ -17,13 +17,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import abc import abc
from nifty.nifty_meta import NiftyMeta
from keepers import Loggable,\ from keepers import Loggable,\
Versionable Versionable
class DomainObject(Versionable, Loggable, object): class DomainObject(Versionable, Loggable, object):
__metaclass__ = abc.ABCMeta __metaclass__ = NiftyMeta
def __init__(self): def __init__(self):
# _global_id is used in the Versioning module from keepers # _global_id is used in the Versioning module from keepers
...@@ -40,6 +41,20 @@ class DomainObject(Versionable, Loggable, object): ...@@ -40,6 +41,20 @@ class DomainObject(Versionable, Loggable, object):
return result_hash return result_hash
def __eq__(self, x): 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)): if isinstance(x, type(self)):
for key in vars(self).keys(): for key in vars(self).keys():
item1 = vars(self)[key] item1 = vars(self)[key]
...@@ -57,23 +72,137 @@ class DomainObject(Versionable, Loggable, object): ...@@ -57,23 +72,137 @@ class DomainObject(Versionable, Loggable, object):
@abc.abstractproperty @abc.abstractproperty
def shape(self): 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( raise NotImplementedError(
"There is no generic shape for DomainObject.") "There is no generic shape for DomainObject.")
@abc.abstractproperty @abc.abstractproperty
def dim(self): 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( raise NotImplementedError(
"There is no generic dim for DomainObject.") "There is no generic dim for DomainObject.")
@abc.abstractmethod @abc.abstractmethod
def weight(self, x, power=1, axes=None, inplace=False): 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( raise NotImplementedError(
"There is no generic weight-method for DomainObject.") "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 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 return x
# ---Serialization--- # ---Serialization---
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from nifty.nifty_meta import NiftyMeta
from keepers import Loggable from keepers import Loggable
class Energy(Loggable, object): class Energy(Loggable, object):
__metaclass__ = NiftyMeta
def __init__(self, position): def __init__(self, position):
self._cache = {} self._cache = {}
try: try:
......
...@@ -167,8 +167,8 @@ class Field(Loggable, Versionable, object): ...@@ -167,8 +167,8 @@ class Field(Loggable, Versionable, object):
# ---Powerspectral methods--- # ---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,
real_signal=True): decompose_power=False):
# check if all spaces in `self.domain` are either harmonic or # check if all spaces in `self.domain` are either harmonic or
# power_space instances # power_space instances
for sp in self.domain: for sp in self.domain:
...@@ -210,18 +210,18 @@ class Field(Loggable, Versionable, object): ...@@ -210,18 +210,18 @@ class Field(Loggable, Versionable, object):
self.val.get_axes_local_distribution_strategy( self.val.get_axes_local_distribution_strategy(
self.domain_axes[space_index]) self.domain_axes[space_index])
harmonic_domain = self.domain[space_index] harmonic_partner = self.domain[space_index]
power_domain = PowerSpace(harmonic_domain=harmonic_domain, power_domain = PowerSpace(harmonic_partner=harmonic_partner,
distribution_strategy=distribution_strategy, distribution_strategy=distribution_strategy,
log=log, nbin=nbin, binbounds=binbounds) logarithmic=logarithmic, nbin=nbin, binbounds=binbounds)
# extract pindex and rho from power_domain # extract pindex and rho from power_domain
pindex = power_domain.pindex pindex = power_domain.pindex
rho = power_domain.rho rho = power_domain.rho
if real_signal: if decompose_power:
hermitian_part, anti_hermitian_part = \ hermitian_part, anti_hermitian_part = \
harmonic_domain.hermitian_decomposition( harmonic_partner.hermitian_decomposition(
self.val, self.val,
axes=self.domain_axes[space_index]) axes=self.domain_axes[space_index])
...@@ -245,7 +245,7 @@ class Field(Loggable, Versionable, object): ...@@ -245,7 +245,7 @@ class Field(Loggable, Versionable, object):
result_domain = list(self.domain) result_domain = list(self.domain)
result_domain[space_index] = power_domain result_domain[space_index] = power_domain
if real_signal: if decompose_power:
result_dtype = np.complex result_dtype = np.complex
else: else:
result_dtype = np.float result_dtype = np.float
...@@ -303,8 +303,8 @@ class Field(Loggable, Versionable, object): ...@@ -303,8 +303,8 @@ class Field(Loggable, Versionable, object):
return result_obj return result_obj
def power_synthesize(self, spaces=None, real_power=True, real_signal=True, def power_synthesize(self, spaces=None, real_power=True,
mean=None, std=None): real_signal=False, mean=None, std=None):
# check if the `spaces` input is valid # check if the `spaces` input is valid
spaces = utilities.cast_axis_to_tuple(spaces, len(self.domain)) spaces = utilities.cast_axis_to_tuple(spaces, len(self.domain))
...@@ -322,8 +322,8 @@ class Field(Loggable, Versionable, object): ...@@ -322,8 +322,8 @@ class Field(Loggable, Versionable, object):
result_domain = list(self.domain) result_domain = list(self.domain)
for power_space_index in spaces: for power_space_index in spaces:
power_space = self.domain[power_space_index] power_space = self.domain[power_space_index]
harmonic_domain = power_space.harmonic_domain harmonic_partner = power_space.harmonic_partner
result_domain[power_space_index] = harmonic_domain result_domain[power_space_index] = harmonic_partner
# create random samples: one or two, depending on whether the # create random samples: one or two, depending on whether the
# power spectrum is real or complex # power spectrum is real or complex
...@@ -365,8 +365,8 @@ class Field(Loggable, Versionable, object): ...@@ -365,8 +365,8 @@ class Field(Loggable, Versionable, object):
if real_signal: if real_signal:
for power_space_index in spaces: for power_space_index in spaces:
harmonic_domain = result_domain[power_space_index] harmonic_partner = result_domain[power_space_index]
result_val_list = [harmonic_domain.hermitian_decomposition( result_val_list = [harmonic_partner.hermitian_decomposition(
result_val, result_val,
axes=result.domain_axes[power_space_index], axes=result.domain_axes[power_space_index],
preserve_gaussian_variance=True)[0] preserve_gaussian_variance=True)[0]
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import abc import abc
from nifty.nifty_meta import NiftyMeta
import numpy as np import numpy as np
...@@ -26,7 +27,7 @@ from .line_searching import LineSearchStrongWolfe ...@@ -26,7 +27,7 @@ from .line_searching import LineSearchStrongWolfe
class QuasiNewtonMinimizer(Loggable, object): class QuasiNewtonMinimizer(Loggable, object):
__metaclass__ = abc.ABCMeta __metaclass__ = NiftyMeta
def __init__(self, line_searcher=LineSearchStrongWolfe(), callback=None, def __init__(self, line_searcher=LineSearchStrongWolfe(), callback=None,
convergence_tolerance=1E-4, convergence_level=3, convergence_tolerance=1E-4, convergence_level=3,
......
# -*- coding: utf-8 -*-
import abc
class DocStringInheritor(type):
"""
A variation on
http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
by Paul McGuire
"""
def __new__(meta, name, bases, clsdict):
if not('__doc__' in clsdict and clsdict['__doc__']):
for mro_cls in (mro_cls for base in bases
for mro_cls in base.mro()):
doc = mro_cls.__doc__
if doc:
clsdict['__doc__'] = doc
break
for attr, attribute in clsdict.items():
if not attribute.__doc__:
for mro_cls in (mro_cls for base in bases
for mro_cls in base.mro()
if hasattr(mro_cls, attr)):
doc = getattr(getattr(mro_cls, attr), '__doc__')
if doc:
if isinstance(attribute, property):
clsdict[attr] = property(attribute.fget,
attribute.fset,
attribute.fdel,
doc)
else:
attribute.__doc__ = doc
break
return super(DocStringInheritor, meta).__new__(meta, name,
bases, clsdict)
class NiftyMeta(DocStringInheritor, abc.ABCMeta):
pass
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
import numpy as np import numpy as np
from itertools import product from itertools import product
def get_slice_list(shape, axes): def get_slice_list(shape, axes):
""" """
Helper function which generates slice list(s) to traverse over all Helper function which generates slice list(s) to traverse over all
...@@ -65,8 +66,7 @@ def get_slice_list(shape, axes): ...@@ -65,8 +66,7 @@ def get_slice_list(shape, axes):
return return
def cast_axis_to_tuple(axis, length=None):
def cast_axis_to_tuple(axis, length):
if axis is None: if axis is None:
return None return None
try: try:
...@@ -78,16 +78,17 @@ def cast_axis_to_tuple(axis, length): ...@@ -78,16 +78,17 @@ def cast_axis_to_tuple(axis, length):
raise TypeError( raise TypeError(
"Could not convert axis-input to tuple of ints") "Could not convert axis-input to tuple of ints")
# shift negative indices to positive ones if length is not None:
axis = tuple(item if (item >= 0) else (item + length) for item in axis) # shift negative indices to positive ones
axis = tuple(item if (item >= 0) else (item + length) for item in axis)
# Deactivated this, in order to allow for the ComposedOperator # Deactivated this, in order to allow for the ComposedOperator
# remove duplicate entries # remove duplicate entries
# axis = tuple(set(axis)) # axis = tuple(set(axis))
# assert that all entries are elements in [0, length] # assert that all entries are elements in [0, length]
for elem in axis: for elem in axis:
assert (0 <= elem < length) assert (0 <= elem < length)
return axis return axis
......
...@@ -21,7 +21,9 @@ from nifty.operators.linear_operator import LinearOperator ...@@ -21,7 +21,9 @@ from nifty.operators.linear_operator import LinearOperator
class ComposedOperator(LinearOperator): class ComposedOperator(LinearOperator):
# ---Overwritten properties and methods--- # ---Overwritten properties and methods---
def __init__(self, operators): def __init__(self, operators, default_spaces=None):
super(ComposedOperator, self).__init__(default_spaces)
self._operator_store = () self._operator_store = ()
for op in operators: for op in operators:
if not isinstance(op, LinearOperator): if not isinstance(op, LinearOperator):
...@@ -54,10 +56,6 @@ class ComposedOperator(LinearOperator): ...@@ -54,10 +56,6 @@ class ComposedOperator(LinearOperator):
self._target += op.target self._target += op.target
return self._target return self._target
@property
def implemented(self):
return True
@property @property
def unitary(self): def unitary(self):
return False return False
......
...@@ -30,12 +30,11 @@ class DiagonalOperator(EndomorphicOperator): ...@@ -30,12 +30,11 @@ class DiagonalOperator(EndomorphicOperator):
# ---Overwritten properties and methods--- # ---Overwritten properties and methods---
def __init__(self, domain=(), implemented=True, def __init__(self, domain=(), diagonal=None, bare=False, copy=True,
diagonal=None, bare=False, copy=True, distribution_strategy=None, default_spaces=None):
distribution_strategy=None): super(DiagonalOperator, self).__init__(default_spaces)
self._domain = self._parse_domain(domain)
self._implemented = bool(implemented) self._domain = self._parse_domain(domain)
if distribution_strategy is None: if distribution_strategy is None:
if isinstance(diagonal, distributed_data_object): if isinstance(diagonal, distributed_data_object):
...@@ -100,10 +99,6 @@ class DiagonalOperator(EndomorphicOperator): ...@@ -100,10 +99,6 @@ class DiagonalOperator(EndomorphicOperator):
def domain(self): def domain(self):
return self._domain return self._domain
@property
def implemented(self):
return self._implemented
@property @property
def self_adjoint(self): def self_adjoint(self):
if self._self_adjoint is None: if self._self_adjoint is None:
...@@ -144,16 +139,12 @@ class DiagonalOperator(EndomorphicOperator): ...@@ -144,16 +139,12 @@ class DiagonalOperator(EndomorphicOperator):
distribution_strategy=self.distribution_strategy, distribution_strategy=self.distribution_strategy,
copy=copy) copy=copy)
# weight if the given values were `bare` and `implemented` is True # weight if the given values were `bare` is True
# do inverse weightening if the other way around # do inverse weightening if the other way around
if bare and self.implemented: if bare:
# If `copy` is True, we won't change external data by weightening # If `copy` is True, we won't change external data by weightening
# Otherwise, inplace weightening would change the external field # Otherwise, inplace weightening would change the external field
f.weight(inplace=copy) f.weight(inplace=copy)
elif not bare and not self.implemented:
# If `copy` is True, we won't change external data by weightening
# Otherwise, inplace weightening would change the external field
f.weight(inplace=copy, power=-1)
# Reset the self_adjoint property: # Reset the self_adjoint property:
self._self_adjoint = None self._self_adjoint = None
......
...@@ -34,13 +34,72 @@ from transformations import RGRGTransformation,\ ...@@ -34,13 +34,72 @@ from transformations import RGRGTransformation,\
class FFTOperator(LinearOperator): class FFTOperator(LinearOperator):