From 12c44bb48b0ddd7c70a1c81f4ae5d16f9bb0d424 Mon Sep 17 00:00:00 2001
From: Martin Reinecke <martin@mpa-garching.mpg.de>
Date: Wed, 8 Aug 2018 20:43:26 +0200
Subject: [PATCH] ... and more cleanups

---
 demos/polynomial_fit.py                       |  6 +--
 nifty5/domains/dof_space.py                   |  1 -
 nifty5/domains/domain.py                      |  8 ++--
 nifty5/domains/gl_space.py                    |  2 -
 nifty5/domains/hp_space.py                    |  1 -
 nifty5/domains/lm_space.py                    |  1 -
 nifty5/domains/log_rg_space.py                |  2 -
 nifty5/domains/power_space.py                 |  2 -
 nifty5/domains/rg_space.py                    |  2 -
 nifty5/domains/unstructured_domain.py         |  1 -
 nifty5/library/los_response.py                |  7 +---
 nifty5/minimization/descent_minimizer.py      |  1 -
 nifty5/minimization/energy.py                 |  1 -
 .../minimization/gradient_norm_controller.py  |  1 -
 nifty5/minimization/line_energy.py            |  1 -
 nifty5/minimization/scipy_minimizer.py        |  2 -
 nifty5/operators/block_diagonal_operator.py   |  9 +----
 nifty5/operators/central_zero_padder.py       |  7 +---
 nifty5/operators/chain_operator.py            |  5 ---
 nifty5/operators/diagonal_operator.py         |  7 +---
 nifty5/operators/dof_distributor.py           |  7 +---
 nifty5/operators/domain_distributor.py        |  5 +--
 nifty5/operators/energy_operators.py          |  5 ---
 nifty5/operators/exp_transform.py             |  5 +--
 nifty5/operators/field_zero_padder.py         |  6 +--
 nifty5/operators/harmonic_operators.py        | 28 ++-----------
 nifty5/operators/inversion_enabler.py         |  6 +--
 nifty5/operators/laplace_operator.py          |  6 +--
 nifty5/operators/linear_operator.py           |  2 +-
 nifty5/operators/mask_operator.py             |  5 +--
 nifty5/operators/operator_adapter.py          |  6 +--
 nifty5/operators/qht_operator.py              |  5 +--
 nifty5/operators/relaxed_sum_operator.py      |  5 ---
 nifty5/operators/sampling_enabler.py          |  6 +--
 nifty5/operators/sandwich_operator.py         |  6 +--
 nifty5/operators/scaling_operator.py          |  6 +--
 nifty5/operators/simple_linear_operators.py   | 40 ++++---------------
 nifty5/operators/slope_operator.py            |  5 +--
 nifty5/operators/sum_operator.py              |  5 ---
 nifty5/operators/symmetrizing_operator.py     |  5 +--
 test/test_minimization/test_minimizers.py     |  5 +--
 41 files changed, 36 insertions(+), 200 deletions(-)

diff --git a/demos/polynomial_fit.py b/demos/polynomial_fit.py
index 969e6822a..020c030c4 100644
--- a/demos/polynomial_fit.py
+++ b/demos/polynomial_fit.py
@@ -38,13 +38,13 @@ class PolynomialResponse(ift.LinearOperator):
     """
 
     def __init__(self, domain, sampling_points):
-        super(PolynomialResponse, self).__init__()
         if not (isinstance(domain, ift.UnstructuredDomain)
                 and isinstance(x, np.ndarray)):
             raise TypeError
         self._domain = ift.DomainTuple.make(domain)
         tgt = ift.UnstructuredDomain(sampling_points.shape)
         self._target = ift.DomainTuple.make(tgt)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
         sh = (self.target.size, domain.size)
         self._mat = np.empty(sh)
@@ -62,10 +62,6 @@ class PolynomialResponse(ift.LinearOperator):
             out = self._mat.conj().T.dot(val)
         return ift.from_global_data(self._tgt(mode), out)
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
 
 # Generate some mock data
 N_params = 10
diff --git a/nifty5/domains/dof_space.py b/nifty5/domains/dof_space.py
index 151c45f62..5d42ad197 100644
--- a/nifty5/domains/dof_space.py
+++ b/nifty5/domains/dof_space.py
@@ -40,7 +40,6 @@ class DOFSpace(StructuredDomain):
     _needed_for_hash = ["_dvol"]
 
     def __init__(self, dof_weights):
-        super(DOFSpace, self).__init__()
         self._dvol = tuple(dof_weights)
 
     @property
diff --git a/nifty5/domains/domain.py b/nifty5/domains/domain.py
index fd7c5ed5f..a807595db 100644
--- a/nifty5/domains/domain.py
+++ b/nifty5/domains/domain.py
@@ -25,10 +25,6 @@ from ..utilities import NiftyMetaBase
 class Domain(NiftyMetaBase()):
     """The abstract class repesenting a (structured or unstructured) domain.
     """
-
-    def __init__(self):
-        self._hash = None
-
     def __repr__(self):
         raise NotImplementedError
 
@@ -40,7 +36,9 @@ class Domain(NiftyMetaBase()):
         Only members that are explicitly added to
         :attr:`._needed_for_hash` will be used for hashing.
         """
