From 695c24db0c4f83560175a9d8099e27bbe2775dff 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 | 132 ++++------------------- odl/space/weighting.py | 161 ++++++----------------------- odl/test/space/npy_tensors_test.py | 59 +---------- 4 files changed, 72 insertions(+), 413 deletions(-) diff --git a/odl/space/npy_tensors.py b/odl/space/npy_tensors.py index 7514c7bf74d..70a14c3886d 100644 --- a/odl/space/npy_tensors.py +++ b/odl/space/npy_tensors.py @@ -122,8 +122,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 @@ -150,19 +148,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. @@ -234,7 +219,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))): @@ -259,15 +243,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): @@ -293,8 +271,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): @@ -1694,7 +1657,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 @@ -1706,16 +1669,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 ----- @@ -1762,13 +1715,11 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): is not checked during initialization. """ super(NumpyTensorSpaceArrayWeighting, self).__init__( - array, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + 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``. @@ -1823,7 +1774,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 @@ -1833,16 +1784,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 ----- @@ -1884,8 +1825,7 @@ def __init__(self, const, exponent=2.0, dist_using_inner=False): inner product or norm, respectively. """ super(NumpyTensorSpaceConstWeighting, self).__init__( - const, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + const, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Return the weighted inner product of ``x1`` and ``x2``. @@ -1942,13 +1882,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)) @@ -1969,17 +1903,11 @@ 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: inst = super().__new__(cls, *args, **kwargs) cls.__instance = inst @@ -1987,7 +1915,7 @@ def __new__(cls, *args, **kwargs): 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 @@ -1995,26 +1923,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 @@ -2028,18 +1945,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 56f97dca2d2..5488d03b580 100644 --- a/odl/space/pspace.py +++ b/odl/space/pspace.py @@ -95,8 +95,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`` @@ -128,21 +126,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 @@ -218,7 +201,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)) @@ -269,7 +251,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 @@ -280,7 +262,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)) @@ -292,8 +274,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): @@ -1034,7 +1015,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 @@ -1044,16 +1025,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 ----- @@ -1091,8 +1062,7 @@ def __init__(self, array, exponent=2.0, dist_using_inner=False): during initialization. """ super(ProductSpaceArrayWeighting, self).__init__( - array, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + array, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Calculate the array-weighted inner product of two elements. @@ -1155,7 +1125,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 @@ -1165,16 +1135,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 ----- @@ -1210,8 +1170,7 @@ def __init__(self, constant, exponent=2.0, dist_using_inner=False): inner product or norm, respectively. """ super(ProductSpaceConstWeighting, self).__init__( - constant, impl='numpy', exponent=exponent, - dist_using_inner=dist_using_inner) + constant, impl='numpy', exponent=exponent) def inner(self, x1, x2): """Calculate the constant-weighted inner product of two elements. @@ -1278,36 +1237,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): @@ -1321,17 +1259,11 @@ 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: parent = super(NoWeighting, ProductSpaceNoWeighting) cls._instance = parent.__new__(cls, *args, **kwargs) @@ -1340,7 +1272,7 @@ def __new__(cls, *args, **kwargs): return super(NoWeighting, ProductSpaceNoWeighting).__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 @@ -1348,26 +1280,16 @@ 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(ProductSpaceNoWeighting, self).__init__( - impl='numpy', exponent=exponent, dist_using_inner=dist_using_inner) + 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 @@ -1382,21 +1304,9 @@ 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(ProductSpaceCustomInner, self).__init__( - impl='numpy', inner=inner, - dist_using_inner=dist_using_inner) + impl='numpy', inner=inner) class ProductSpaceCustomNorm(CustomNorm): diff --git a/odl/space/weighting.py b/odl/space/weighting.py index 0b6ddf78e38..b6b66622676 100644 --- a/odl/space/weighting.py +++ b/odl/space/weighting.py @@ -36,7 +36,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 @@ -46,28 +46,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): @@ -79,11 +63,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``. @@ -101,13 +80,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. @@ -172,14 +149,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): @@ -194,8 +164,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 @@ -209,16 +178,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`` @@ -259,8 +218,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(MatrixWeighting, self).__init__( - impl=impl, exponent=exponent, dist_using_inner=dist_using_inner) + super(MatrixWeighting, self).__init__(impl=impl, exponent=exponent) # Check and set matrix if scipy.sparse.isspmatrix(matrix): @@ -478,8 +436,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): @@ -494,16 +450,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' @@ -533,7 +485,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 @@ -546,19 +498,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(ArrayWeighting, self).__init__( - impl=impl, exponent=exponent, dist_using_inner=dist_using_inner) + super(ArrayWeighting, self).__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 @@ -633,16 +574,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', '']) @@ -658,7 +597,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 @@ -670,19 +609,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(ConstWeighting, self).__init__( - impl=impl, exponent=exponent, dist_using_inner=dist_using_inner) + super(ConstWeighting, self).__init__(impl=impl, exponent=exponent) self.__const = float(const) if self.const <= 0: @@ -737,16 +665,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)) @@ -759,7 +685,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 @@ -769,29 +695,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)) @@ -804,7 +718,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 @@ -822,19 +736,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(CustomInner, self).__init__( - impl=impl, exponent=2.0, dist_using_inner=dist_using_inner) + super(CustomInner, self).__init__(impl=impl, exponent=2.0) if not callable(inner): raise TypeError('`inner` {!r} is not callable' @@ -865,15 +768,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) @@ -902,8 +803,7 @@ def __init__(self, norm, impl): impl : string Specifier for the implementation backend """ - super(CustomNorm, self).__init__( - impl=impl, exponent=1.0, dist_using_inner=False) + super(CustomNorm, self).__init__(impl=impl, exponent=1.0) if not callable(norm): raise TypeError('`norm` {!r} is not callable' @@ -939,15 +839,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) @@ -977,8 +875,7 @@ def __init__(self, dist, impl): impl : string Specifier for the implementation backend """ - super(CustomDist, self).__init__( - impl=impl, exponent=1.0, dist_using_inner=False) + super(CustomDist, self).__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 7f74e1a3600..926b947d98e 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)