Exemplo n.º 1
0
def test_fourier_trafo_create_temp():

    shape = 10
    space_discr = odl.uniform_discr(0, 1, shape, dtype="complex64")

    ft = FourierTransform(space_discr)
    ft.create_temporaries()
    assert ft._tmp_r is not None
    assert ft._tmp_f is not None

    ift = ft.inverse
    assert ift._tmp_r is not None
    assert ift._tmp_f is not None

    ft.clear_temporaries()
    assert ft._tmp_r is None
    assert ft._tmp_f is None
Exemplo n.º 2
0
def test_fourier_trafo_call(impl, floating_dtype):
    # Test if all variants can be called without error

    # Not supported, skip
    if floating_dtype == np.dtype("float16") and impl == "pyfftw":
        return

    shape = 10
    halfcomplex, _ = _params_from_dtype(floating_dtype)
    space_discr = odl.uniform_discr(0, 1, shape, dtype=floating_dtype)

    ft = FourierTransform(space_discr, impl=impl, halfcomplex=halfcomplex)
    ift = ft.inverse

    one = space_discr.one()
    assert np.allclose(ift(ft(one)), one)

    # With temporaries
    ft.create_temporaries()
    ift = ft.inverse  # shares temporaries
    one = space_discr.one()
    assert np.allclose(ift(ft(one)), one)
Exemplo n.º 3
0
def test_fourier_trafo_scaling():
    # Test if the FT scales correctly

    # Characteristic function of [0, 1], its Fourier transform is
    # given by exp(-1j * y / 2) * sinc(y/2)
    def char_interval(x):
        return (x >= 0) & (x <= 1)

    def char_interval_ft(x):
        return np.exp(-1j * x / 2) * sinc(x / 2) / np.sqrt(2 * np.pi)

    fspace = odl.FunctionSpace(odl.IntervalProd(-2, 2),
                               field=odl.ComplexNumbers())
    discr = odl.uniform_discr_fromspace(fspace, 40, impl='numpy')
    dft = FourierTransform(discr)

    for factor in (2, 1j, -2.5j, 1 - 4j):
        func_true_ft = factor * dft.range.element(char_interval_ft)
        func_dft = dft(factor * fspace.element(char_interval))
        assert (func_dft - func_true_ft).norm() < 1e-6
Exemplo n.º 4
0
def test_fourier_trafo_hat_1d():
    # Hat function as used in linear interpolation. It is not so
    # well discretized by nearest neighbor interpolation, so a larger
    # error is to be expected.
    def hat_func(x):
        out = np.where(x < 0, 1 + x, 1 - x)
        out[x < -1] = 0
        out[x > 1] = 0
        return out

    def hat_func_ft(x):
        return sinc(x / 2)**2 / np.sqrt(2 * np.pi)

    # Using a single-precision implementation, should be as good
    # With linear interpolation in the discretization, should be better?
    discr = odl.uniform_discr(-2, 2, 101, impl='numpy', dtype='float32')
    dft = FourierTransform(discr)
    func_true_ft = dft.range.element(hat_func_ft)
    func_dft = dft(hat_func)
    assert (func_dft - func_true_ft).norm() < 0.001
Exemplo n.º 5
0
def test_fourier_trafo_create_temp():

    shape = 10
    space_discr = odl.uniform_discr(0, 1, shape, dtype='complex64')

    ft = FourierTransform(space_discr)
    ft.create_temporaries()
    assert ft._tmp_r is not None
    assert ft._tmp_f is not None

    ift = ft.inverse
    assert ift._tmp_r is not None
    assert ift._tmp_f is not None

    ft.clear_temporaries()
    assert ft._tmp_r is None
    assert ft._tmp_f is None
Exemplo n.º 6
0
def test_dft_with_known_pairs_2d():

    # Frequency-shifted product of characteristic functions
    def fshift_char_rect(x):
        # Characteristic function of the cuboid
        # [-1, 1] x [1, 2]
        return (x[0] >= -1) & (x[0] <= 1) & (x[1] >= 1) & (x[1] <= 2)

    def fshift_char_rect_ft(x):
        # FT is a product of shifted and frequency-shifted sinc functions
        # 1st comp.: 2 * sinc(y)
        # 2nd comp.: exp(-1j * y * 3/2) * sinc(y/2)
        # Overall factor: (2 * pi)^(-1)
        return (2 * sinc(x[0]) * np.exp(-1j * x[1] * 3 / 2) * sinc(x[1] / 2) /
                (2 * np.pi))

    discr = odl.uniform_discr([-2] * 2, [2] * 2, (100, 400),
                              impl='numpy',
                              dtype='complex64')
    dft = FourierTransform(discr)
    func_true_ft = dft.range.element(fshift_char_rect_ft)
    func_dft = dft(fshift_char_rect)
    assert (func_dft - func_true_ft).norm() < 0.001