-        if self._hash is None:
+        try:
+            return self._hash
+        except AttributeError:
             h = 0
             for key in self._needed_for_hash:
                 h ^= hash(vars(self)[key])
diff --git a/nifty5/domains/gl_space.py b/nifty5/domains/gl_space.py
index fff20b0dc..529dea48d 100644
--- a/nifty5/domains/gl_space.py
+++ b/nifty5/domains/gl_space.py
@@ -43,8 +43,6 @@ class GLSpace(StructuredDomain):
     _needed_for_hash = ["_nlat", "_nlon"]
 
     def __init__(self, nlat, nlon=None):
-        super(GLSpace, self).__init__()
-
         self._nlat = int(nlat)
         if self._nlat < 1:
             raise ValueError("nlat must be a positive number.")
diff --git a/nifty5/domains/hp_space.py b/nifty5/domains/hp_space.py
index 9df48287c..a8a99b954 100644
--- a/nifty5/domains/hp_space.py
+++ b/nifty5/domains/hp_space.py
@@ -40,7 +40,6 @@ class HPSpace(StructuredDomain):
     _needed_for_hash = ["_nside"]
 
     def __init__(self, nside):
-        super(HPSpace, self).__init__()
         self._nside = int(nside)
         if self._nside < 1:
             raise ValueError("nside must be >=1.")
diff --git a/nifty5/domains/lm_space.py b/nifty5/domains/lm_space.py
index 54a046849..d40d0c102 100644
--- a/nifty5/domains/lm_space.py
+++ b/nifty5/domains/lm_space.py
@@ -48,7 +48,6 @@ class LMSpace(StructuredDomain):
     _needed_for_hash = ["_lmax", "_mmax"]
 
     def __init__(self, lmax, mmax=None):
-        super(LMSpace, self).__init__()
         self._lmax = np.int(lmax)
         if self._lmax < 0:
             raise ValueError("lmax must be >=0.")
diff --git a/nifty5/domains/log_rg_space.py b/nifty5/domains/log_rg_space.py
index 3f0d7abee..6f3833ec9 100644
--- a/nifty5/domains/log_rg_space.py
+++ b/nifty5/domains/log_rg_space.py
@@ -32,8 +32,6 @@ class LogRGSpace(StructuredDomain):
     _needed_for_hash = ['_shape', '_bindistances', '_t_0', '_harmonic']
 
     def __init__(self, shape, bindistances, t_0, harmonic=False):
-        super(LogRGSpace, self).__init__()
-
         self._harmonic = bool(harmonic)
 
         if np.isscalar(shape):
diff --git a/nifty5/domains/power_space.py b/nifty5/domains/power_space.py
index 75a905c4f..1cd6d9523 100644
--- a/nifty5/domains/power_space.py
+++ b/nifty5/domains/power_space.py
@@ -158,8 +158,6 @@ class PowerSpace(StructuredDomain):
             return PowerSpace.linear_binbounds(nbin, lbound, rbound)
 
     def __init__(self, harmonic_partner, binbounds=None):
-        super(PowerSpace, self).__init__()
-
         if not (isinstance(harmonic_partner, StructuredDomain) and
                 harmonic_partner.harmonic):
             raise ValueError("harmonic_partner must be a harmonic space.")
diff --git a/nifty5/domains/rg_space.py b/nifty5/domains/rg_space.py
index 337511751..14b9c9a10 100644
--- a/nifty5/domains/rg_space.py
+++ b/nifty5/domains/rg_space.py
@@ -51,8 +51,6 @@ class RGSpace(StructuredDomain):
     _needed_for_hash = ["_distances", "_shape", "_harmonic"]
 
     def __init__(self, shape, distances=None, harmonic=False):
-        super(RGSpace, self).__init__()
-
         self._harmonic = bool(harmonic)
         if np.isscalar(shape):
             shape = (shape,)
diff --git a/nifty5/domains/unstructured_domain.py b/nifty5/domains/unstructured_domain.py
index 5c5c6d34d..489b77773 100644
--- a/nifty5/domains/unstructured_domain.py
+++ b/nifty5/domains/unstructured_domain.py
@@ -38,7 +38,6 @@ class UnstructuredDomain(Domain):
     _needed_for_hash = ["_shape"]
 
     def __init__(self, shape):
-        super(UnstructuredDomain, self).__init__()
         try:
             self._shape = tuple([int(i) for i in shape])
         except TypeError:
diff --git a/nifty5/library/los_response.py b/nifty5/library/los_response.py
index f8c112f23..2dd414f1d 100644
--- a/nifty5/library/los_response.py
+++ b/nifty5/library/los_response.py
@@ -134,9 +134,8 @@ class LOSResponse(LinearOperator):
     """
 
     def __init__(self, domain, starts, ends, sigmas_low=None, sigmas_up=None):
-
-        super(LOSResponse, self).__init__()
         self._domain = DomainTuple.make(domain)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
         if ((not isinstance(self.domain[0], RGSpace)) or
                 (len(self._domain) != 1)):
@@ -221,10 +220,6 @@ class LOSResponse(LinearOperator):
 
         self._target = DomainTuple.make(UnstructuredDomain(nlos))
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
     def apply(self, x, mode):
         self._check_input(x, mode)
         if mode == self.TIMES:
diff --git a/nifty5/minimization/descent_minimizer.py b/nifty5/minimization/descent_minimizer.py
index 3ddfefd0d..963159f18 100644
--- a/nifty5/minimization/descent_minimizer.py
+++ b/nifty5/minimization/descent_minimizer.py
@@ -42,7 +42,6 @@ class DescentMinimizer(Minimizer):
     """
 
     def __init__(self, controller, line_searcher=LineSearchStrongWolfe()):
