From 0282855bba53d6bbae5f7c647d575e7a8b8f0ee6 Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Fri, 17 Mar 2017 16:26:45 +0100 Subject: [PATCH] API: remove dist_using_inner, closes #963 --- odl/space/npy_tensors.py | 133 ++++-------------------- odl/space/pspace.py | 133 ++++-------------------- odl/space/weighting.py | 159 ++++++----------------------- odl/test/space/npy_tensors_test.py | 59 +---------- 4 files changed, 72 insertions(+), 412 deletions(-) diff --git a/odl/space/npy_tensors.py b/odl/space/npy_tensors.py index 5505c015a24..c4b3bd64f22 100644 --- a/odl/space/npy_tensors.py +++ b/odl/space/npy_tensors.py @@ -125,8 +125,6 @@ def __init__(self, shape, dtype=None, order='A', **kwargs): mathematical requirements. By default, ``dist(x, y)`` is calculated as ``norm(x - y)``. - This creates an intermediate array ``x - y``, which can be - avoided by choosing ``dist_using_inner=True``. This option cannot be combined with ``weight``, ``norm`` or ``inner``. It also cannot be used in case of @@ -153,19 +151,6 @@ def __init__(self, shape, dtype=None, order='A', **kwargs): ``dist`` or ``norm``. It also cannot be used in case of non-numeric ``dtype``. - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. - - Default: False. - kwargs : Further keyword arguments are passed to the weighting classes. @@ -237,7 +222,6 @@ def __init__(self, shape, dtype=None, order='A', **kwargs): inner = kwargs.pop('inner', None) weighting = kwargs.pop('weighting', None) exponent = kwargs.pop('exponent', getattr(weighting, 'exponent', 2.0)) - dist_using_inner = bool(kwargs.pop('dist_using_inner', False)) if (not is_numeric_dtype(self.dtype) and any(x is not None for x in (dist, norm, inner, weighting))): @@ -262,15 +246,9 @@ def __init__(self, shape, dtype=None, order='A', **kwargs): raise ValueError('`weighting.exponent` conflicts with ' '`exponent`: {} != {}' ''.format(weighting.exponent, exponent)) - if weighting.dist_using_inner != dist_using_inner: - raise ValueError('`weighting.dist_using_inner` conflicts ' - 'with `dist_using_inner`: {} != {}' - ''.format(weighting.dist_using_inner, - dist_using_inner)) self.__weighting = weighting else: - self.__weighting = _weighting( - weighting, exponent, dist_using_inner=dist_using_inner) + self.__weighting = _weighting(weighting, exponent) # Check (afterwards) that the weighting input was sane if isinstance(self.weighting, NumpyTensorSpaceArrayWeighting): @@ -296,8 +274,7 @@ def __init__(self, shape, dtype=None, order='A', **kwargs): elif inner is not None: self.__weighting = NumpyTensorSpaceCustomInner(inner) else: - self.__weighting = NumpyTensorSpaceNoWeighting( - exponent, dist_using_inner=dist_using_inner) + self.__weighting = NumpyTensorSpaceNoWeighting(exponent) @property def impl(self): @@ -1529,18 +1506,15 @@ def fallback_copy(x1, x2, n): out.data[:] = out_arr.reshape(out.shape, order=ravel_order) -def _weighting(weights, exponent, dist_using_inner=False): +def _weighting(weights, exponent): """Return a weighting whose type is inferred from the arguments.""" if np.isscalar(weights): - weighting = NumpyTensorSpaceConstWeighting( - weights, exponent, dist_using_inner=dist_using_inner) + weighting = NumpyTensorSpaceConstWeighting(weights, exponent) elif weights is None: - weighting = NumpyTensorSpaceNoWeighting( - exponent, dist_using_inner=dist_using_inner) + weighting = NumpyTensorSpaceNoWeighting(exponent) else: # last possibility: make an array arr = np.asarray(weights) - weighting = NumpyTensorSpaceArrayWeighting( - arr, exponent, dist_using_inner=dist_using_inner) + weighting = NumpyTensorSpaceArrayWeighting(arr, exponent) return weighting @@ -1594,7 +1568,7 @@ def npy_weighted_norm(weights, exponent=2.0): return _weighting(weights, exponent=exponent).norm -def npy_weighted_dist(weights, exponent=2.0, use_inner=False): +def npy_weighted_dist(weights, exponent=2.0): """Weighted distance on `TensorSpace`'s as free function. Parameters @@ -1604,16 +1578,6 @@ def npy_weighted_dist(weights, exponent=2.0, use_inner=False): constant weight, a 1-dim. array as a weighting vector. exponent : positive `float` Exponent of the norm. - use_inner : `bool`, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. Returns ------- @@ -1627,8 +1591,7 @@ def npy_weighted_dist(weights, exponent=2.0, use_inner=False): NumpyTensorSpaceConstWeighting NumpyTensorSpaceArrayWeighting """ - return _weighting(weights, exponent=exponent, - dist_using_inner=use_inner).dist + return _weighting(weights, exponent=exponent).dist def _norm_default(x): @@ -1688,7 +1651,7 @@ class NumpyTensorSpaceArrayWeighting(ArrayWeighting): See ``Notes`` for mathematical details. """ - def __init__(self, array, exponent=2.0, dist_using_inner=False): + def __init__(self, array, exponent=2.0): """Initialize a new instance. Parameters @@ -1700,16 +1663,6 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): exponent : positive `float` Exponent of the norm. For values other than 2.0, no inner product is defined. - dist_using_inner : `bool`, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. Notes ----- @@ -1755,13 +1708,11 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): it does not define an inner product or norm, respectively. This is not checked during initialization. """ - super().__init__(array, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(array, impl='numpy', exponent=exponent) def __hash__(self): """Return ``hash(self)``.""" - return hash((type(self), self.array.tobytes(), self.exponent, - self.dist_using_inner)) + return hash((type(self), self.array.tobytes(), self.exponent)) def inner(self, x1, x2): """Return the weighted inner product of ``x1`` and ``x2``. @@ -1816,7 +1767,7 @@ class NumpyTensorSpaceConstWeighting(ConstWeighting): See ``Notes`` for mathematical details. """ - def __init__(self, const, exponent=2.0, dist_using_inner=False): + def __init__(self, const, exponent=2.0): """Initialize a new instance. Parameters @@ -1826,16 +1777,6 @@ def __init__(self, const, exponent=2.0, dist_using_inner=False): exponent : positive float Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : `bool`, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. Notes ----- @@ -1876,8 +1817,7 @@ def __init__(self, const, exponent=2.0, dist_using_inner=False): - The constant must be positive, otherwise it does not define an inner product or norm, respectively. """ - super().__init__(const, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(const, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Return the weighted inner product of ``x1`` and ``x2``. @@ -1934,13 +1874,7 @@ def dist(self, x1, x2): dist : float The distance between the tensors. """ - if self.dist_using_inner: - dist_squared = (_norm_default(x1) ** 2 + _norm_default(x2) ** 2 - - 2 * _inner_default(x1, x2).real) - if dist_squared < 0.0: # Compensate for numerical error - dist_squared = 0.0 - return float(np.sqrt(self.const * dist_squared)) - elif self.exponent == 2.0: + if self.exponent == 2.0: return float(np.sqrt(self.const) * _norm_default(x1 - x2)) elif self.exponent == float('inf'): return float(self.const * _pnorm_default(x1 - x2, self.exponent)) @@ -1961,24 +1895,18 @@ def __new__(cls, *args, **kwargs): """Implement singleton pattern if ``exp==2.0``.""" if len(args) == 0: exponent = kwargs.pop('exponent', 2.0) - dist_using_inner = kwargs.pop('dist_using_inner', False) - elif len(args) == 1: - exponent = args[0] - args = args[1:] - dist_using_inner = kwargs.pop('dist_using_inner', False) else: exponent = args[0] - dist_using_inner = args[1] - args = args[2:] + args = args[1:] - if exponent == 2.0 and not dist_using_inner: + if exponent == 2.0: if not cls.__instance: cls.__instance = super().__new__(cls, *args, **kwargs) return cls.__instance else: return super().__new__(cls, *args, **kwargs) - def __init__(self, exponent=2.0, dist_using_inner=False): + def __init__(self, exponent=2.0): """Initialize a new instance. Parameters @@ -1986,26 +1914,15 @@ def __init__(self, exponent=2.0, dist_using_inner=False): exponent : positive `float`, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : `bool`, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. """ - super().__init__(impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(impl='numpy', exponent=exponent) class NumpyTensorSpaceCustomInner(CustomInner): """Class for handling a user-specified inner product.""" - def __init__(self, inner, dist_using_inner=True): + def __init__(self, inner): """Initialize a new instance. Parameters @@ -2019,18 +1936,8 @@ def __init__(self, inner, dist_using_inner=True): - `` = conj()`` - `` = s * + `` - `` = 0`` if and only if ``x = 0`` - - dist_using_inner : `bool`, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. """ - super().__init__(inner, impl='numpy', - dist_using_inner=dist_using_inner) + super().__init__(inner, impl='numpy') class NumpyTensorSpaceCustomNorm(CustomNorm): diff --git a/odl/space/pspace.py b/odl/space/pspace.py index 90e3560c6b9..a29bccd46f8 100644 --- a/odl/space/pspace.py +++ b/odl/space/pspace.py @@ -97,8 +97,6 @@ class instance can be retrieved from the space by the - ``dist(x, y) <= dist(x, z) + dist(z, y)`` By default, ``dist(x, y)`` is calculated as ``norm(x - y)``. - This creates an intermediate array ``x - y``, which can be - avoided by choosing ``dist_using_inner=True``. Cannot be combined with: ``weighting, norm, inner`` @@ -130,21 +128,6 @@ class instance can be retrieved from the space by the Cannot be combined with: ``weighting, dist, norm`` - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. - - Default: ``False``. - - Cannot be combined with: ``dist`` - Examples -------- Product of two rn spaces @@ -220,7 +203,6 @@ class instance can be retrieved from the space by the inner = kwargs.pop('inner', None) weighting = kwargs.pop('weighting', None) exponent = float(kwargs.pop('exponent', 2.0)) - dist_using_inner = bool(kwargs.pop('dist_using_inner', False)) if kwargs: raise TypeError('got unexpected keyword arguments: {}' ''.format(kwargs)) @@ -271,7 +253,7 @@ class instance can be retrieved from the space by the self.__weighting = weighting elif np.isscalar(weighting): self.__weighting = ProductSpaceConstWeighting( - weighting, exponent, dist_using_inner=dist_using_inner) + weighting, exponent) elif weighting is None: # Need to wait until dist, norm and inner are handled pass @@ -282,7 +264,7 @@ class instance can be retrieved from the space by the ''.format(weighting)) if arr.ndim == 1: self.__weighting = ProductSpaceArrayWeighting( - arr, exponent, dist_using_inner=dist_using_inner) + arr, exponent) else: raise ValueError('weighting array has {} dimensions, ' 'expected 1'.format(arr.ndim)) @@ -294,8 +276,7 @@ class instance can be retrieved from the space by the elif inner is not None: self.__weighting = ProductSpaceCustomInner(inner) else: # all None -> no weighing - self.__weighting = ProductSpaceNoWeighting( - exponent, dist_using_inner=dist_using_inner) + self.__weighting = ProductSpaceNoWeighting(exponent) @property def size(self): @@ -965,7 +946,7 @@ class ProductSpaceArrayWeighting(ArrayWeighting): See ``Notes`` for mathematical details. """ - def __init__(self, array, exponent=2.0, dist_using_inner=False): + def __init__(self, array, exponent=2.0): """Initialize a new instance. Parameters @@ -975,16 +956,6 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, no inner product is defined. - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re ``. - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. Notes ----- @@ -1021,8 +992,7 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): define an inner product or norm, respectively. This is not checked during initialization. """ - super().__init__(array, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(array, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Calculate the array-weighted inner product of two elements. @@ -1085,7 +1055,7 @@ class ProductSpaceConstWeighting(ConstWeighting): """ - def __init__(self, constant, exponent=2.0, dist_using_inner=False): + def __init__(self, constant, exponent=2.0): """Initialize a new instance. Parameters @@ -1095,16 +1065,6 @@ def __init__(self, constant, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, no inner product is defined. - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. Notes ----- @@ -1139,8 +1099,7 @@ def __init__(self, constant, exponent=2.0, dist_using_inner=False): - The constant must be positive, otherwise it does not define an inner product or norm, respectively. """ - super().__init__(constant, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(constant, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Calculate the constant-weighted inner product of two elements. @@ -1207,36 +1166,15 @@ def dist(self, x1, x2): dist : float The distance between the elements. """ - if self.dist_using_inner: - norms1 = np.fromiter( - (x1i.norm() for x1i in x1), - dtype=np.float64, count=len(x1)) - norm1 = np.linalg.norm(norms1) - - norms2 = np.fromiter( - (x2i.norm() for x2i in x2), - dtype=np.float64, count=len(x2)) - norm2 = np.linalg.norm(norms2) - - inners = np.fromiter( - (x1i.inner(x2i) for x1i, x2i in zip(x1, x2)), - dtype=x1[0].space.dtype, count=len(x1)) - inner_re = np.sum(inners.real) - - dist_squared = norm1 ** 2 + norm2 ** 2 - 2 * inner_re - if dist_squared < 0.0: # Compensate for numerical error - dist_squared = 0.0 - return np.sqrt(self.const) * float(np.sqrt(dist_squared)) - else: - dnorms = np.fromiter( - ((x1i - x2i).norm() for x1i, x2i in zip(x1, x2)), - dtype=np.float64, count=len(x1)) + dnorms = np.fromiter( + ((x1i - x2i).norm() for x1i, x2i in zip(x1, x2)), + dtype=np.float64, count=len(x1)) - if self.exponent == float('inf'): - return self.const * np.linalg.norm(dnorms, ord=self.exponent) - else: - return (self.const ** (1 / self.exponent) * - np.linalg.norm(dnorms, ord=self.exponent)) + if self.exponent == float('inf'): + return self.const * np.linalg.norm(dnorms, ord=self.exponent) + else: + return (self.const ** (1 / self.exponent) * + np.linalg.norm(dnorms, ord=self.exponent)) class ProductSpaceNoWeighting(NoWeighting, ProductSpaceConstWeighting): @@ -1250,24 +1188,18 @@ def __new__(cls, *args, **kwargs): """Implement singleton pattern if ``exp==2.0``.""" if len(args) == 0: exponent = kwargs.pop('exponent', 2.0) - dist_using_inner = kwargs.pop('dist_using_inner', False) - elif len(args) == 1: - exponent = args[0] - args = args[1:] - dist_using_inner = kwargs.pop('dist_using_inner', False) else: exponent = args[0] - dist_using_inner = args[1] - args = args[2:] + args = args[1:] - if exponent == 2.0 and not dist_using_inner: + if exponent == 2.0: if not cls._instance: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance else: return super().__new__(cls, *args, **kwargs) - def __init__(self, exponent=2.0, dist_using_inner=False): + def __init__(self, exponent=2.0): """Initialize a new instance. Parameters @@ -1275,26 +1207,15 @@ def __init__(self, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. """ - super().__init__(impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(impl='numpy', exponent=exponent) class ProductSpaceCustomInner(CustomInner): """Class for handling a user-specified inner products.""" - def __init__(self, inner, dist_using_inner=False): + def __init__(self, inner): """Initialize a new instance. Parameters @@ -1309,20 +1230,8 @@ def __init__(self, inner, dist_using_inner=False): - `` = conj()`` - `` = s * + `` - `` = 0`` if and only if ``x = 0`` - - dist_using_inner : bool, optional - Calculate ``dist`` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. """ - super().__init__(impl='numpy', inner=inner, - dist_using_inner=dist_using_inner) + super().__init__(impl='numpy', inner=inner) class ProductSpaceCustomNorm(CustomNorm): diff --git a/odl/space/weighting.py b/odl/space/weighting.py index 167dc866ced..7a2c2073198 100644 --- a/odl/space/weighting.py +++ b/odl/space/weighting.py @@ -43,7 +43,7 @@ class Weighting(object): functions are being used. """ - def __init__(self, impl, exponent=2.0, dist_using_inner=False): + def __init__(self, impl, exponent=2.0): """Initialize a new instance. Parameters @@ -53,28 +53,12 @@ def __init__(self, impl, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : bool, optional - Calculate `dist` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re `` - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. - - Default: False. """ self.__impl = str(impl).lower() self.__exponent = float(exponent) - self.__dist_using_inner = bool(dist_using_inner) if self.exponent <= 0: raise ValueError('only positive exponents or inf supported, ' 'got {}'.format(exponent)) - elif self.exponent != 2.0 and self.dist_using_inner: - raise ValueError('`dist_using_inner` can only be used if the ' - 'exponent is 2.0') @property def impl(self): @@ -86,11 +70,6 @@ def exponent(self): """Exponent of this weighting.""" return self.__exponent - @property - def dist_using_inner(self): - """``True`` if the distance should be calculated using inner.""" - return self.__dist_using_inner - def __eq__(self, other): """Return ``self == other``. @@ -108,13 +87,11 @@ def __eq__(self, other): """ return (isinstance(other, Weighting) and self.impl == other.impl and - self.exponent == other.exponent and - self.dist_using_inner == other.dist_using_inner) + self.exponent == other.exponent) def __hash__(self): """Return ``hash(self)``.""" - return hash((type(self), self.impl, self.exponent, - self.dist_using_inner)) + return hash((type(self), self.impl, self.exponent)) def equiv(self, other): """Test if ``other`` is an equivalent weighting. @@ -179,14 +156,7 @@ def dist(self, x1, x2): dist : float The distance between the elements. """ - if self.dist_using_inner: - dist_squared = (self.norm(x1) ** 2 + self.norm(x2) ** 2 - - 2 * self.inner(x1, x2).real) - if dist_squared < 0: # Compensate for numerical error - dist_squared = 0.0 - return float(np.sqrt(dist_squared)) - else: - return self.norm(x1 - x2) + return self.norm(x1 - x2) class MatrixWeighting(Weighting): @@ -201,8 +171,7 @@ class MatrixWeighting(Weighting): checked during initialization. """ - def __init__(self, matrix, impl, exponent=2.0, dist_using_inner=False, - **kwargs): + def __init__(self, matrix, impl, exponent=2.0, **kwargs): """Initialize a new instance. Parameters @@ -216,16 +185,6 @@ def __init__(self, matrix, impl, exponent=2.0, dist_using_inner=False, product is not defined. If ``matrix`` is a sparse matrix, only 1.0, 2.0 and ``inf`` are allowed. - dist_using_inner : bool, optional - Calculate `dist` using the following formula:: - - ||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. precomp_mat_pow : bool, optional If ``True``, precompute the matrix power ``W ** (1/p)`` during initialization. This has no effect if ``exponent`` @@ -263,8 +222,7 @@ def __init__(self, matrix, impl, exponent=2.0, dist_using_inner=False, precomp_mat_pow = kwargs.pop('precomp_mat_pow', False) self._cache_mat_pow = bool(kwargs.pop('cache_mat_pow', True)) self._cache_mat_decomp = bool(kwargs.pop('cache_mat_decomp', False)) - super().__init__(impl=impl, exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(impl=impl, exponent=exponent) # Check and set matrix if isspmatrix(matrix): @@ -468,8 +426,6 @@ def repr_part(self): part = 'weighting={}'.format(arraynd_repr(self.matrix, nprint=10)) if self.exponent != 2.0: part += ', exponent={}'.format(self.exponent) - if self.dist_using_inner: - part += ', dist_using_inner=True' return part def __repr__(self): @@ -481,16 +437,12 @@ def __repr__(self): nnz = self.matrix.nnz if self.exponent != 2.0: inner_fstr += ', exponent={ex}' - if self.dist_using_inner: - inner_fstr += ', dist_using_inner=True' else: inner_fstr = '\n{matrix!r}' fmt = '' nnz = 0 if self.exponent != 2.0: inner_fstr += ',\nexponent={ex}' - if self.dist_using_inner: - inner_fstr += ',\ndist_using_inner=True' else: inner_fstr += '\n' @@ -520,7 +472,7 @@ class ArrayWeighting(Weighting): during initialization. """ - def __init__(self, array, impl, exponent=2.0, dist_using_inner=False): + def __init__(self, array, impl, exponent=2.0): """Initialize a new instance. Parameters @@ -533,19 +485,8 @@ def __init__(self, array, impl, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : bool, optional - Calculate `dist` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re ``. - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. """ - super().__init__(impl=impl, exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(impl=impl, exponent=exponent) # We store our "own" data structures as-is to retain Numpy # compatibility while avoiding copies. Other things are run through @@ -619,16 +560,14 @@ def equiv(self, other): def repr_part(self): """String usable in a space's ``__repr__`` method.""" optargs = [('weighting', arraynd_repr(self.array, nprint=10), ''), - ('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + ('exponent', self.exponent, 2.0)] return signature_string([], optargs, sep=[',\n', ', ', ',\n'], mod=[[], ['!s', '', '']]) def __repr__(self): """Return ``repr(self)``.""" posargs = [arraynd_repr(self.array)] - optargs = [('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + optargs = [('exponent', self.exponent, 2.0)] inner_str = signature_string(posargs, optargs, sep=[', ', ', ', ',\n'], mod=['!s', '']) @@ -642,7 +581,7 @@ class ConstWeighting(Weighting): """Weighting of a space by a constant.""" - def __init__(self, const, impl, exponent=2.0, dist_using_inner=False): + def __init__(self, const, impl, exponent=2.0): """Initialize a new instance. Parameters @@ -654,19 +593,8 @@ def __init__(self, const, impl, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : bool, optional - Calculate `dist` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re ``. - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. """ - super().__init__(impl=impl, exponent=exponent, - dist_using_inner=dist_using_inner) + super().__init__(impl=impl, exponent=exponent) self.__const = float(const) if self.const <= 0: raise ValueError('expected positive constant, got {}' @@ -720,16 +648,14 @@ def equiv(self, other): def repr_part(self): """String usable in a space's ``__repr__`` method.""" optargs = [('weighting', self.const, 1.0), - ('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + ('exponent', self.exponent, 2.0)] return signature_string([], optargs, - mod=[[], [':.4', '', '']]) + mod=[[], [':.4', '']]) def __repr__(self): """Return ``repr(self)``.""" posargs = [self.const] - optargs = [('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + optargs = [('exponent', self.exponent, 2.0)] return '{}({})'.format(self.__class__.__name__, signature_string(posargs, optargs)) @@ -740,7 +666,7 @@ class NoWeighting(ConstWeighting): """Weighting with constant 1.""" - def __init__(self, impl, exponent=2.0, dist_using_inner=False): + def __init__(self, impl, exponent=2.0): """Initialize a new instance. Parameters @@ -750,29 +676,17 @@ def __init__(self, impl, exponent=2.0, dist_using_inner=False): exponent : positive float, optional Exponent of the norm. For values other than 2.0, the inner product is not defined. - dist_using_inner : bool, optional - Calculate `dist` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re ``. - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - This option can only be used if ``exponent`` is 2.0. """ # Support singleton pattern for subclasses if not hasattr(self, '_initialized'): - ConstWeighting.__init__( - self, const=1.0, impl=impl, exponent=exponent, - dist_using_inner=dist_using_inner) + ConstWeighting.__init__(self, const=1.0, impl=impl, + exponent=exponent) self._initialized = True def __repr__(self): """Return ``repr(self)``.""" posargs = [] - optargs = [('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + optargs = [('exponent', self.exponent, 2.0)] return '{}({})'.format(self.__class__.__name__, signature_string(posargs, optargs)) @@ -783,7 +697,7 @@ class CustomInner(Weighting): """Class for handling a user-specified inner product.""" - def __init__(self, inner, impl, dist_using_inner=False): + def __init__(self, inner, impl): """Initialize a new instance. Parameters @@ -801,19 +715,8 @@ def __init__(self, inner, impl, dist_using_inner=False): impl : string Specifier for the implementation backend. - dist_using_inner : bool, optional - Calculate `dist` using the formula - - ``||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re ``. - - This avoids the creation of new arrays and is thus faster - for large arrays. On the downside, it will not evaluate to - exactly zero for equal (but not identical) ``x`` and ``y``. - - Can only be used if ``exponent`` is 2.0. """ - super().__init__(impl=impl, exponent=2.0, - dist_using_inner=dist_using_inner) + super().__init__(impl=impl, exponent=2.0) if not callable(inner): raise TypeError('`inner` {!r} is not callable' @@ -843,15 +746,13 @@ def __hash__(self): @property def repr_part(self): """String usable in a space's ``__repr__`` method.""" - optargs = [('inner', self.inner, ''), - ('dist_using_inner', self.dist_using_inner, False)] - return signature_string([], optargs, mod=[[], ['!r', '']]) + optargs = [('inner', self.inner, '')] + return signature_string([], optargs, mod=[[], ['!r']]) def __repr__(self): """Return ``repr(self)``.""" posargs = [self.inner] - optargs = [('dist_using_inner', self.dist_using_inner, False)] - inner_str = signature_string(posargs, optargs, mod=['!r', '']) + inner_str = signature_string(posargs, [], mod=['!r']) return '{}({})'.format(self.__class__.__name__, inner_str) @@ -880,7 +781,7 @@ def __init__(self, norm, impl): impl : string Specifier for the implementation backend """ - super().__init__(impl=impl, exponent=1.0, dist_using_inner=False) + super().__init__(impl=impl, exponent=1.0) if not callable(norm): raise TypeError('`norm` {!r} is not callable' @@ -915,15 +816,13 @@ def __hash__(self): def repr_part(self): """Return a string usable in a space's ``__repr__`` method.""" optargs = [('norm', self.norm, ''), - ('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] - return signature_string([], optargs, mod=[[], ['!r', '', '']]) + ('exponent', self.exponent, 2.0)] + return signature_string([], optargs, mod=[[], ['!r', '']]) def __repr__(self): """Return ``repr(self)``.""" posargs = [self.norm] - optargs = [('exponent', self.exponent, 2.0), - ('dist_using_inner', self.dist_using_inner, False)] + optargs = [('exponent', self.exponent, 2.0)] inner_str = signature_string(posargs, optargs, mod=['!r', '']) return '{}({})'.format(self.__class__.__name__, inner_str) @@ -953,7 +852,7 @@ def __init__(self, dist, impl): impl : string Specifier for the implementation backend """ - super().__init__(impl=impl, exponent=1.0, dist_using_inner=False) + super().__init__(impl=impl, exponent=1.0) if not callable(dist): raise TypeError('`dist` {!r} is not callable' diff --git a/odl/test/space/npy_tensors_test.py b/odl/test/space/npy_tensors_test.py index 03407e54f4c..aab5fa9597b 100644 --- a/odl/test/space/npy_tensors_test.py +++ b/odl/test/space/npy_tensors_test.py @@ -904,28 +904,6 @@ def test_array_weighting_dist(tspace, exponent): assert pdist(x, y) == pytest.approx(true_dist, rel=rtol) -def test_array_weighting_dist_using_inner(tspace): - """Test dist using inner product in a weighted space.""" - rtol = np.sqrt(np.finfo(tspace.dtype).resolution) - - [xarr, yarr], [x, y] = noise_elements(tspace, 2) - - weight_arr = _pos_array(tspace) - w = NumpyTensorSpaceArrayWeighting(weight_arr) - - true_dist = np.linalg.norm(np.sqrt(weight_arr) * (xarr - yarr)) - assert w.dist(x, y) == pytest.approx(true_dist, rel=rtol) - - # Only possible for exponent == 2 - with pytest.raises(ValueError): - NumpyTensorSpaceArrayWeighting(weight_arr, exponent=1, - dist_using_inner=True) - - # With free function - w_dist = npy_weighted_dist(weight_arr, use_inner=True) - assert w_dist(x, y) == pytest.approx(true_dist, rel=rtol) - - def test_const_weighting_init(exponent): """Test initialization of constant weightings.""" constant = 1.5 @@ -1030,48 +1008,19 @@ def test_const_weighting_dist(tspace, exponent): assert w_const_dist(x, y) == pytest.approx(true_dist) -def test_const_weighting_dist_using_inner(tspace): - """Test dist with constant weighting using inner product.""" - rtol = np.sqrt(np.finfo(tspace.dtype).resolution) - - [xarr, yarr], [x, y] = noise_elements(tspace, 2) - - constant = 1.5 - w = NumpyTensorSpaceConstWeighting(constant) - - true_dist = np.sqrt(constant) * np.linalg.norm(xarr - yarr) - assert w.dist(x, y) == pytest.approx(true_dist, rel=rtol) - - # Only possible for exponent=2 - with pytest.raises(ValueError): - NumpyTensorSpaceConstWeighting(constant, exponent=1, - dist_using_inner=True) - - # With free function - w_dist = npy_weighted_dist(constant, use_inner=True) - assert w_dist(x, y) == pytest.approx(true_dist, rel=rtol) - - def test_noweight_init(): """Test initialization of trivial weighting.""" w = NumpyTensorSpaceNoWeighting() w_same1 = NumpyTensorSpaceNoWeighting() w_same2 = NumpyTensorSpaceNoWeighting(2) - w_same3 = NumpyTensorSpaceNoWeighting(2, False) - w_same4 = NumpyTensorSpaceNoWeighting(2, dist_using_inner=False) - w_same5 = NumpyTensorSpaceNoWeighting(exponent=2, dist_using_inner=False) w_other_exp = NumpyTensorSpaceNoWeighting(exponent=1) - w_dist_inner = NumpyTensorSpaceNoWeighting(dist_using_inner=True) # Singleton pattern - for same in (w_same1, w_same2, w_same3, w_same4, w_same5): - assert w is same + assert w is w_same1 + assert w is w_same2 # Proper creation - assert w is not w_other_exp - assert w is not w_dist_inner assert w != w_other_exp - assert w != w_dist_inner def test_custom_inner(tspace): @@ -1086,12 +1035,10 @@ def inner(x, y): w = NumpyTensorSpaceCustomInner(inner) w_same = NumpyTensorSpaceCustomInner(inner) w_other = NumpyTensorSpaceCustomInner(np.dot) - w_d = NumpyTensorSpaceCustomInner(inner, dist_using_inner=False) assert w == w assert w == w_same assert w != w_other - assert w != w_d true_inner = inner(xarr, yarr) assert w.inner(x, y) == pytest.approx(true_inner) @@ -1100,9 +1047,7 @@ def inner(x, y): assert w.norm(x) == pytest.approx(true_norm) true_dist = np.linalg.norm((xarr - yarr).ravel()) - # Uses dist_using_inner by default in this case, therefore tolerance assert w.dist(x, y) == pytest.approx(true_dist, rel=rtol) - assert w_d.dist(x, y) == pytest.approx(true_dist) with pytest.raises(TypeError): NumpyTensorSpaceCustomInner(1)