Beispiel #1
0
def test_functional_composition(space):
    """Test composition from the right with an operator."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    func = odl.solvers.L2NormSquared(space)

    # Verify that an error is raised if an invalid operator is used
    # (e.g. wrong range)
    scalar = 2.1
    wrong_space = odl.uniform_discr(1, 2, 10)
    op_wrong = odl.operator.ScalingOperator(wrong_space, scalar)

    with pytest.raises(OpTypeError):
        func * op_wrong

    # Test composition with operator from the right
    op = odl.operator.ScalingOperator(space, scalar)
    func_op_comp = func * op
    assert isinstance(func_op_comp, odl.solvers.Functional)

    x = noise_element(space)
    assert func_op_comp(x) == pytest.approx(func(op(x)), rel=rtol)

    # Test gradient and derivative with composition from the right
    assert all_almost_equal(func_op_comp.gradient(x),
                            (op.adjoint * func.gradient * op)(x), ndigits)

    p = noise_element(space)
    assert all_almost_equal(
        func_op_comp.derivative(x)(p),
        (op.adjoint * func.gradient * op)(x).inner(p), ndigits)
Beispiel #2
0
def test_translation_of_functional(space):
    """Test for the translation of a functional: (f(. - y))^*."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    # The translation; an element in the domain
    translation = noise_element(space)

    test_functional = odl.solvers.L2NormSquared(space)
    translated_functional = test_functional.translated(translation)
    x = noise_element(space)

    # Test for evaluation of the functional
    expected_result = test_functional(x - translation)
    assert all_almost_equal(translated_functional(x), expected_result, ndigits)

    # Test for the gradient
    expected_result = test_functional.gradient(x - translation)
    translated_gradient = translated_functional.gradient
    assert all_almost_equal(translated_gradient(x), expected_result, ndigits)

    # Test for proximal
    sigma = 1.2
    # The helper function below is tested explicitly in proximal_utils_test
    expected_result = odl.solvers.proximal_translation(
        test_functional.proximal, translation)(sigma)(x)
    assert all_almost_equal(
        translated_functional.proximal(sigma)(x), expected_result, ndigits)

    # Test for conjugate functional
    # The helper function below is tested explicitly further down in this file
    expected_result = odl.solvers.FunctionalQuadraticPerturb(
        test_functional.convex_conj, linear_term=translation)(x)
    assert all_almost_equal(translated_functional.convex_conj(x),
                            expected_result, ndigits)

    # Test for derivative in direction p
    p = noise_element(space)

    # Explicit computation in point x, in direction p: <x/2 + translation, p>
    expected_result = p.inner(test_functional.gradient(x - translation))
    assert all_almost_equal(
        translated_functional.derivative(x)(p), expected_result, ndigits)

    # Test for optimized implementation, when translating a translated
    # functional
    second_translation = noise_element(space)
    double_translated_functional = translated_functional.translated(
        second_translation)

    # Evaluation
    assert (double_translated_functional(x) == pytest.approx(
        test_functional(x - translation - second_translation), rel=rtol))