-        super(DescentMinimizer, self).__init__()
         self._controller = controller
         self.line_searcher = line_searcher
 
diff --git a/nifty5/minimization/energy.py b/nifty5/minimization/energy.py
index d14df4af4..c213a6e8f 100644
--- a/nifty5/minimization/energy.py
+++ b/nifty5/minimization/energy.py
@@ -47,7 +47,6 @@ class Energy(NiftyMetaBase()):
     """
 
     def __init__(self, position):
-        super(Energy, self).__init__()
         self._position = position
         self._gradnorm = None
 
diff --git a/nifty5/minimization/gradient_norm_controller.py b/nifty5/minimization/gradient_norm_controller.py
index c7d98be96..6c6a49c24 100644
--- a/nifty5/minimization/gradient_norm_controller.py
+++ b/nifty5/minimization/gradient_norm_controller.py
@@ -47,7 +47,6 @@ class GradientNormController(IterationController):
 
     def __init__(self, tol_abs_gradnorm=None, tol_rel_gradnorm=None,
                  convergence_level=1, iteration_limit=None, name=None):
-        super(GradientNormController, self).__init__()
         self._tol_abs_gradnorm = tol_abs_gradnorm
         self._tol_rel_gradnorm = tol_rel_gradnorm
         self._convergence_level = convergence_level
diff --git a/nifty5/minimization/line_energy.py b/nifty5/minimization/line_energy.py
index f6fd528d2..b7fbe1393 100644
--- a/nifty5/minimization/line_energy.py
+++ b/nifty5/minimization/line_energy.py
@@ -50,7 +50,6 @@ class LineEnergy(object):
     """
 
     def __init__(self, line_position, energy, line_direction, offset=0.):
-        super(LineEnergy, self).__init__()
         self._line_position = float(line_position)
         self._line_direction = line_direction
 
diff --git a/nifty5/minimization/scipy_minimizer.py b/nifty5/minimization/scipy_minimizer.py
index 9fad98daa..057355b0d 100644
--- a/nifty5/minimization/scipy_minimizer.py
+++ b/nifty5/minimization/scipy_minimizer.py
@@ -74,7 +74,6 @@ class ScipyMinimizer(Minimizer):
     """
 
     def __init__(self, method, options, need_hessp, bounds):
-        super(ScipyMinimizer, self).__init__()
         if not dobj.is_numpy():
             raise NotImplementedError
         self._method = method
@@ -130,7 +129,6 @@ def L_BFGS_B(ftol, gtol, maxiter, maxcor=10, disp=False, bounds=None):
 
 class ScipyCG(Minimizer):
     def __init__(self, tol, maxiter):
-        super(ScipyCG, self).__init__()
         if not dobj.is_numpy():
             raise NotImplementedError
         self._tol = tol
diff --git a/nifty5/operators/block_diagonal_operator.py b/nifty5/operators/block_diagonal_operator.py
index 9a601e15c..eaa0b4a7a 100644
--- a/nifty5/operators/block_diagonal_operator.py
+++ b/nifty5/operators/block_diagonal_operator.py
@@ -33,21 +33,16 @@ class BlockDiagonalOperator(EndomorphicOperator):
             dictionary with operators domain names as keys and
             LinearOperators as items
         """
-        super(BlockDiagonalOperator, self).__init__()
         if not isinstance(domain, MultiDomain):
             raise TypeError("MultiDomain expected")
         if not isinstance(operators, tuple):
             raise TypeError("tuple expected")
         self._domain = domain
         self._ops = operators
-        self._cap = self._all_ops
+        self._capability = self._all_ops
         for op in self._ops:
             if op is not None:
-                self._cap &= op.capability
-
-    @property
-    def capability(self):
-        return self._cap
+                self._capability &= op.capability
 
     def apply(self, x, mode):
         self._check_input(x, mode)
diff --git a/nifty5/operators/central_zero_padder.py b/nifty5/operators/central_zero_padder.py
index 1f48c8d83..499b09f79 100644
--- a/nifty5/operators/central_zero_padder.py
+++ b/nifty5/operators/central_zero_padder.py
@@ -16,8 +16,6 @@ from .. import dobj
 #           highest frequency.
 class CentralZeroPadder(LinearOperator):
     def __init__(self, domain, new_shape, space=0):
-        super(CentralZeroPadder, self).__init__()
-
         self._domain = DomainTuple.make(domain)
         self._space = utilities.infer_space(self._domain, space)
         dom = self._domain[self._space]
@@ -35,6 +33,7 @@ class CentralZeroPadder(LinearOperator):
         self._target = list(self._domain)
         self._target[self._space] = tgt
         self._target = DomainTuple.make(self._target)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
         slicer = []
         axes = self._target.axes[self._space]