Exemplo n.º 7
0
def test_fourier_trafo_inverse(impl, sign):
    # Test if the inverse really is the inverse

    def char_interval(x):
        return (x >= 0) & (x <= 1)

    # Complex-to-complex
    discr = odl.uniform_discr(-2, 2, 40, impl="numpy", dtype="complex64")
    discr_char = discr.element(char_interval)

    ft = FourierTransform(discr, sign=sign, impl=impl)
    assert all_almost_equal(ft.inverse(ft(char_interval)), discr_char)
    assert all_almost_equal(ft.adjoint(ft(char_interval)), discr_char)

    # Half-complex
    discr = odl.uniform_discr(-2, 2, 40, impl="numpy", dtype="float32")
    ft = FourierTransform(discr, impl=impl, halfcomplex=True)
    assert all_almost_equal(ft.inverse(ft(char_interval)), discr_char)

    def char_rect(x):
        return (x[0] >= 0) & (x[0] <= 1) & (x[1] >= 0) & (x[1] <= 1)

    # 2D with axes, C2C
    discr = odl.uniform_discr([-2, -2], [2, 2], (20, 10), impl="numpy", dtype="complex64")
    discr_rect = discr.element(char_rect)

    for axes in [(0,), 1]:
        ft = FourierTransform(discr, sign=sign, impl=impl, axes=axes)
        assert all_almost_equal(ft.inverse(ft(char_rect)), discr_rect)
        assert all_almost_equal(ft.adjoint(ft(char_rect)), discr_rect)

    # 2D with axes, halfcomplex
    discr = odl.uniform_discr([-2, -2], [2, 2], (20, 10), impl="numpy", dtype="float32")
    discr_rect = discr.element(char_rect)

    for halfcomplex in [False, True]:
        if halfcomplex and sign == "+":
            continue  # cannot mix halfcomplex with sign

        for axes in [(0,), (1,)]:
            ft = FourierTransform(discr, sign=sign, impl=impl, axes=axes, halfcomplex=halfcomplex)
            assert all_almost_equal(ft.inverse(ft(char_rect)), discr_rect)
            assert all_almost_equal(ft.adjoint(ft(char_rect)), discr_rect)
