diff --git a/nifty/operators/composed_operator/composed_operator.py b/nifty/operators/composed_operator/composed_operator.py index b2e3ecd94a8a0d42e2e04399132e5ac848b35686..d6b833e36c52015448c3c0a676219b279f000c38 100644 --- a/nifty/operators/composed_operator/composed_operator.py +++ b/nifty/operators/composed_operator/composed_operator.py @@ -28,6 +28,10 @@ class ComposedOperator(LinearOperator): ---------- operators : tuple of NIFTy Operators The tuple of LinearOperators. + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) + Attributes ---------- @@ -35,6 +39,8 @@ class ComposedOperator(LinearOperator): The NIFTy.space in which the operator is defined. target : tuple of DomainObjects, i.e. Spaces and FieldTypes The NIFTy.space in which the outcome of the operator lives + unitary : boolean + Indicates whether the Operator is unitary or not. Raises ------ diff --git a/nifty/operators/diagonal_operator/diagonal_operator.py b/nifty/operators/diagonal_operator/diagonal_operator.py index dec42da42dc9a788b0d64912d9a5224474f346a5..467330c7a261182c7c836e30e5f6845112db7701 100644 --- a/nifty/operators/diagonal_operator/diagonal_operator.py +++ b/nifty/operators/diagonal_operator/diagonal_operator.py @@ -50,9 +50,21 @@ class DiagonalOperator(EndomorphicOperator): setting the prober distribution_strategy of the diagonal (default : None). In case diagonal is d2o-object or Field, their distribution_strategy is used as a fallback. + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. + self_adjoint : boolean + Indicates whether the operator is self_adjoint or not. distribution_strategy : string Defines the distribution_strategy of the distributed_data_object in which the diagonal entries are stored in. diff --git a/nifty/operators/endomorphic_operator/endomorphic_operator.py b/nifty/operators/endomorphic_operator/endomorphic_operator.py index 1340708be765601e6dfcffa274f523292b330e39..cde405f480fe4ed649323bfe104c5a21c2bf3fc2 100644 --- a/nifty/operators/endomorphic_operator/endomorphic_operator.py +++ b/nifty/operators/endomorphic_operator/endomorphic_operator.py @@ -30,7 +30,9 @@ class EndomorphicOperator(LinearOperator): Parameters ---------- - #TODO: Copy Parameters from LinearOperator + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- @@ -39,6 +41,8 @@ class EndomorphicOperator(LinearOperator): target : tuple of DomainObjects, i.e. Spaces and FieldTypes The domain in which the outcome of the operator lives. As the Operator is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. self_adjoint : boolean Indicates whether the operator is self_adjoint or not. diff --git a/nifty/operators/projection_operator/projection_operator.py b/nifty/operators/projection_operator/projection_operator.py index 9a51f363634dc7bf0c1112b3ae60ee0b74b785ce..3298b556adee8acb483609a39bf5ff706e4e4c71 100644 --- a/nifty/operators/projection_operator/projection_operator.py +++ b/nifty/operators/projection_operator/projection_operator.py @@ -33,9 +33,21 @@ class ProjectionOperator(EndomorphicOperator): ---------- projection_field : Field Field on which the operator projects + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. + self_adjoint : boolean + Indicates whether the operator is self_adjoint or not. Raises ------ diff --git a/nifty/operators/propagator_operator/harmonic_propagator_operator.py b/nifty/operators/propagator_operator/harmonic_propagator_operator.py index 3ad6501fe217cd4610ef7e1b8787aeb7f3eb4b0e..908e2e372f992dbd0c7afc7ce8fdbc0c62c94833 100644 --- a/nifty/operators/propagator_operator/harmonic_propagator_operator.py +++ b/nifty/operators/propagator_operator/harmonic_propagator_operator.py @@ -46,9 +46,21 @@ class HarmonicPropagatorOperator(InvertibleOperatorMixin, EndomorphicOperator): (default:ConjugateGradient) preconditioner : Field numerical preconditioner to speed up convergence + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. + self_adjoint : boolean + Indicates whether the operator is self_adjoint or not. Raises ------ diff --git a/nifty/operators/propagator_operator/propagator_operator.py b/nifty/operators/propagator_operator/propagator_operator.py index cb70e7b7cb9fbaa179517b8fe61abe0ac44ab1af..6bdf5c163023f994fba9a9e3924d1527ee4b22ea 100644 --- a/nifty/operators/propagator_operator/propagator_operator.py +++ b/nifty/operators/propagator_operator/propagator_operator.py @@ -44,9 +44,21 @@ class PropagatorOperator(InvertibleOperatorMixin, EndomorphicOperator): (default:ConjugateGradient) preconditioner : Field numerical preconditioner to speed up convergence + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. + self_adjoint : boolean + Indicates whether the operator is self_adjoint or not. Raises ------ diff --git a/nifty/operators/response_operator/response_operator.py b/nifty/operators/response_operator/response_operator.py index 3005f69c919503965d361d8034dbeca8969714f6..0e25e4282547524304968319bfad3e08c438e15b 100644 --- a/nifty/operators/response_operator/response_operator.py +++ b/nifty/operators/response_operator/response_operator.py @@ -24,10 +24,19 @@ class ResponseOperator(LinearOperator): Defines the smoothing length of the operator for each space it lives on exposure : list(np.float) Defines the exposure of the operator for each space it lives on - + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. Raises ------ @@ -72,27 +81,22 @@ class ResponseOperator(LinearOperator): shape_target = np.append(shape_target, self._domain[ii].shape) self._target = self._parse_domain(FieldArray(shape_target)) - self._sigma = sigma - self._exposure = exposure - - self._kernel = len(self._domain)*[None] - - for ii in xrange(len(self._kernel)): - self._kernel[ii] = SmoothingOperator(self._domain[ii], - sigma=self._sigma[ii]) - - self._composed_kernel = ComposedOperator(self._kernel) - - self._exposure_op = len(self._domain)*[None] - if len(self._exposure_op) != len(self._kernel): - raise ValueError("Definition of kernel and exposure do not suit " - "each other") - else: - for ii in xrange(len(self._exposure_op)): - self._exposure_op[ii] = DiagonalOperator( - self._domain[ii], - diagonal=self._exposure[ii]) - self._composed_exposure = ComposedOperator(self._exposure_op) + + kernel_smoothing = len(self._domain)*[None] + kernel_exposure = len(self._domain)*[None] + + if len(sigma)!= len(exposure): + raise ValueError("Length of smoothing kernel and length of" + "exposure do not match") + + for ii in xrange(len(kernel_smoothing)): + kernel_smoothing[ii] = SmoothingOperator(self._domain[ii], + sigma=sigma[ii]) + kernel_exposure[ii] = DiagonalOperator(self._domain[ii], + diagonal=exposure[ii]) + + self._composed_kernel = ComposedOperator(kernel_smoothing) + self._composed_exposure = ComposedOperator(kernel_exposure) @property def domain(self): diff --git a/nifty/operators/smoothing_operator/smoothing_operator.py b/nifty/operators/smoothing_operator/smoothing_operator.py index ab071af213f209e07b4c1a93bf8f33f801b40d99..cdc34cfff71f2f811045275c9685d05387decd0e 100644 --- a/nifty/operators/smoothing_operator/smoothing_operator.py +++ b/nifty/operators/smoothing_operator/smoothing_operator.py @@ -34,15 +34,28 @@ class SmoothingOperator(EndomorphicOperator): Parameters ---------- - domain : tuple of DomainObjects, i.e. Spaces and FieldTypes - The Space on which the operator acts + domain : DomainObject, i.e. Space or FieldType + The Space on which the operator acts. The SmoothingOperator + can only live on one space or FieldType sigma : float Sets the length of the Gaussian convolution kernel log_distances : boolean States whether the convolution happens on the logarithmic grid or not. + default_spaces : tuple of ints *optional* + Defines on which space(s) of a given field the Operator acts by + default (default: None) Attributes ---------- + domain : DomainObject, i.e. Space or FieldType + The domain on which the Operator's input Field lives. + target : tuple of DomainObjects, i.e. Spaces and FieldTypes + The domain in which the outcome of the operator lives. As the Operator + is endomorphic this is the same as its domain. + unitary : boolean + Indicates whether the Operator is unitary or not. + self_adjoint : boolean + Indicates whether the operator is self_adjoint or not. sigma : float Sets the length of the Gaussian convolution kernel log_distances : boolean diff --git a/test/test_operators/test_composed_operator.py b/test/test_operators/test_composed_operator.py new file mode 100644 index 0000000000000000000000000000000000000000..22e148f221836036087c93fd8079ff74f5483eca --- /dev/null +++ b/test/test_operators/test_composed_operator.py @@ -0,0 +1,61 @@ +import unittest + +from numpy.testing import assert_equal,\ + assert_allclose,\ + assert_approx_equal + +from nifty import Field,\ + DiagonalOperator,\ + ComposedOperator + +from test.common import generate_spaces + +from itertools import product +from test.common import expand + +class ComposedOperator_Tests(unittest.TestCase): + spaces = generate_spaces() + + @expand(product(spaces, spaces)) + def test_property(self, space1, space2): + rand1 = Field.from_random('normal', domain=space1) + rand2 = Field.from_random('normal', domain=space2) + op1 = DiagonalOperator(space1, diagonal=rand1) + op2 = DiagonalOperator(space2, diagonal=rand2) + op = ComposedOperator((op1, op2)) + if op.domain != (op1.domain[0], op2.domain[0]): + raise TypeError + if op.unitary != False: + raise ValueError + + @expand(product(spaces,spaces)) + def test_times_adjoint_times(self, space1, space2): + diag1 = Field.from_random('normal', domain=space1) + diag2 = Field.from_random('normal', domain=space2) + op1 = DiagonalOperator(space1, diagonal=diag1) + op2 = DiagonalOperator(space2, diagonal=diag2) + + op = ComposedOperator((op1, op2)) + + rand1 = Field.from_random('normal', domain=(space1,space2)) + rand2 = Field.from_random('normal', domain=(space1,space2)) + + tt1 = rand2.dot(op.times(rand1)) + tt2 = rand1.dot(op.adjoint_times(rand2)) + assert_approx_equal(tt1, tt2) + + @expand(product(spaces, spaces)) + def test_times_inverse_times(self, space1, space2): + diag1 = Field.from_random('normal', domain=space1) + diag2 = Field.from_random('normal', domain=space2) + op1 = DiagonalOperator(space1, diagonal=diag1) + op2 = DiagonalOperator(space2, diagonal=diag2) + + op = ComposedOperator((op1, op2)) + + rand1 = Field.from_random('normal', domain=(space1, space2)) + tt1 = op.inverse_times(op.times(rand1)) + + assert_allclose(tt1.val.get_full_data(), + rand1.val.get_full_data()) + diff --git a/test/test_operators/test_response_operator.py b/test/test_operators/test_response_operator.py new file mode 100644 index 0000000000000000000000000000000000000000..c230e0bd6cd562f66b19b23402b9e5bd5fda1f2a --- /dev/null +++ b/test/test_operators/test_response_operator.py @@ -0,0 +1,32 @@ +import unittest + +from numpy.testing import assert_approx_equal + +from nifty import Field,\ + RGSpace,\ + ResponseOperator + +from itertools import product +from test.common import expand + +class ResponseOperator_Tests(unittest.TestCase): + spaces = [RGSpace(100)] + + @expand(product(spaces, [0., 5., 1.], [0., 1., .33] )) + def test_property(self, space, sigma, exposure): + op = ResponseOperator(space, sigma=[sigma], + exposure=[exposure]) + if op.domain[0] != space: + raise TypeError + if op.unitary != False: + raise ValueError + + @expand(product(spaces, [0., 5., 1.], [0., 1., .33] )) + def test_times_adjoint_times(self, space, sigma, exposure): + op = ResponseOperator(space, sigma=[sigma], + exposure=[exposure]) + rand1 = Field.from_random('normal', domain=space) + rand2 = Field.from_random('normal', domain=op.target[0]) + tt1 = rand2.dot(op.times(rand1)) + tt2 = rand1.dot(op.adjoint_times(rand2)) + assert_approx_equal(tt1, tt2) diff --git a/test/test_operators/test_smoothing_operator.py b/test/test_operators/test_smoothing_operator.py index 253603919dfe8708798ecd4cdaa034195070a04c..ec582e6d9c627784f4664dc1d0b08c72bfab3103 100644 --- a/test/test_operators/test_smoothing_operator.py +++ b/test/test_operators/test_smoothing_operator.py @@ -18,13 +18,14 @@ import unittest import numpy as np -from numpy.testing import assert_equal,\ +from numpy.testing import assert_equal, assert_approx_equal,\ assert_allclose -from nifty.config import dependency_injector as di + from nifty import Field,\ RGSpace,\ PowerSpace,\ SmoothingOperator + from itertools import product from test.common import expand @@ -35,8 +36,55 @@ def _get_rtol(tp): else: return 1e-5 +from itertools import product +from test.common import expand + +class SmoothingOperator_Tests(unittest.TestCase): + spaces = [RGSpace(100)] + + @expand(product(spaces, [0., .5, 5.], [True, False])) + def test_property(self, space, sigma, log_distances): + op = SmoothingOperator(space, sigma=sigma, + log_distances=log_distances) + if op.domain[0] != space: + raise TypeError + if op.unitary != False: + raise ValueError + if op.self_adjoint != True: + raise ValueError + if op.sigma != sigma: + raise ValueError + if op.log_distances != log_distances: + raise ValueError + + @expand(product(spaces, [0., .5, 5.], [True, False])) + def test_adjoint_times(self, space, sigma, log_distances): + op = SmoothingOperator(space, sigma=sigma, + log_distances=log_distances) + rand1 = Field.from_random('normal', domain=space) + rand2 = Field.from_random('normal', domain=space) + tt1 = rand1.dot(op.times(rand2)) + tt2 = rand2.dot(op.adjoint_times(rand1)) + assert_approx_equal(tt1, tt2) + + @expand(product(spaces, [0., .5, 5.], [False])) + def test_times(self, space, sigma, log_distances): + op = SmoothingOperator(space, sigma=sigma, + log_distances=log_distances) + rand1 = Field(space, val=0.) + rand1.val[0] = 1. + tt1 = op.times(rand1) + assert_approx_equal(1, tt1.sum()) -class Misc_Tests(unittest.TestCase): + @expand(product(spaces, [0., .5, 5.], [True, False])) + def test_inverse_adjoint_times(self, space, sigma, log_distances): + op = SmoothingOperator(space, sigma=sigma, + log_distances=log_distances) + rand1 = Field.from_random('normal', domain=space) + rand2 = Field.from_random('normal', domain=space) + tt1 = rand1.dot(op.inverse_times(rand2)) + tt2 = rand2.dot(op.inverse_adjoint_times(rand1)) + assert_approx_equal(tt1, tt2) @expand(product([100, 200], [1, 0.4], [0., 1., 3.7], [np.float64, np.complex128]))