@@ -53,10 +52,6 @@ class CentralZeroPadder(LinearOperator):
                     self.slicer[i] = tuple(tmp)
         self.slicer = tuple(self.slicer)
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
     def apply(self, x, mode):
         self._check_input(x, mode)
         x = x.val
diff --git a/nifty5/operators/chain_operator.py b/nifty5/operators/chain_operator.py
index 548ba0474..16365a7b3 100644
--- a/nifty5/operators/chain_operator.py
+++ b/nifty5/operators/chain_operator.py
@@ -31,7 +31,6 @@ class ChainOperator(LinearOperator):
     def __init__(self, ops, _callingfrommake=False):
         if not _callingfrommake:
             raise NotImplementedError
-        super(ChainOperator, self).__init__()
         self._ops = ops
         self._capability = self._all_ops
         for op in ops:
@@ -132,10 +131,6 @@ class ChainOperator(LinearOperator):
             return self.make([op._flip_modes(trafo) for op in self._ops])
         raise ValueError("invalid operator transformation")
 
-    @property
-    def capability(self):
-        return self._capability
-
     def apply(self, x, mode):
         self._check_mode(mode)
         t_ops = self._ops if mode & self._backwards else reversed(self._ops)
diff --git a/nifty5/operators/diagonal_operator.py b/nifty5/operators/diagonal_operator.py
index 506964163..5864ae038 100644
--- a/nifty5/operators/diagonal_operator.py
+++ b/nifty5/operators/diagonal_operator.py
@@ -57,8 +57,6 @@ class DiagonalOperator(EndomorphicOperator):
     """
 
     def __init__(self, diagonal, domain=None, spaces=None):
-        super(DiagonalOperator, self).__init__()
-
         if not isinstance(diagonal, Field):
             raise TypeError("Field object required")
         if domain is None:
@@ -99,6 +97,7 @@ class DiagonalOperator(EndomorphicOperator):
     def _fill_rest(self):
         self._ldiag.flags.writeable = False
         self._complex = utilities.iscomplextype(self._ldiag.dtype)
+        self._capability = self._all_ops
         if not self._complex:
             lmin = self._ldiag.min() if self._ldiag.size > 0 else 1.
             self._diagmin = dobj.np_allreduce_min(np.array(lmin))[()]
@@ -150,10 +149,6 @@ class DiagonalOperator(EndomorphicOperator):
             return Field.from_local_data(x.domain, x.local_data*xdiag)
         return Field.from_local_data(x.domain, x.local_data/xdiag)
 
-    @property
-    def capability(self):
-        return self._all_ops
-
     def _flip_modes(self, trafo):
         xdiag = self._ldiag
         if self._complex and (trafo & self.ADJOINT_BIT):
diff --git a/nifty5/operators/dof_distributor.py b/nifty5/operators/dof_distributor.py
index ef864760c..4f7538317 100644
--- a/nifty5/operators/dof_distributor.py
+++ b/nifty5/operators/dof_distributor.py
@@ -54,8 +54,6 @@ class DOFDistributor(LinearOperator):
     """
 
     def __init__(self, dofdex, target=None, space=None):
-        super(DOFDistributor, self).__init__()
-
         if target is None:
             target = dofdex.domain
         self._target = DomainTuple.make(target)
@@ -98,6 +96,7 @@ class DOFDistributor(LinearOperator):
         dom = list(self._target)
         dom[self._space] = other_space
         self._domain = DomainTuple.make(dom)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
         if dobj.default_distaxis() in self._domain.axes[self._space]:
             dofdex = dobj.local_data(dofdex)
@@ -142,7 +141,3 @@ class DOFDistributor(LinearOperator):
     def apply(self, x, mode):
         self._check_input(x, mode)
         return self._times(x) if mode == self.TIMES else self._adjoint_times(x)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/domain_distributor.py b/nifty5/operators/domain_distributor.py
index 08939a45f..6044e6e32 100644
--- a/nifty5/operators/domain_distributor.py
+++ b/nifty5/operators/domain_distributor.py
@@ -35,6 +35,7 @@ class DomainDistributor(LinearOperator):
         self._domain = [tgt for i, tgt in enumerate(self._target)
                         if i in self._spaces]
         self._domain = DomainTuple.make(self._domain)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -49,7 +50,3 @@ class DomainDistributor(LinearOperator):
         else:
             return x.sum([s for s in range(len(x.domain))
                           if s not in self._spaces])
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/energy_operators.py b/nifty5/operators/energy_operators.py
index dc5c75e94..a35598991 100644
--- a/nifty5/operators/energy_operators.py
+++ b/nifty5/operators/energy_operators.py
@@ -36,7 +36,6 @@ class EnergyOperator(Operator):
 
 class SquaredNormOperator(EnergyOperator):
     def __init__(self, domain):
-        super(SquaredNormOperator, self).__init__()
         self._domain = domain
 
     def apply(self, x):
@@ -50,7 +49,6 @@ class SquaredNormOperator(EnergyOperator):
 class QuadraticFormOperator(EnergyOperator):
     def __init__(self, op):
         from .endomorphic_operator import EndomorphicOperator