Exemplo n.º 8
0
def test_fourier_trafo_init_plan(impl, floating_dtype):

    # Not supported, skip
    if floating_dtype == np.dtype("float16") and impl == "pyfftw":
        return

    shape = 10
    halfcomplex, _ = _params_from_dtype(floating_dtype)

    space_discr = odl.uniform_discr(0, 1, shape, dtype=floating_dtype)

    ft = FourierTransform(space_discr, impl=impl, halfcomplex=halfcomplex)
    if impl != "pyfftw":
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(), ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None

    # With temporaries
    ft.create_temporaries(r=True, f=False)
    if impl != "pyfftw":
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(), ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None

    ft.create_temporaries(r=False, f=True)
    if impl != "pyfftw":
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(), ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None
Exemplo n.º 9
0
def test_fourier_trafo_completely():
    # Complete explicit test of all FT components on two small examples

    # Discretization with 4 points
    discr = odl.uniform_discr(-2, 2, 4, dtype='complex')
    # Interval boundaries -2, -1, 0, 1, 2
    assert np.allclose(discr.partition.cell_boundary_vecs[0],
                       [-2, -1, 0, 1, 2])
    # Grid points -1.5, -0.5, 0.5, 1.5
    assert np.allclose(discr.grid.coord_vectors[0], [-1.5, -0.5, 0.5, 1.5])

    # First test function, symmetric. Can be represented exactly in the
    # discretization.
    def f(x):
        return (x >= -1) & (x <= 1)

    def fhat(x):
        return np.sqrt(2 / np.pi) * sinc(x)

    # Discretize f, check values
    f_discr = discr.element(f)
    assert np.allclose(f_discr, [0, 1, 1, 0])

    # "s" = shifted, "n" = not shifted

    # Reciprocal grids
    recip_s = reciprocal_grid(discr.grid, shift=True)
    recip_n = reciprocal_grid(discr.grid, shift=False)
    assert np.allclose(recip_s.coord_vectors[0],
                       np.linspace(-np.pi, np.pi / 2, 4))
    assert np.allclose(recip_n.coord_vectors[0],
                       np.linspace(-3 * np.pi / 4, 3 * np.pi / 4, 4))

    # Range
    range_part_s = odl.uniform_partition_fromgrid(recip_s)
    range_s = odl.uniform_discr_frompartition(range_part_s, dtype='complex')
    range_part_n = odl.uniform_partition_fromgrid(recip_n)
    range_n = odl.uniform_discr_frompartition(range_part_n, dtype='complex')

    # Pre-processing
    preproc_s = [1, -1, 1, -1]
    preproc_n = [np.exp(1j * 3 / 4 * np.pi * k) for k in range(4)]

    fpre_s = dft_preprocess_data(f_discr, shift=True)
    fpre_n = dft_preprocess_data(f_discr, shift=False)
    assert np.allclose(fpre_s, f_discr * discr.element(preproc_s))
    assert np.allclose(fpre_n, f_discr * discr.element(preproc_n))

    # FFT step, replicating the _call_numpy method
    fft_s = np.fft.fftn(fpre_s, s=discr.shape, axes=[0])
    fft_n = np.fft.fftn(fpre_n, s=discr.shape, axes=[0])
    assert np.allclose(fft_s, [0, -1 + 1j, 2, -1 - 1j])
    assert np.allclose(fft_n, [
        np.exp(1j * np.pi * (3 - 2 * k) / 4) + np.exp(1j * np.pi *
                                                      (3 - 2 * k) / 2)
        for k in range(4)
    ])

    # Interpolation kernel FT
    interp_s = np.sinc(np.linspace(-1 / 2, 1 / 4, 4)) / np.sqrt(2 * np.pi)
    interp_n = np.sinc(np.linspace(-3 / 8, 3 / 8, 4)) / np.sqrt(2 * np.pi)
    assert np.allclose(
        interp_s,
        _interp_kernel_ft(np.linspace(-1 / 2, 1 / 4, 4), interp='nearest'))
    assert np.allclose(
        interp_n,
        _interp_kernel_ft(np.linspace(-3 / 8, 3 / 8, 4), interp='nearest'))

    # Post-processing
    postproc_s = np.exp(1j * np.pi * np.linspace(-3 / 2, 3 / 4, 4))
    postproc_n = np.exp(1j * np.pi * np.linspace(-9 / 8, 9 / 8, 4))

    fpost_s = dft_postprocess_data(range_s.element(fft_s),
                                   real_grid=discr.grid,
                                   recip_grid=recip_s,
                                   shift=[True],
                                   axes=(0, ),
                                   interp='nearest')
    fpost_n = dft_postprocess_data(range_n.element(fft_n),
                                   real_grid=discr.grid,
                                   recip_grid=recip_n,
                                   shift=[False],
                                   axes=(0, ),
                                   interp='nearest')

    assert np.allclose(fpost_s, fft_s * postproc_s * interp_s)
    assert np.allclose(fpost_n, fft_n * postproc_n * interp_n)

    # Comparing to the known result sqrt(2/pi) * sinc(x)
    assert np.allclose(fpost_s, fhat(recip_s.coord_vectors[0]))
    assert np.allclose(fpost_n, fhat(recip_n.coord_vectors[0]))

    # Doing the exact same with direct application of the FT operator
    ft_op_s = FourierTransform(discr, shift=True)
    ft_op_n = FourierTransform(discr, shift=False)
    assert ft_op_s.range.grid == recip_s
    assert ft_op_n.range.grid == recip_n

    ft_f_s = ft_op_s(f)
    ft_f_n = ft_op_n(f)
    assert np.allclose(ft_f_s, fhat(recip_s.coord_vectors[0]))
    assert np.allclose(ft_f_n, fhat(recip_n.coord_vectors[0]))

    # Second test function, asymmetric. Can also be represented exactly in the
    # discretization.
    def f(x):
        return (x >= 0) & (x <= 1)

    def fhat(x):
        return np.exp(-1j * x / 2) * sinc(x / 2) / np.sqrt(2 * np.pi)

    # Discretize f, check values
    f_discr = discr.element(f)
    assert np.allclose(f_discr, [0, 0, 1, 0])

    # Pre-processing
    fpre_s = dft_preprocess_data(f_discr, shift=True)
    fpre_n = dft_preprocess_data(f_discr, shift=False)
    assert np.allclose(fpre_s, [0, 0, 1, 0])
    assert np.allclose(fpre_n, [0, 0, -1j, 0])

    # FFT step
    fft_s = np.fft.fftn(fpre_s, s=discr.shape, axes=[0])
    fft_n = np.fft.fftn(fpre_n, s=discr.shape, axes=[0])
    assert np.allclose(fft_s, [1, -1, 1, -1])
    assert np.allclose(fft_n, [-1j, 1j, -1j, 1j])

    fpost_s = dft_postprocess_data(range_s.element(fft_s),
                                   real_grid=discr.grid,
                                   recip_grid=recip_s,
                                   shift=[True],
                                   axes=(0, ),
                                   interp='nearest')
    fpost_n = dft_postprocess_data(range_n.element(fft_n),
                                   real_grid=discr.grid,
                                   recip_grid=recip_n,
                                   shift=[False],
                                   axes=(0, ),
                                   interp='nearest')

    assert np.allclose(fpost_s, fft_s * postproc_s * interp_s)
    assert np.allclose(fpost_n, fft_n * postproc_n * interp_n)

    # Comparing to the known result exp(-1j*x/2) * sinc(x/2) / sqrt(2*pi)
    assert np.allclose(fpost_s, fhat(recip_s.coord_vectors[0]))
    assert np.allclose(fpost_n, fhat(recip_n.coord_vectors[0]))

    # Doing the exact same with direct application of the FT operator
    ft_f_s = ft_op_s(f)
    ft_f_n = ft_op_n(f)
    assert np.allclose(ft_f_s, fhat(recip_s.coord_vectors[0]))
    assert np.allclose(ft_f_n, fhat(recip_n.coord_vectors[0]))
