# Copyright 2014-2019 The ODL contributors
#
# This file is part of ODL.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
"""Standardized tests for ``LinearSpace``."""
from __future__ import absolute_import, division, print_function
from builtins import object
from copy import copy, deepcopy
from odl.diagnostics.examples import samples
from odl.set import Field
from odl.util.testutils import fail_counter
__all__ = ('SpaceTest',)
def _approx_equal(x, y, eps):
"""Test if elements ``x`` and ``y`` are approximately equal.
``eps`` is a given absolute tolerance.
"""
if x.space != y.space:
return False
if x is y:
return True
try:
return x.dist(y) <= eps
except NotImplementedError:
try:
return x == y
except NotImplementedError:
return False
[docs]class SpaceTest(object):
"""Automated tests for `LinearSpace` instances.
This class allows users to automatically test various features of a
`LinearSpace` such as linearity and vector space operations.
"""
[docs] def __init__(self, space, verbose=True, tol=1e-5):
"""Initialize a new instance.
Parameters
----------
space : `LinearSpace`
Space that should be tested.
verbose : bool, optional
If ``True``, print additional info text.
tol : float, optional
Tolerance parameter used as a base for the actual tolerance
in the tests. Depending on the expected accuracy, the actual
tolerance used in a test can be a factor times this number.
"""
self.space = space
self.verbose = bool(verbose)
self.tol = float(tol)
[docs] def log(self, message):
"""Print message if ``self.verbose == True``."""
if self.verbose:
print(message)
[docs] def element_method(self):
"""Verify `LinearSpace.element`."""
with fail_counter(
test_name='Verifying element method', logger=self.log
) as counter:
try:
elem = self.space.element()
except NotImplementedError:
counter.fail('*** element failed ***')
return
if elem not in self.space:
counter.fail('*** space.element() not in space ***')
[docs] def field(self):
"""Verify `LinearSpace.field`."""
with fail_counter(
test_name='Verifying field property', logger=self.log
) as counter:
try:
field = self.space.field
except NotImplementedError:
counter.fail('*** field failed ***')
return
if not isinstance(field, Field):
counter.fail('*** space.field not a `Field` ***')
return
try:
zero = field.element(0)
except NotImplementedError:
counter.fail('*** field.element(0) failed ***')
zero = None
if zero is not None and zero != 0:
counter.fail('*** field.element(0) != 0 ***')
if zero is not None and zero != 0.0:
counter.fail('*** field.element(0) != 0.0 ***')
try:
one = field.element(1)
except NotImplementedError:
counter.fail('*** field.element(1) failed ***')
one = None
if one is not None and one != 1:
counter.fail('*** field.element(1) != 1 ***')
if one is not None and one != 1.0:
counter.fail('*** field.element(1) != 1.0 ***')
try:
minus_one = field.element(-1)
except NotImplementedError:
counter.fail('field.element(-1) failed')
minus_one = None
if minus_one is not None and minus_one != -1:
counter.fail('field.element(-1) != -1')
if minus_one is not None and minus_one != -1.0:
counter.fail('field.element(-1) != -1.0')
def _associativity_of_addition(self):
"""Verify addition associativity."""
with fail_counter(
test_name='Verifying associativity of addition',
err_msg='error = dist(x + (y + z), (x + y) + z)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [n_z, z] in samples(self.space,
self.space,
self.space):
correct = _approx_equal(x + (y + z), (x + y) + z, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s} z={:25s}'
''.format(n_x, n_y, n_z))
def _commutativity_of_addition(self):
"""Verify addition commutativity."""
with fail_counter(
test_name='Verifying commutativity of addition',
err_msg='error = dist(x + y, y + x)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
correct = _approx_equal(x + y, y + x, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s}'
''.format(n_x, n_y))
def _identity_of_addition(self):
"""Verify additive neutral element ('zero')."""
try:
zero = self.space.zero()
except (AttributeError, NotImplementedError):
print('*** SPACE HAS NO ZERO VECTOR ***')
return
with fail_counter(
test_name='Verifying identity element of addition',
err_msg='error = dist(x + 0, x)',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
correct = _approx_equal(x + zero, x, self.tol)
if not correct:
counter.fail('failed with x={:25s}'.format(n_x))
def _inverse_element_of_addition(self):
"""Verify additive inverse."""
try:
zero = self.space.zero()
except (AttributeError, NotImplementedError):
print('*** SPACE HAS NO ZERO VECTOR ***')
return
with fail_counter(
test_name='Verifying inverse element of addition',
err_msg='error = dist(x + (-x), 0)',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
correct = _approx_equal(x + (-x), zero, self.tol)
if not correct:
counter.fail('failed with x={:25s}'.format(n_x))
def _commutativity_of_scalar_mult(self):
"""Verify scalar multiplication commutativity."""
with fail_counter(
test_name='Verifying commutativity of scalar multiplication',
err_msg='error = dist(a * (b * x), (a * b) * x)',
logger=self.log
) as counter:
for [n_x, x], [_, a], [_, b] in samples(self.space,
self.space.field,
self.space.field):
correct = _approx_equal(a * (b * x), (a * b) * x, self.tol)
if not correct:
counter.fail('failed with x={:25s}, a={}, b={}'
''.format(n_x, a, b))
def _identity_of_mult(self):
"""Verify multiplicative neutral element ('one')."""
with fail_counter(
test_name='Verifying identity element of multiplication',
err_msg='error = dist(1 * x, x)',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
correct = _approx_equal(1 * x, x, self.tol)
if not correct:
counter.fail('failed with x={:25s}'.format(n_x))
def _distributivity_of_mult_vector(self):
"""Verify scalar multiplication distributivity wrt vector addition."""
with fail_counter(
test_name='Verifying distributivity of scalar multiplication '
'under vector addition',
err_msg='error = dist(a * (x + y), a * x + a * y)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [_, a] in samples(self.space,
self.space,
self.space.field):
correct = _approx_equal(a * (x + y), a * x + a * y, self.tol)
if not correct:
counter.fail('failed with x={:25s}, y={:25s}, a={}'
''.format(n_x, n_y, a))
def _distributivity_of_mult_scalar(self):
"""Verify scalar multiplication distributivity wrt scalar addition."""
with fail_counter(
test_name='Verifying distributivity of scalar multiplication '
'under scalar addition',
err_msg='error = dist((a + b) * x, a * x + b * x)',
logger=self.log
) as counter:
for [n_x, x], [_, a], [_, b] in samples(self.space,
self.space.field,
self.space.field):
correct = _approx_equal((a + b) * x, a * x + b * x, self.tol)
if not correct:
counter.fail('failed with x={:25s}, a={}, b={}'
''.format(n_x, a, b))
def _subtraction(self):
"""Verify element subtraction as addition of additive inverse."""
with fail_counter(
test_name='Verifying element subtraction',
err_msg='error = dist(x - y, x + (-1 * y))',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
correct = (_approx_equal(x - y, x + (-1 * y), self.tol) and
_approx_equal(x - y, x + (-y), self.tol))
if not correct:
counter.fail('failed with x={:25s}, y={:25s}'
''.format(n_x, n_y))
def _division(self):
"""Verify scalar division as multiplication with mult. inverse."""
with fail_counter(
test_name='Verifying scalar division',
err_msg='error = dist(x / a, x * (1/a))',
logger=self.log
) as counter:
for [n_x, x], [_, a] in samples(self.space, self.space.field):
if a != 0:
correct = _approx_equal(x / a, x * (1.0 / a), self.tol)
if not correct:
counter.fail('failed with x={:25s}, a={}'
''.format(n_x, a))
def _lincomb_aliased(self):
"""Verify several scenarios of aliased linear combination."""
with fail_counter(
test_name='Verifying linear combination with aliased input',
err_msg='error = dist(aliased, non-aliased)',
logger=self.log
) as counter:
for [n_x, x_in], [n_y, y] in samples(self.space, self.space):
x = x_in.copy()
x.lincomb(1, x, 1, y)
correct = _approx_equal(x, x_in + y, self.tol)
if not correct:
counter.fail('failed with x.lincomb(1, x, 1, y),'
'x={:25s} y={:25s} '
''.format(n_x, n_y))
x = x_in.copy()
x.lincomb(1, x, 1, x)
correct = _approx_equal(x, x_in + x_in, self.tol)
if not correct:
counter.fail('failed with x.lincomb(1, x, 1, x),'
'x={:25s} '
''.format(n_x))
[docs] def _lincomb(self):
"""Verify linear combination."""
self.log('\nTesting lincomb')
self._lincomb_aliased()
[docs] def linearity(self):
"""Verify the linear space properties by examples.
These properties include things such as associativity
``x + y = y + x``
and identity of the ``LinearSpace.zero`` element
``x + 0 = x``
References
----------
Wikipedia article on `Vector space`_.
.. _Vector space: https://en.wikipedia.org/wiki/Vector_space
"""
self.log('\n== Verifying linear space properties ==\n')
self._associativity_of_addition()
self._commutativity_of_addition()
self._identity_of_addition()
self._inverse_element_of_addition()
self._commutativity_of_scalar_mult()
self._identity_of_mult()
self._distributivity_of_mult_vector()
self._distributivity_of_mult_scalar()
self._subtraction()
self._division()
self._lincomb()
def _inner_linear_scalar(self):
"""Verify homogeneity of the inner product in the first argument."""
with fail_counter(
test_name='Verifying homogeneity of the inner product in the '
'first argument',
err_msg='error = |<a*x, y> - a*<x, y>|',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [_, a] in samples(self.space,
self.space,
self.space.field):
error = abs((a * x).inner(y) - a * x.inner(y))
if error > self.tol:
counter.fail('x={:25s}, y={:25s}, a={}: error={}'
''.format(n_x, n_y, a, error))
def _inner_conjugate_symmetry(self):
"""Verify conjugate symmetry of the inner product."""
with fail_counter(
test_name='Verifying conjugate symmetry of the inner product',
err_msg='error = |<x, y> - <y, x>.conj()|',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
error = abs((x).inner(y) - y.inner(x).conjugate())
if error > self.tol:
counter.fail('x={:25s}, y={:25s}: error={}'
''.format(n_x, n_y, error))
def _inner_linear_sum(self):
"""Verify distributivity of the inner product in the first argument."""
with fail_counter(
test_name='Verifying distributivity of the inner product'
'in the first argument',
err_msg='error = |<x+y, z> - (<x, z> + <y, z>)|',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [n_z, z] in samples(self.space,
self.space,
self.space):
error = abs((x + y).inner(z) - (x.inner(z) + y.inner(z)))
if error > self.tol:
counter.fail('x={:25s}, y={:25s}, z={:25s}: error={}'
''.format(n_x, n_y, n_z, error))
def _inner_positive(self):
"""Verify positive definiteness of the inner product."""
with fail_counter(
test_name='Verifying positive definiteness of the inner '
'product',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
inner = x.inner(x)
if abs(inner.imag) > self.tol:
counter.fail('<x, x>.imag != 0, x={:25s}, <x, x>.imag = {}'
''.format(n_x, inner.imag))
if n_x == 'Zero' and inner.real != 0:
counter.fail('<0, 0> != 0.0, x={:25s}: <0, 0>={}'
''.format(n_x, inner))
elif n_x != 'Zero' and inner.real <= 0:
counter.fail('<x, x> <= 0, x={:25s}: <x, x>={}'
''.format(n_x, inner))
[docs] def inner(self):
"""Verify ``LinearSpace.inner``.
The inner product is checked for the following properties:
- conjugate symmetry:
``<x, y> = <y, x>^*`` (^* complex conjugate)
- linearity:
``<a * x, y> = a * <x, y>``
``<x + y, z> = <x, z> + <y, z>``
- positive definiteness:
``<x, x> >= 0``
References
----------
Wikipedia article on `inner product`_.
.. _inner product: https://en.wikipedia.org/wiki/Inner_product_space
"""
self.log('\n== Verifying inner product ==\n')
try:
zero = self.space.zero()
zero.inner(zero)
except NotImplementedError:
self.log('Space has no inner product')
return
self._inner_conjugate_symmetry()
self._inner_linear_scalar()
self._inner_linear_sum()
self._inner_positive()
def _norm_positive(self):
"""Verify positive definiteness of the norm."""
with fail_counter(
test_name='Verifying positive definiteness of the norm',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
norm = x.norm()
if n_x == 'Zero' and norm != 0:
counter.fail('||0|| != 0.0, x={:25s}: ||x||={}'
''.format(n_x, norm))
elif n_x != 'Zero' and norm <= 0:
counter.fail('||x|| <= 0, x={:25s}: ||x||={}'
''.format(n_x, norm))
def _norm_subadditive(self):
"""Verify subadditivity of the norm."""
with fail_counter(
test_name='Verifying sub-additivity of the norm',
err_msg='error = max(||x+y|| - (||x|| + ||y||), 0)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
norm_x = x.norm()
norm_y = y.norm()
norm_xy = (x + y).norm()
error = norm_xy - norm_x - norm_y
if error > 0:
counter.fail('x={:25s} y={:25s}: error={}'
''.format(n_x, n_y, error))
def _norm_homogeneity(self):
"""Verify positive homogeneity of the norm."""
with fail_counter(
test_name='Verifying positive homogeneity of the norm',
err_msg='error = | ||a*x|| - |a|*||x|| |',
logger=self.log
) as counter:
for [n_x, x], [_, a] in samples(self.space, self.space.field):
error = abs((a * x).norm() - abs(a) * x.norm())
if error > self.tol:
counter.fail('x={:25s} a={}: error={}'
''.format(n_x, a, error))
def _norm_inner_compatible(self):
"""Verify compatibility of norm and inner product."""
try:
zero = self.space.zero()
zero.inner(zero)
except NotImplementedError:
self.log('Space has no inner product')
return
with fail_counter(
test_name='Verifying compatibility of norm and inner product',
err_msg='error = | ||x||^2 - <x, x> |',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
error = abs(x.norm() ** 2 - x.inner(x))
if error > self.tol:
counter.fail('x={:25s}: error={}'
''.format(n_x, error))
[docs] def norm(self):
"""Verify ``LinearSpace.norm``.
The norm is checked for the following properties:
- linearity:
``||a * x|| = |a| * ||x||``
- subadditivity:
``||x + y|| <= ||x|| + ||y||``
- positive homogeneity:
``||a * x|| = |a| * ||x||``
- positive definiteness:
``||x|| >= 0``
``||x|| = 0`` iff ``x = 0``
- compatibility with the inner product (if available):
``||x||^2 = <x, x>``
References
----------
Wikipedia article on norm_.
.. _norm: https://en.wikipedia.org/wiki/Norm_(mathematics)
"""
self.log('\n== Verifying norm ==\n')
try:
self.space.zero().norm()
except NotImplementedError:
self.log('Space has no norm')
return
self._norm_positive()
self._norm_subadditive()
self._norm_homogeneity()
self._norm_inner_compatible()
def _dist_positivity(self):
"""Verify nonnegativity of the distance."""
with fail_counter(
test_name='Verifying nonnegativity of the distance',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
dist = x.dist(y)
if n_x == n_y and dist != 0:
counter.fail('d(x, x) != 0.0, x={:25s}: dist={}'
''.format(n_x, dist))
elif n_x != n_y and dist <= 0:
counter.fail('d(x, y) <= 0, x={:25s} y={:25s}: dist={}'
''.format(n_x, n_y, dist))
def _dist_symmetric(self):
"""Verify symmetry of the distance."""
with fail_counter(
test_name='Verifying symmetry of the distance',
err_msg='error = |d(x, y) - d(y, x)|',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space, self.space):
dist_1 = x.dist(y)
dist_2 = y.dist(x)
error = abs(dist_1 - dist_2)
if error > self.tol:
counter.fail('x={:25s}, y={:25s}: error={}'
''.format(n_x, n_y, error))
def _dist_subtransitive(self):
"""Verify sub-transitivity of the distance."""
with fail_counter(
test_name='Verifying sub-additivity of the distance',
err_msg='error = max(d(x,z) - (d(x, y) + d(y, z)), 0)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [n_z, z] in samples(self.space,
self.space,
self.space):
dxz = x.dist(z)
dxy = x.dist(y)
dyz = y.dist(z)
error = dxz - (dxy + dyz)
if error > self.tol:
counter.fail('x={:25s}, y={:25s}, z={:25s}: error={}'
''.format(n_x, n_y, n_z, error))
def _dist_norm_compatible(self):
"""Verify compatibility of distance and norm."""
try:
self.space.zero().norm()
except NotImplementedError:
self.log('Space has no norm')
return
with fail_counter(
test_name='Verifying compatibility of distance and norm',
err_msg='error = |d(x, y) - ||x-y|| |',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space,
self.space):
error = abs(x.dist(y) - (x - y).norm())
if error > self.tol:
counter.fail('x={:25s}, y={:25s}: error={}'
''.format(n_x, n_y, error))
[docs] def dist(self):
"""Verify ``LinearSpace.dist``.
The distance metric is checked for the following properties:
- positive definiteness:
``d(x, y) >= 0``
``d(x, y) = 0`` iff ``x = y``
- symmetry:
``d(x, y) = d(y, x)``
- sub-transitivity:
``d(x, z) <= d(x, y) + d(y, z)``
- compatibility with the norm (if available):
``d(x, y) = ||x - y||``
References
----------
Wikipedia article on metric_
.. _metric: https://en.wikipedia.org/wiki/Metric_(mathematics)
"""
self.log('\n== Verifying dist ==\n')
try:
zero = self.space.zero()
self.space.dist(zero, zero)
except NotImplementedError:
self.log('Space has no distance metric')
return
self._dist_positivity()
self._dist_symmetric()
self._dist_subtransitive()
self._dist_norm_compatible()
def _multiply_zero(self):
"""Verify that vector multiplication with zero is zero."""
try:
zero = self.space.zero()
except NotImplementedError:
print('*** SPACE HAS NO ZERO VECTOR ***')
return
with fail_counter(
test_name='Verifying vector multiplication with zero',
err_msg='error = ||x * 0||',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
error = (zero * x).norm()
if error > self.tol:
counter.fail('x={:25s},: error={}'
''.format(n_x, error))
def _multiply_commutative(self):
"""Verify commutativity of vector multiplication."""
with fail_counter(
test_name='Verifying commutativity of vector multiplication',
err_msg='error = dist(x * y, y * x)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], _ in samples(self.space,
self.space,
self.space):
correct = _approx_equal(x * y, y * x, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s}'
''.format(n_x, n_y))
def _multiply_associative(self):
"""Verify associativity of vector multiplication."""
with fail_counter(
test_name='Verifying associativity of vector multiplication',
err_msg='error = dist(x * (y * z), (x * y) * z)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [n_z, z] in samples(self.space,
self.space,
self.space):
correct = _approx_equal(x * (y * z), (x * y) * z, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s} z={:25s}'
''.format(n_x, n_y, n_z))
def _multiply_distributive_scalar(self):
"""Verify distributivity of scalar multiplication."""
with fail_counter(
test_name='Verifying distributivity of vector multiplication '
'under scalar multiplication',
err_msg='error = dist(a * (x + y), a * x + a * y)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [_, a] in samples(self.space,
self.space,
self.space.field):
correct = _approx_equal(a * (x + y), a * x + a * y, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s} a={}'
''.format(n_x, n_y, a))
def _multiply_distributive_vector(self):
"""Verify distributivity of vector multiplication."""
with fail_counter(
test_name='Verifying distributivity of vector multiplication '
'under vector multiplication',
err_msg='error = dist(x * (y + z), x * y + x * z)',
logger=self.log
) as counter:
for [n_x, x], [n_y, y], [n_z, z] in samples(self.space,
self.space,
self.space):
correct = _approx_equal(x * (y + z), x * y + x * z, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s} z={:25s}'
''.format(n_x, n_y, n_z))
[docs] def multiply(self):
"""Verify ``LinearSpace.multiply``.
The vector multiplication is checked for the following properties:
- Zero element:
``0 * x = 0``
- Commutativity:
``x * y = y * x``
- Associativity:
``x * (y * z) = (x * y) * z``
- Distributivity:
``a * (x + y) = a * x + a * y``
``x * (y + z) = x * y + x * z``
"""
self.log('\n== Verifying multiplication ==\n')
try:
zero = self.space.zero()
except NotImplementedError:
print('*** SPACE HAS NO ZERO VECTOR ***')
return
try:
self.space.multiply(zero, zero)
except NotImplementedError:
self.log('Space has no vector multiplication.')
return
self._multiply_zero()
self._multiply_commutative()
self._multiply_associative()
self._multiply_distributive_scalar()
self._multiply_distributive_vector()
[docs] def equals(self):
"""Verify `LinearSpace.__eq__`."""
self.log('\n== Verifying __eq__ ==\n')
if not self.space == self.space:
print('** space == space failed ***')
if self.space != self.space:
print('** not space != space failed***')
if self.space != copy(self.space):
print('** space == copy(space) failed***')
if self.space != deepcopy(self.space):
print('** space == deepcopy(space) failed***')
with fail_counter(
test_name='Verify behavior of `space == obj` when `obj` '
'is not a space',
logger=self.log
) as counter:
for obj in [[1, 2], list(), tuple(), dict(), 5.0]:
if self.space == obj:
counter.fail('space == obj, with obj={}'
''.format(obj))
if not self.space != obj:
counter.fail('not space != obj, with obj={}'
''.format(obj))
[docs] def contains(self):
"""Verify `LinearSpace.__contains__`."""
with fail_counter(
test_name='Verify behavior of `obj in space`',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
if x not in self.space:
counter.fail('x not in space, with x={}'
''.format(n_x))
if x not in self.space:
counter.fail('not x in space, with x={}'
''.format(n_x))
for obj in [[1, 2], list(), tuple(), dict(), 5.0]:
if obj in self.space:
counter.fail('obj in space, with obj={}'
''.format(obj))
if not obj not in self.space:
counter.fail('not obj not in space, with obj={}'
''.format(obj))
[docs] def element_assign(self):
"""Verify `LinearSpaceElement.assign`."""
with fail_counter(
test_name='Verify behavior of `LinearSpaceElement.assign`',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space,
self.space):
x.assign(y)
correct = _approx_equal(x, y, self.tol)
if not correct:
counter.fail('failed with x={:25s} y={:25s}'
''.format(n_x, n_y))
[docs] def element_copy(self):
"""Verify `LinearSpaceElement.copy`."""
with fail_counter(
test_name='Verify behavior of `LinearSpaceElement.copy`',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
# equal after copy
y = x.copy()
correct = _approx_equal(x, y, self.tol)
if not correct:
counter.fail('failed with x={:s5s}'
''.format(n_x))
# modify y, x stays the same
y *= 2.0
correct = n_x == 'Zero' or not _approx_equal(x, y, self.tol)
if not correct:
counter.fail('modified y, x changed with x={:25s}'
''.format(n_x))
[docs] def element_set_zero(self):
"""Verify `LinearSpaceElement.set_zero`."""
try:
zero = self.space.zero()
except NotImplementedError:
print('*** SPACE HAS NO ZERO VECTOR ***')
return
with fail_counter(
test_name='Verify behavior of `LinearSpaceElement.set_zero`',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
x.set_zero()
correct = _approx_equal(x, zero, self.tol)
if not correct:
counter.fail('failed with x={:25s}'
''.format(n_x))
[docs] def element_equals(self):
"""Verify `LinearSpaceElement.__eq__`."""
try:
zero = self.space.zero()
except NotImplementedError:
print('*** SPACE HAS NO ZERO VECTOR ***')
return
try:
zero == zero
except NotImplementedError:
self.log('Vector has no __eq__')
return
with fail_counter(
test_name='Verify behavior of `element1 == element2`',
logger=self.log
) as counter:
for [n_x, x], [n_y, y] in samples(self.space,
self.space):
if n_x == n_y:
if not x == y:
counter.fail('failed x == x with x={:25s}'
''.format(n_x))
if x != y:
counter.fail('failed not x != x with x={:25s}'
''.format(n_x))
else:
if x == y:
counter.fail('failed not x == y with x={:25s}, '
'x={:25s}'.format(n_x, n_y))
if not x != y:
counter.fail('failed x != y with x={:25s}, x={:25s}'
''.format(n_x, n_y))
[docs] def element_space(self):
"""Verify `LinearSpaceElement.space`."""
with fail_counter(
test_name='Verify `LinearSpaceElement.space`',
logger=self.log
) as counter:
for [n_x, x] in samples(self.space):
if x.space != self.space:
counter.fail('failed with x={:25s}'.format(n_x))
[docs] def element(self):
"""Verify `LinearSpaceElement`."""
self.log('\n== Verifying element attributes ==\n')
self.element_assign()
self.element_copy()
self.element_set_zero()
self.element_equals()
self.element_space()
[docs] def run_tests(self):
"""Run all tests on this space."""
self.log('\n== RUNNING ALL TESTS ==\n')
self.log('Space = {}'.format(self.space))
self.field()
self.element_method()
self.linearity()
self.inner()
self.norm()
self.dist()
self.multiply()
self.equals()
self.contains()
self.element()
def __str__(self):
"""Return ``str(self)``."""
return '{}({})'.format(self.__class__.__name__, self.space)
def __repr__(self):
"""Return ``repr(self)``."""
inner_str = '{!r}'.format(self.space)
if not self.verbose:
inner_str += ', verbose=False'
if self.tol != 1e-5:
inner_str += ', tol={}'.format(self.tol)
return '{}({})'.format(self.__class__.__name__, inner_str)
if __name__ == '__main__':
from odl import rn, uniform_discr
SpaceTest(rn(10), verbose=False).run_tests()
SpaceTest(uniform_discr([0, 0], [1, 1], [5, 5])).run_tests()