-        super(QuadraticFormOperator, self).__init__()
         if not isinstance(op, EndomorphicOperator):
             raise TypeError("op must be an EndomorphicOperator")
         self._op = op
@@ -67,7 +65,6 @@ class QuadraticFormOperator(EnergyOperator):
 
 class GaussianEnergy(EnergyOperator):
     def __init__(self, mean=None, covariance=None, domain=None):
-        super(GaussianEnergy, self).__init__()
         self._domain = None
         if mean is not None:
             self._checkEquivalence(mean.domain)
@@ -132,7 +129,6 @@ class BernoulliEnergy(EnergyOperator):
 
 class Hamiltonian(EnergyOperator):
     def __init__(self, lh, ic_samp=None):
-        super(Hamiltonian, self).__init__()
         self._lh = lh
         self._prior = GaussianEnergy(domain=lh.domain)
         self._ic_samp = ic_samp
@@ -156,7 +152,6 @@ class SampledKullbachLeiblerDivergence(EnergyOperator):
         h: Hamiltonian
         N: Number of samples to be used
         """
-        super(SampledKullbachLeiblerDivergence, self).__init__()
         self._h = h
         self._domain = h.domain
         self._res_samples = tuple(res_samples)
diff --git a/nifty5/operators/exp_transform.py b/nifty5/operators/exp_transform.py
index db12ba311..9d7d04b5c 100644
--- a/nifty5/operators/exp_transform.py
+++ b/nifty5/operators/exp_transform.py
@@ -33,6 +33,7 @@ from ..utilities import infer_space, special_add_at
 class ExpTransform(LinearOperator):
     def __init__(self, target, dof, space=0):
         self._target = DomainTuple.make(target)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
         self._space = infer_space(self._target, space)
         tgt = self._target[self._space]
         if not ((isinstance(tgt, RGSpace) and tgt.harmonic) or
@@ -115,7 +116,3 @@ class ExpTransform(LinearOperator):
             if d == ax:
                 x = dobj.redistribute(x, dist=ax)
         return Field(self._tgt(mode), val=x)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/field_zero_padder.py b/nifty5/operators/field_zero_padder.py
index b0f67daa4..afe53e03f 100644
--- a/nifty5/operators/field_zero_padder.py
+++ b/nifty5/operators/field_zero_padder.py
@@ -13,7 +13,6 @@ from .. import utilities
 
 class FieldZeroPadder(LinearOperator):
     def __init__(self, domain, new_shape, space=0):
-        super(FieldZeroPadder, self).__init__()
         self._domain = DomainTuple.make(domain)
         self._space = utilities.infer_space(self._domain, space)
         dom = self._domain[self._space]
@@ -30,10 +29,7 @@ class FieldZeroPadder(LinearOperator):
         self._target = list(self._domain)
         self._target[self._space] = tgt
         self._target = DomainTuple.make(self._target)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
diff --git a/nifty5/operators/harmonic_operators.py b/nifty5/operators/harmonic_operators.py
index e07d3da10..480540848 100644
--- a/nifty5/operators/harmonic_operators.py
+++ b/nifty5/operators/harmonic_operators.py
@@ -57,10 +57,9 @@ class FFTOperator(LinearOperator):
     """
 
     def __init__(self, domain, target=None, space=None):
-        super(FFTOperator, self).__init__()
-
         # Initialize domain and target
         self._domain = DomainTuple.make(domain)
+        self._capability = self._all_ops
         self._space = utilities.infer_space(self._domain, space)
 
         adom = self._domain[self._space]
@@ -127,10 +126,6 @@ class FFTOperator(LinearOperator):
             fct *= self._target[self._space].scalar_dvol
         return Tval if fct == 1 else Tval*fct
 
-    @property
-    def capability(self):
-        return self._all_ops
-
 
 class HartleyOperator(LinearOperator):
     """Transforms between a pair of position and harmonic RGSpaces.
@@ -162,10 +157,9 @@ class HartleyOperator(LinearOperator):
     """
 
     def __init__(self, domain, target=None, space=None):
-        super(HartleyOperator, self).__init__()
-
         # Initialize domain and target
         self._domain = DomainTuple.make(domain)
+        self._capability = self._all_ops
         self._space = utilities.infer_space(self._domain, space)
 
         adom = self._domain[self._space]
@@ -239,10 +233,6 @@ class HartleyOperator(LinearOperator):
             fct = self._target[self._space].scalar_dvol
         return Tval if fct == 1 else Tval*fct
 
-    @property
-    def capability(self):
-        return self._all_ops
-
 
 class SHTOperator(LinearOperator):
     """Transforms between a harmonic domain on the sphere and a position
@@ -272,10 +262,9 @@ class SHTOperator(LinearOperator):
     """
 
     def __init__(self, domain, target=None, space=None):
-        super(SHTOperator, self).__init__()
-
         # Initialize domain and target
         self._domain = DomainTuple.make(domain)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
         self._space = utilities.infer_space(self._domain, space)
 
         hspc = self._domain[self._space]