Exemplo n.º 10
0
def test_fourier_trafo_inverse(impl, sign):
    # Test if the inverse really is the inverse

    def char_interval(x):
        return (x >= 0) & (x <= 1)

    # Complex-to-complex
    discr = odl.uniform_discr(-2, 2, 40, impl='numpy', dtype='complex64')
    discr_char = discr.element(char_interval)

    ft = FourierTransform(discr, sign=sign, impl=impl)
    assert all_almost_equal(ft.inverse(ft(char_interval)), discr_char)
    assert all_almost_equal(ft.adjoint(ft(char_interval)), discr_char)

    # Half-complex
    discr = odl.uniform_discr(-2, 2, 40, impl='numpy', dtype='float32')
    ft = FourierTransform(discr, impl=impl, halfcomplex=True)
    assert all_almost_equal(ft.inverse(ft(char_interval)), discr_char)

    def char_rect(x):
        return (x[0] >= 0) & (x[0] <= 1) & (x[1] >= 0) & (x[1] <= 1)

    # 2D with axes, C2C
    discr = odl.uniform_discr([-2, -2], [2, 2], (20, 10),
                              impl='numpy',
                              dtype='complex64')
    discr_rect = discr.element(char_rect)

    for axes in [(0, ), 1]:
        ft = FourierTransform(discr, sign=sign, impl=impl, axes=axes)
        assert all_almost_equal(ft.inverse(ft(char_rect)), discr_rect)
        assert all_almost_equal(ft.adjoint(ft(char_rect)), discr_rect)

    # 2D with axes, halfcomplex
    discr = odl.uniform_discr([-2, -2], [2, 2], (20, 10),
                              impl='numpy',
                              dtype='float32')
    discr_rect = discr.element(char_rect)

    for halfcomplex in [False, True]:
        if halfcomplex and sign == '+':
            continue  # cannot mix halfcomplex with sign

        for axes in [(0, ), (1, )]:
            ft = FourierTransform(discr,
                                  sign=sign,
                                  impl=impl,
                                  axes=axes,
                                  halfcomplex=halfcomplex)
            assert all_almost_equal(ft.inverse(ft(char_rect)), discr_rect)
            assert all_almost_equal(ft.adjoint(ft(char_rect)), discr_rect)
Exemplo n.º 11
0
def test_fourier_trafo_init_plan(impl, floating_dtype):

    # Not supported, skip
    if floating_dtype == np.dtype('float16') and impl == 'pyfftw':
        return

    shape = 10
    halfcomplex, _ = _params_from_dtype(floating_dtype)

    space_discr = odl.uniform_discr(0, 1, shape, dtype=floating_dtype)

    ft = FourierTransform(space_discr, impl=impl, halfcomplex=halfcomplex)
    if impl != 'pyfftw':
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(),
                      ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None

    # With temporaries
    ft.create_temporaries(r=True, f=False)
    if impl != 'pyfftw':
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(),
                      ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None

    ft.create_temporaries(r=False, f=True)
    if impl != 'pyfftw':
        with pytest.raises(ValueError):
            ft.init_fftw_plan()
        with pytest.raises(ValueError):
            ft.clear_fftw_plan()
    else:
        ft.init_fftw_plan()

        # Make sure plan can be used
        ft._fftw_plan(ft.domain.element().asarray(),
                      ft.range.element().asarray())
        ft.clear_fftw_plan()
        assert ft._fftw_plan is None