Beispiel #3
0
def test_functional_quadratic_perturb(space, linear_term, quadratic_coeff):
    """Test for the functional f(.) + a | . |^2 + <y, .>."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    orig_func = odl.solvers.L2NormSquared(space)

    if linear_term:
        linear_term_arg = None
        linear_term = space.zero()
    else:
        linear_term_arg = linear_term = noise_element(space)

    # Creating the functional ||x||_2^2 and add the quadratic perturbation
    functional = odl.solvers.FunctionalQuadraticPerturb(
        orig_func,
        quadratic_coeff=quadratic_coeff,
        linear_term=linear_term_arg)

    # Create an element in the space, in which to evaluate
    x = noise_element(space)

    # Test for evaluation of the functional
    assert (functional(x) == pytest.approx(
        orig_func(x) + quadratic_coeff * x.inner(x) + x.inner(linear_term),
        rel=rtol))

    # Test for the gradient
    assert all_almost_equal(
        functional.gradient(x),
        orig_func.gradient(x) + 2.0 * quadratic_coeff * x + linear_term,
        ndigits)

    # Test for the proximal operator if it exists
    sigma = 1.2
    # Explicit computation gives
    c = 1 / np.sqrt(2 * sigma * quadratic_coeff + 1)
    prox = orig_func.proximal(sigma * c**2)
    expected_result = prox((x - sigma * linear_term) * c**2)
    assert all_almost_equal(
        functional.proximal(sigma)(x), expected_result, ndigits)

    # Test convex conjugate functional
    if quadratic_coeff == 0:
        expected = orig_func.convex_conj.translated(linear_term)(x)
        assert functional.convex_conj(x) == pytest.approx(expected, rel=rtol)

    # Test proximal of the convex conjugate
    cconj_prox = odl.solvers.proximal_convex_conj(functional.proximal)
    assert all_almost_equal(
        functional.convex_conj.proximal(sigma)(x),
        cconj_prox(sigma)(x), ndigits)
Beispiel #4
0
def test_multiplication_with_vector(space):
    """Test for multiplying a functional with a vector, both left and right."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    x = noise_element(space)
    y = noise_element(space)
    func = odl.solvers.L2NormSquared(space)

    wrong_space = odl.uniform_discr(1, 2, 10)
    y_other_space = noise_element(wrong_space)

    # Multiplication from the right. Make sure it is a
    # FunctionalRightVectorMult
    func_times_y = func * y
    assert isinstance(func_times_y, odl.solvers.FunctionalRightVectorMult)

    expected_result = func(y * x)
    assert func_times_y(x) == pytest.approx(expected_result, rel=rtol)

    # Test for the gradient.
    # Explicit calculations: 2*y*y*x
    expected_result = 2.0 * y * y * x
    assert all_almost_equal(func_times_y.gradient(x), expected_result, ndigits)

    # Test for convex_conj
    cc_func_times_y = func_times_y.convex_conj
    # Explicit calculations: 1/4 * ||x/y||_2^2
    expected_result = 1.0 / 4.0 * (x / y).norm()**2
    assert cc_func_times_y(x) == pytest.approx(expected_result, rel=rtol)

    # Make sure that right muliplication is not allowed with vector from
    # another space
    with pytest.raises(TypeError):
        func * y_other_space

    # Multiplication from the left. Make sure it is a FunctionalLeftVectorMult
    y_times_func = y * func
    assert isinstance(y_times_func, odl.FunctionalLeftVectorMult)

    expected_result = y * func(x)
    assert all_almost_equal(y_times_func(x), expected_result, ndigits)

    # Now, multiplication with vector from another space is ok (since it is the
    # same as scaling that vector with the scalar returned by the functional).
    y_other_times_func = y_other_space * func
    assert isinstance(y_other_times_func, odl.FunctionalLeftVectorMult)

    expected_result = y_other_space * func(x)
    assert all_almost_equal(y_other_times_func(x), expected_result, ndigits)
Beispiel #5
0
def test_left_scalar_mult(space, scalar):
    """Test for right and left multiplication of a functional with a scalar."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    x = noise_element(space)
    func = odl.solvers.functional.L2Norm(space)
    lmul_func = scalar * func

    if scalar == 0:
        assert isinstance(scalar * func, odl.solvers.ZeroFunctional)
        return

    # Test functional evaluation
    assert lmul_func(x) == pytest.approx(scalar * func(x), rel=rtol)

    # Test gradient of left scalar multiplication
    assert all_almost_equal(lmul_func.gradient(x), scalar * func.gradient(x),
                            ndigits)

    # Test derivative of left scalar multiplication
    p = noise_element(space)
    assert all_almost_equal(
        lmul_func.derivative(x)(p),
        scalar * (func.derivative(x))(p), ndigits)

    # Test convex conjugate. This requires positive scaling to work
    pos_scalar = abs(scalar)
    neg_scalar = -pos_scalar

    with pytest.raises(ValueError):
        (neg_scalar * func).convex_conj

    assert all_almost_equal((pos_scalar * func).convex_conj(x),
                            pos_scalar * func.convex_conj(x / pos_scalar),
                            ndigits)

    # Test proximal operator. This requires scaling to be positive.
    sigma = 1.2
    with pytest.raises(ValueError):
        (neg_scalar * func).proximal(sigma)

    assert all_almost_equal((pos_scalar * func).proximal(sigma)(x),
                            func.proximal(sigma * pos_scalar)(x))
Beispiel #6
0
def test_right_scalar_mult(space, scalar):
    """Test for right and left multiplication of a functional with a scalar."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    x = noise_element(space)
    func = odl.solvers.functional.L2NormSquared(space)
    rmul_func = func * scalar

    if scalar == 0:
        # expecting the constant functional x -> func(0)
        assert isinstance(rmul_func, odl.solvers.ConstantFunctional)
        assert all_almost_equal(rmul_func(x), func(space.zero()), ndigits)
        # Nothing more to do, rest is part of ConstantFunctional test
        return

    # Test functional evaluation
    assert rmul_func(x) == pytest.approx(func(scalar * x), rel=rtol)

    # Test gradient of right scalar multiplication
    assert all_almost_equal(rmul_func.gradient(x),
                            scalar * func.gradient(scalar * x), ndigits)

    # Test derivative of right scalar multiplication
    p = noise_element(space)
    assert all_almost_equal(
        rmul_func.derivative(x)(p),
        scalar * func.derivative(scalar * x)(p), ndigits)

    # Test convex conjugate conjugate
    assert all_almost_equal(rmul_func.convex_conj(x),
                            func.convex_conj(x / scalar), ndigits)

    # Test proximal operator
    sigma = 1.2
    assert all_almost_equal(
        rmul_func.proximal(sigma)(x),
        (1.0 / scalar) * func.proximal(sigma * scalar**2)(x * scalar), ndigits)

    # Verify that for linear functionals, left multiplication is used.
    func = odl.solvers.ZeroFunctional(space)
    assert isinstance(func * scalar, odl.solvers.FunctionalLeftScalarMult)