@@ -351,10 +340,6 @@ class SHTOperator(LinearOperator):
             odat = dobj.redistribute(odat, dist=dobj.distaxis(x.val))
         return Field(tdom, odat)
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
 
 class HarmonicTransformOperator(LinearOperator):
     """Transforms between a harmonic domain and a position domain counterpart.
@@ -384,8 +369,6 @@ class HarmonicTransformOperator(LinearOperator):
     """
 
     def __init__(self, domain, target=None, space=None):
-        super(HarmonicTransformOperator, self).__init__()
-
         domain = DomainTuple.make(domain)
         space = utilities.infer_space(domain, space)
 
@@ -399,15 +382,12 @@ class HarmonicTransformOperator(LinearOperator):
             self._op = SHTOperator(domain, target, space)
         self._domain = self._op.domain
         self._target = self._op.target
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
         return self._op.apply(x, mode)
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
 
 def HarmonicSmoothingOperator(domain, sigma, space=None):
     """ This function returns an operator that carries out a smoothing with
diff --git a/nifty5/operators/inversion_enabler.py b/nifty5/operators/inversion_enabler.py
index 9b7cc7654..1be154473 100644
--- a/nifty5/operators/inversion_enabler.py
+++ b/nifty5/operators/inversion_enabler.py
@@ -51,15 +51,11 @@ class InversionEnabler(EndomorphicOperator):
     """
 
     def __init__(self, op, iteration_controller, approximation=None):
-        super(InversionEnabler, self).__init__()
         self._op = op
         self._ic = iteration_controller
         self._approximation = approximation
         self._domain = op.domain
-
-    @property
-    def capability(self):
-        return self._addInverse[self._op.capability]
+        self._capability = self._addInverse[self._op.capability]
 
     def apply(self, x, mode):
         self._check_mode(mode)
diff --git a/nifty5/operators/laplace_operator.py b/nifty5/operators/laplace_operator.py
index a779b9279..21a63e743 100644
--- a/nifty5/operators/laplace_operator.py
+++ b/nifty5/operators/laplace_operator.py
@@ -47,8 +47,8 @@ class LaplaceOperator(EndomorphicOperator):
     """
 
     def __init__(self, domain, space=None, logarithmic=True):
-        super(LaplaceOperator, self).__init__()
         self._domain = DomainTuple.make(domain)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
         self._space = infer_space(self._domain, space)
 
         if not isinstance(self._domain[self._space], PowerSpace):
@@ -68,10 +68,6 @@ class LaplaceOperator(EndomorphicOperator):
         self._dposc[1:] += self._dpos
         self._dposc *= 0.5
 
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
-
     def _times(self, x):
         axes = x.domain.axes[self._space]
         axis = axes[0]
diff --git a/nifty5/operators/linear_operator.py b/nifty5/operators/linear_operator.py
index a74b4292e..4cca76fa3 100644
--- a/nifty5/operators/linear_operator.py
+++ b/nifty5/operators/linear_operator.py
@@ -145,7 +145,7 @@ class LinearOperator(Operator):
         :attr:`INVERSE_TIMES`, and :attr:`ADJOINT_INVERSE_TIMES`,
         joined together by the "|" operator.
         """
-        raise NotImplementedError
+        return self._capability
 
     def apply(self, x, mode):
         """ Applies the Operator to a given `x`, in a specified `mode`.
diff --git a/nifty5/operators/mask_operator.py b/nifty5/operators/mask_operator.py
index 6cc700f2f..abd9ed111 100644
--- a/nifty5/operators/mask_operator.py
+++ b/nifty5/operators/mask_operator.py
@@ -38,6 +38,7 @@ class MaskOperator(LinearOperator):
         self._domain = DomainTuple.make(mask.domain)
         self._mask = np.logical_not(mask.to_global_data())
         self._target = DomainTuple.make(UnstructuredDomain(self._mask.sum()))
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
     def data_indices(self):
         if len(self.domain.shape) == 1:
@@ -55,7 +56,3 @@ class MaskOperator(LinearOperator):
         res[self._mask] = x
         res[~self._mask] = 0
         return Field.from_global_data(self.domain, res)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/operator_adapter.py b/nifty5/operators/operator_adapter.py
index 6d4d79c38..338f087c1 100644
--- a/nifty5/operators/operator_adapter.py
+++ b/nifty5/operators/operator_adapter.py
@@ -45,17 +45,13 @@ class OperatorAdapter(LinearOperator):
     """
 
     def __init__(self, op, op_transform):
-        super(OperatorAdapter, self).__init__()
         self._op = op
         self._trafo = int(op_transform)
         if self._trafo < 1 or self._trafo > 3:
             raise ValueError("invalid operator transformation")
         self._domain = self._op._dom(1 << self._trafo)
         self._target = self._op._tgt(1 << self._trafo)
-
-    @property
-    def capability(self):
-        return self._capTable[self._trafo][self._op.capability]
+        self._capability = self._capTable[self._trafo][self._op.capability]
 
     def _flip_modes(self, trafo):
         newtrafo = trafo ^ self._trafo
diff --git a/nifty5/operators/qht_operator.py b/nifty5/operators/qht_operator.py
index b2d0f07b3..2bd7919d7 100644
--- a/nifty5/operators/qht_operator.py
+++ b/nifty5/operators/qht_operator.py
@@ -59,6 +59,7 @@ class QHTOperator(LinearOperator):
         self._domain[self._space] = \
             self._target[self._space].get_default_codomain()
         self._domain = DomainTuple.make(self._domain)
+        self._capability = self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -76,7 +77,3 @@ class QHTOperator(LinearOperator):
             if i == ax:
                 x = dobj.redistribute(x, dist=ax)
         return Field(self._tgt(mode), val=x)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/relaxed_sum_operator.py b/nifty5/operators/relaxed_sum_operator.py
index fb4d6117d..53530e2c5 100644
--- a/nifty5/operators/relaxed_sum_operator.py
+++ b/nifty5/operators/relaxed_sum_operator.py
@@ -32,7 +32,6 @@ class RelaxedSumOperator(LinearOperator):
     """Class representing sums of operators with compatible domains."""
 
     def __init__(self, ops):
-        super(RelaxedSumOperator, self).__init__()
         self._ops = ops
         self._domain = domain_union([op.domain for op in ops])
         self._target = domain_union([op.target for op in ops])
@@ -50,10 +49,6 @@ class RelaxedSumOperator(LinearOperator):
     def adjoint(self):
         return RelaxedSumOperator([op.adjoint for op in self._ops])
 
-    @property
-    def capability(self):
-        return self._capability
-
     def apply(self, x, mode):
         self._check_input(x, mode)
         res = None
diff --git a/nifty5/operators/sampling_enabler.py b/nifty5/operators/sampling_enabler.py
index 728f4ad86..66933b2cb 100644
--- a/nifty5/operators/sampling_enabler.py
+++ b/nifty5/operators/sampling_enabler.py
@@ -50,13 +50,13 @@ class SamplingEnabler(EndomorphicOperator):
 
     def __init__(self, likelihood, prior, iteration_controller,
                  approximation=None):
-        super(SamplingEnabler, self).__init__()
         self._op = likelihood + prior
         self._likelihood = likelihood
         self._prior = prior
         self._ic = iteration_controller
         self._approximation = approximation
         self._domain = self._op.domain
+        self._capability = self._op.capability
 
     def draw_sample(self, from_inverse=False, dtype=np.float64):
         try:
@@ -77,9 +77,5 @@ class SamplingEnabler(EndomorphicOperator):
                 energy, convergence = inverter(energy)
             return energy.position
 
-    @property
-    def capability(self):
-        return self._op.capability
-
     def apply(self, x, mode):
         return self._op.apply(x, mode)
diff --git a/nifty5/operators/sandwich_operator.py b/nifty5/operators/sandwich_operator.py
index 76ea55a50..6895bd7a6 100644
--- a/nifty5/operators/sandwich_operator.py
+++ b/nifty5/operators/sandwich_operator.py
@@ -34,11 +34,11 @@ class SandwichOperator(EndomorphicOperator):
     def __init__(self, bun, cheese, op, _callingfrommake=False):
         if not _callingfrommake:
             raise NotImplementedError
-        super(SandwichOperator, self).__init__()
         self._bun = bun
         self._cheese = cheese
         self._op = op
         self._domain = op.domain
+        self._capability = op._capability
 
     @staticmethod
     def make(bun, cheese=None):
@@ -66,10 +66,6 @@ class SandwichOperator(EndomorphicOperator):
             return op
         return SandwichOperator(bun, cheese, op, _callingfrommake=True)
 
-    @property
-    def capability(self):
-        return self._op.capability
-
     def apply(self, x, mode):
         return self._op.apply(x, mode)
 
diff --git a/nifty5/operators/scaling_operator.py b/nifty5/operators/scaling_operator.py
index b3d5e5d42..3f8c5ff4e 100644
--- a/nifty5/operators/scaling_operator.py
+++ b/nifty5/operators/scaling_operator.py
@@ -54,12 +54,12 @@ class ScalingOperator(EndomorphicOperator):
 
     def __init__(self, factor, domain):
         from ..sugar import makeDomain
-        super(ScalingOperator, self).__init__()
 
         if not np.isscalar(factor):
             raise TypeError("Scalar required")
         self._factor = factor
         self._domain = makeDomain(domain)
+        self._capability = self._all_ops
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -82,10 +82,6 @@ class ScalingOperator(EndomorphicOperator):
             fct = 1./fct
         return ScalingOperator(fct, self._domain)
 
-    @property
-    def capability(self):
-        return self._all_ops
-
     def draw_sample(self, from_inverse=False, dtype=np.float64):
         fct = self._factor
         if fct.imag != 0. or fct.real < 0.:
diff --git a/nifty5/operators/simple_linear_operators.py b/nifty5/operators/simple_linear_operators.py
index cb047d324..f5b5573db 100644
--- a/nifty5/operators/simple_linear_operators.py
+++ b/nifty5/operators/simple_linear_operators.py
@@ -33,14 +33,10 @@ from ..multi_field import MultiField
 
 class VdotOperator(LinearOperator):
     def __init__(self, field):
-        super(VdotOperator, self).__init__()
         self._field = field
         self._domain = field.domain
         self._target = DomainTuple.scalar_domain()
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -51,13 +47,9 @@ class VdotOperator(LinearOperator):
 
 class SumReductionOperator(LinearOperator):
     def __init__(self, domain):