Beispiel #7
0
def test_functional_plus_scalar(space):
    """Test for sum of functioanl and scalar."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    func = odl.solvers.L2NormSquared(space)
    scalar = -1.3

    # Test for scalar not in the field (field of unifor_discr is RealNumbers)
    complex_scalar = 1j
    with pytest.raises(TypeError):
        func + complex_scalar

    func_scalar_sum = func + scalar
    x = noise_element(space)
    p = noise_element(space)

    # Test for evaluation
    assert func_scalar_sum(x) == pytest.approx(func(x) + scalar, rel=rtol)

    # Test for derivative and gradient
    assert all_almost_equal(func_scalar_sum.gradient(x), func.gradient(x),
                            ndigits)

    assert (func_scalar_sum.derivative(x)(p) == pytest.approx(
        func.gradient(x).inner(p), rel=rtol))

    # Test proximal operator
    sigma = 1.2
    assert all_almost_equal(
        func_scalar_sum.proximal(sigma)(x),
        func.proximal(sigma)(x), ndigits)

    # Test convex conjugate
    assert (func_scalar_sum.convex_conj(x) == pytest.approx(
        func.convex_conj(x) - scalar, rel=rtol))

    assert all_almost_equal(func_scalar_sum.convex_conj.gradient(x),
                            func.convex_conj.gradient(x), ndigits)
Beispiel #8
0
def test_functional_sum(space):
    """Test for the sum of two functionals."""
    # Less strict checking for single precision
    ndigits = dtype_ndigits(space.dtype)
    rtol = dtype_tol(space.dtype)

    func1 = odl.solvers.L2NormSquared(space)
    func2 = odl.solvers.L2Norm(space)

    # Verify that an error is raised if one operand is "wrong"
    op = odl.operator.IdentityOperator(space)
    with pytest.raises(OpTypeError):
        func1 + op

    wrong_space = odl.uniform_discr(1, 2, 10)
    func_wrong_domain = odl.solvers.L2Norm(wrong_space)
    with pytest.raises(OpTypeError):
        func1 + func_wrong_domain

    func_sum = func1 + func2
    x = noise_element(space)
    p = noise_element(space)

    # Test functional evaluation
    assert func_sum(x) == pytest.approx(func1(x) + func2(x), rel=rtol)

    # Test gradient and derivative
    assert all_almost_equal(func_sum.gradient(x),
                            func1.gradient(x) + func2.gradient(x), ndigits)

    assert (func_sum.derivative(x)(p) == pytest.approx(
        func1.gradient(x).inner(p) + func2.gradient(x).inner(p), rel=rtol))

    # Verify that proximal raises
    with pytest.raises(NotImplementedError):
        func_sum.proximal

    # Test the convex conjugate raises
    with pytest.raises(NotImplementedError):
        func_sum.convex_conj(x)