-        super(SumReductionOperator, self).__init__()
         self._domain = domain
         self._target = DomainTuple.scalar_domain()
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -68,12 +60,8 @@ class SumReductionOperator(LinearOperator):
 
 class ConjugationOperator(EndomorphicOperator):
     def __init__(self, domain):
-        super(ConjugationOperator, self).__init__()
         self._domain = domain
-
-    @property
-    def capability(self):
-        return self._all_ops
+        self._capability =  self._all_ops
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -82,12 +70,8 @@ class ConjugationOperator(EndomorphicOperator):
 
 class Realizer(EndomorphicOperator):
     def __init__(self, domain):
-        super(Realizer, self).__init__()
         self._domain = domain
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -99,10 +83,7 @@ class FieldAdapter(LinearOperator):
         self._domain = MultiDomain.make(dom)
         self._name = name_dom
         self._target = dom[name_dom]
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -131,14 +112,10 @@ class GeometryRemover(LinearOperator):
     """
 
     def __init__(self, domain):
-        super(GeometryRemover, self).__init__()
         self._domain = DomainTuple.make(domain)
         target_list = [UnstructuredDomain(dom.shape) for dom in self._domain]
         self._target = DomainTuple.make(target_list)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     def apply(self, x, mode):
         self._check_input(x, mode)
@@ -162,6 +139,7 @@ class NullOperator(LinearOperator):
         from ..sugar import makeDomain
         self._domain = makeDomain(domain)
         self._target = makeDomain(target)
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
     @staticmethod
     def _nullfield(dom):
@@ -176,7 +154,3 @@ class NullOperator(LinearOperator):
         if mode == self.TIMES:
             return self._nullfield(self._target)
         return self._nullfield(self._domain)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/slope_operator.py b/nifty5/operators/slope_operator.py
index 618b022c8..06d33e4ad 100644
--- a/nifty5/operators/slope_operator.py
+++ b/nifty5/operators/slope_operator.py
@@ -37,6 +37,7 @@ class SlopeOperator(LinearOperator):
 
         self._domain = DomainTuple.make(domain)
         self._target = DomainTuple.make(target)
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
 
         if self.domain[0].shape != (len(self.target[0].shape) + 1,):
             raise AssertionError("Shape mismatch!")
@@ -74,7 +75,3 @@ class SlopeOperator(LinearOperator):
         for i in range(self.ndim):
             res[i] = np.sum(self.pos[i] * xglob) * self._sigmas[i]
         return Field.from_global_data(self.domain, res)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/nifty5/operators/sum_operator.py b/nifty5/operators/sum_operator.py
index de43f1650..0dcf73d7b 100644
--- a/nifty5/operators/sum_operator.py
+++ b/nifty5/operators/sum_operator.py
@@ -31,7 +31,6 @@ class SumOperator(LinearOperator):
     def __init__(self, ops, neg, dom, tgt, _callingfrommake=False):
         if not _callingfrommake:
             raise NotImplementedError
-        super(SumOperator, self).__init__()
         self._ops = ops
         self._neg = neg
         self._domain = dom
@@ -163,10 +162,6 @@ class SumOperator(LinearOperator):
     def adjoint(self):
         return self.make([op.adjoint for op in self._ops], self._neg)
 
-    @property
-    def capability(self):
-        return self._capability
-
     def apply(self, x, mode):
         self._check_mode(mode)
         res = None
diff --git a/nifty5/operators/symmetrizing_operator.py b/nifty5/operators/symmetrizing_operator.py
index 5c4d6276a..b8f5370ee 100644
--- a/nifty5/operators/symmetrizing_operator.py
+++ b/nifty5/operators/symmetrizing_operator.py
@@ -30,6 +30,7 @@ from .. import utilities
 class SymmetrizingOperator(EndomorphicOperator):
     def __init__(self, domain, space=0):
         self._domain = DomainTuple.make(domain)
+        self._capability =  self.TIMES | self.ADJOINT_TIMES
         self._space = utilities.infer_space(self._domain, space)
         dom = self._domain[self._space]
         if not (isinstance(dom, LogRGSpace) and not dom.harmonic):
@@ -48,7 +49,3 @@ class SymmetrizingOperator(EndomorphicOperator):
             if i == ax:
                 tmp = dobj.redistribute(tmp, dist=ax)
         return Field(self.target, val=tmp)
-
-    @property
-    def capability(self):
-        return self.TIMES | self.ADJOINT_TIMES
diff --git a/test/test_minimization/test_minimizers.py b/test/test_minimization/test_minimizers.py
index 057d45ad1..35be855ca 100644
--- a/test/test_minimization/test_minimizers.py
+++ b/test/test_minimization/test_minimizers.py
@@ -99,15 +99,12 @@ class Test_Minimizers(unittest.TestCase):
                 class RBCurv(ift.EndomorphicOperator):
                     def __init__(self, loc):
                         self._loc = loc.to_global_data_rw()
+                        self._capability = self.TIMES
 
                     @property
                     def domain(self):
                         return space
 
-                    @property
-                    def capability(self):
-                        return self.TIMES
-
                     def apply(self, x, mode):
                         self._check_input(x, mode)
                         inp = x.to_global_data_rw()
-- 
GitLab