Example #1
0
def test_reciprocal_grid_nd_halfcomplex():

    grid = odl.uniform_grid([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    stride_last = 2 * np.pi / (s[-1] * n[-1])
    n[-1] = n[-1] // 2 + 1

    # Without shift
    rgrid = reciprocal_grid(grid, shift=False, halfcomplex=True)
    assert all_equal(rgrid.shape, n)
    assert rgrid.max_pt[-1] == 0  # last dim is odd

    # With shift
    rgrid = reciprocal_grid(grid, shift=True, halfcomplex=True)
    assert all_equal(rgrid.shape, n)
    assert rgrid.max_pt[-1] == -stride_last / 2

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=True,
                            halfcx_parity='odd')
    assert irgrid.approx_equals(grid, atol=1e-6)

    with pytest.raises(ValueError):
        realspace_grid(rgrid, grid.min_pt, halfcomplex=True,
                       halfcx_parity='+')
Example #2
0
def test_reciprocal_grid_nd_halfcomplex():

    grid = odl.uniform_sampling([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    stride_last = 2 * np.pi / (s[-1] * n[-1])
    n[-1] = n[-1] // 2 + 1

    # Without shift
    rgrid = reciprocal_grid(grid, shift=False, halfcomplex=True)
    assert all_equal(rgrid.shape, n)
    assert rgrid.max_pt[-1] == 0  # last dim is odd

    # With shift
    rgrid = reciprocal_grid(grid, shift=True, halfcomplex=True)
    assert all_equal(rgrid.shape, n)
    assert rgrid.max_pt[-1] == -stride_last / 2

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=True,
                            halfcx_parity='odd')
    assert irgrid.approx_equals(grid, atol=1e-6)

    with pytest.raises(ValueError):
        realspace_grid(rgrid, grid.min_pt, halfcomplex=True,
                       halfcx_parity='+')
Example #3
0
def test_fourier_trafo_range(exponent, odl_floating_dtype):
    # Check if the range is initialized correctly. Encompasses the init test
    dtype = odl_floating_dtype

    # Testing R2C for real dtype, else C2C

    # 1D
    shape = 10
    space_discr = odl.uniform_discr(0,
                                    1,
                                    shape,
                                    exponent=exponent,
                                    impl='numpy',
                                    dtype=dtype)

    dft = FourierTransform(space_discr, halfcomplex=True, shift=True)
    assert dft.range.field == odl.ComplexNumbers()
    halfcomplex = True if is_real_dtype(dtype) else False
    assert dft.range.grid == reciprocal_grid(dft.domain.grid,
                                             halfcomplex=halfcomplex,
                                             shift=True)
    assert dft.range.exponent == conj_exponent(exponent)

    # 3D
    shape = (3, 4, 5)
    space_discr = odl.uniform_discr([0] * 3, [1] * 3,
                                    shape,
                                    exponent=exponent,
                                    impl='numpy',
                                    dtype=dtype)

    dft = FourierTransform(space_discr, halfcomplex=True, shift=True)
    assert dft.range.field == odl.ComplexNumbers()
    halfcomplex = True if is_real_dtype(dtype) else False
    assert dft.range.grid == reciprocal_grid(dft.domain.grid,
                                             halfcomplex=halfcomplex,
                                             shift=True)
    assert dft.range.exponent == conj_exponent(exponent)

    # shift must be True in the last axis
    if halfcomplex:
        with pytest.raises(ValueError):
            FourierTransform(space_discr, shift=(True, True, False))

    if exponent != 2.0:
        with pytest.raises(NotImplementedError):
            dft.adjoint

    with pytest.raises(TypeError):
        FourierTransform(dft.domain.partition)
Example #4
0
def test_reciprocal_grid_nd_axes():

    grid = odl.uniform_sampling([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    axes_list = [[1, -1], [0], 0, [0, 2, 1], [2, 0]]

    for axes in axes_list:
        active = np.zeros(grid.ndim, dtype=bool)
        active[axes] = True
        inactive = np.logical_not(active)

        true_recip_stride = np.empty(grid.ndim)
        true_recip_stride[active] = 2 * np.pi / (s[active] * n[active])
        true_recip_stride[inactive] = s[inactive]

        # Without shift altogether
        rgrid = reciprocal_grid(grid, shift=False, axes=axes,
                                halfcomplex=False)

        assert all_equal(rgrid.shape, n)
        assert all_almost_equal(rgrid.stride, true_recip_stride)
        assert all_almost_equal(rgrid.min_pt[active], -rgrid.max_pt[active])
        assert all_equal(rgrid.min_pt[inactive], grid.min_pt[inactive])
        assert all_equal(rgrid.max_pt[inactive], grid.max_pt[inactive])

        # Inverting should give back the original
        irgrid = realspace_grid(rgrid, grid.min_pt, axes=axes,
                                halfcomplex=False)
        assert irgrid.approx_equals(grid, atol=1e-6)
Example #5
0
def test_reciprocal_grid_nd_axes():

    grid = odl.uniform_grid([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    axes_list = [[1, -1], [0], 0, [0, 2, 1], [2, 0]]

    for axes in axes_list:
        active = np.zeros(grid.ndim, dtype=bool)
        active[axes] = True
        inactive = np.logical_not(active)

        true_recip_stride = np.empty(grid.ndim)
        true_recip_stride[active] = 2 * np.pi / (s[active] * n[active])
        true_recip_stride[inactive] = s[inactive]

        # Without shift altogether
        rgrid = reciprocal_grid(grid, shift=False, axes=axes,
                                halfcomplex=False)

        assert all_equal(rgrid.shape, n)
        assert all_almost_equal(rgrid.stride, true_recip_stride)
        assert all_almost_equal(rgrid.min_pt[active], -rgrid.max_pt[active])
        assert all_equal(rgrid.min_pt[inactive], grid.min_pt[inactive])
        assert all_equal(rgrid.max_pt[inactive], grid.max_pt[inactive])

        # Inverting should give back the original
        irgrid = realspace_grid(rgrid, grid.min_pt, axes=axes,
                                halfcomplex=False)
        assert irgrid.approx_equals(grid, atol=1e-6)
Example #6
0
def test_reciprocal_grid_1d(halfcomplex, shift, parity):

    shape = 10 if parity == 'even' else 11
    grid = odl.uniform_grid(0, 1, shape=shape)
    s = grid.stride
    n = np.array(grid.shape)

    rgrid = reciprocal_grid(grid, shift=shift, halfcomplex=halfcomplex)

    # Independent of halfcomplex, shift and parity
    true_recip_stride = 2 * np.pi / (s * n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)

    if halfcomplex:
        assert all_equal(rgrid.shape, n // 2 + 1)

        if parity == 'odd' and shift:
            # Max point should be half a negative recip stride
            assert all_almost_equal(rgrid.max_pt, -true_recip_stride / 2)
        elif parity == 'even' and not shift:
            # Max point should be half a positive recip stride
            assert all_almost_equal(rgrid.max_pt, true_recip_stride / 2)
        elif (parity == 'odd' and not shift) or (parity == 'even' and shift):
            # Max should be zero
            assert all_almost_equal(rgrid.max_pt, 0)
        else:
            raise RuntimeError('parameter combination not covered')
    else:  # halfcomplex = False
        assert all_equal(rgrid.shape, n)

        if (parity == 'even' and shift) or (parity == 'odd' and not shift):
            # Zero should be at index n // 2
            assert all_almost_equal(rgrid[n // 2], 0)
        elif (parity == 'odd' and shift) or (parity == 'even' and not shift):
            # No point should be closer to 0 than half a recip stride
            atol = 0.999 * true_recip_stride / 2
            assert not rgrid.approx_contains(0, atol=atol)
        else:
            raise RuntimeError('parameter combination not covered')

        if not shift:
            # Grid Should be symmetric
            assert all_almost_equal(rgrid.min_pt, -rgrid.max_pt)
            if parity == 'odd':
                # Midpoint should be 0
                assert all_almost_equal(rgrid.mid_pt, 0)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid,
                            grid.min_pt,
                            halfcomplex=halfcomplex,
                            halfcx_parity=parity)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #7
0
def test_reciprocal_grid_1d(halfcomplex, shift, parity):

    shape = 10 if parity == 'even' else 11
    grid = odl.uniform_sampling(0, 1, shape=shape)
    s = grid.stride
    n = np.array(grid.shape)

    rgrid = reciprocal_grid(grid, shift=shift, halfcomplex=halfcomplex)

    # Independent of halfcomplex, shift and parity
    true_recip_stride = 2 * np.pi / (s * n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)

    if halfcomplex:
        assert all_equal(rgrid.shape, n // 2 + 1)

        if parity == 'odd' and shift:
            # Max point should be half a negative recip stride
            assert all_almost_equal(rgrid.max_pt, -true_recip_stride / 2)
        elif parity == 'even' and not shift:
            # Max point should be half a positive recip stride
            assert all_almost_equal(rgrid.max_pt, true_recip_stride / 2)
        elif (parity == 'odd' and not shift) or (parity == 'even' and shift):
            # Max should be zero
            assert all_almost_equal(rgrid.max_pt, 0)
        else:
            raise RuntimeError('parameter combination not covered')
    else:  # halfcomplex = False
        assert all_equal(rgrid.shape, n)

        if (parity == 'even' and shift) or (parity == 'odd' and not shift):
            # Zero should be at index n // 2
            assert all_almost_equal(rgrid[n // 2], 0)
        elif (parity == 'odd' and shift) or (parity == 'even' and not shift):
            # No point should be closer to 0 than half a recip stride
            atol = 0.999 * true_recip_stride / 2
            assert not rgrid.approx_contains(0, atol=atol)
        else:
            raise RuntimeError('parameter combination not covered')

        if not shift:
            # Grid Should be symmetric
            assert all_almost_equal(rgrid.min_pt, -rgrid.max_pt)
            if parity == 'odd':
                # Midpoint should be 0
                assert all_almost_equal(rgrid.mid_pt, 0)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=halfcomplex,
                            halfcx_parity=parity)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #8
0
def test_fourier_trafo_range(exponent, floating_dtype):
    # Check if the range is initialized correctly. Encompasses the init test

    # Testing R2C for real dtype, else C2C

    # 1D
    shape = 10
    space_discr = odl.uniform_discr(0, 1, shape, exponent=exponent, impl="numpy", dtype=floating_dtype)

    dft = FourierTransform(space_discr, halfcomplex=True, shift=True)
    assert dft.range.field == odl.ComplexNumbers()
    halfcomplex = True if is_real_dtype(floating_dtype) else False
    assert dft.range.grid == reciprocal_grid(dft.domain.grid, halfcomplex=halfcomplex, shift=True)
    assert dft.range.exponent == conj_exponent(exponent)

    # 3D
    shape = (3, 4, 5)
    space_discr = odl.uniform_discr([0] * 3, [1] * 3, shape, exponent=exponent, impl="numpy", dtype=floating_dtype)

    dft = FourierTransform(space_discr, halfcomplex=True, shift=True)
    assert dft.range.field == odl.ComplexNumbers()
    halfcomplex = True if is_real_dtype(floating_dtype) else False
    assert dft.range.grid == reciprocal_grid(dft.domain.grid, halfcomplex=halfcomplex, shift=True)
    assert dft.range.exponent == conj_exponent(exponent)

    # shift must be True in the last axis
    if halfcomplex:
        with pytest.raises(ValueError):
            FourierTransform(space_discr, shift=(True, True, False))

    if exponent != 2.0:
        with pytest.raises(NotImplementedError):
            dft.adjoint

    with pytest.raises(TypeError):
        FourierTransform(dft.domain.partition)
Example #9
0
def test_reciprocal_grid_nd():

    grid = odl.uniform_sampling([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)

    true_recip_stride = 2 * np.pi / (s * n)

    # Without shift altogether
    rgrid = reciprocal_grid(grid, shift=False, halfcomplex=False)

    assert all_equal(rgrid.shape, n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)
    assert all_almost_equal(rgrid.min_pt, -rgrid.max_pt)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=False)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #10
0
def test_reciprocal_grid_nd():

    grid = odl.uniform_grid([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)

    true_recip_stride = 2 * np.pi / (s * n)

    # Without shift altogether
    rgrid = reciprocal_grid(grid, shift=False, halfcomplex=False)

    assert all_equal(rgrid.shape, n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)
    assert all_almost_equal(rgrid.min_pt, -rgrid.max_pt)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=False)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #11
0
def test_reciprocal_grid_nd_shift_list():

    grid = odl.uniform_sampling([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    shift = [False, True, False]

    true_recip_stride = 2 * np.pi / (s * n)

    # Shift only the even dimension, then zero must be contained
    rgrid = reciprocal_grid(grid, shift=shift, halfcomplex=False)
    noshift = np.where(np.logical_not(shift))

    assert all_equal(rgrid.shape, n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)
    assert all_almost_equal(rgrid.min_pt[noshift], -rgrid.max_pt[noshift])
    assert all_almost_equal(rgrid[n // 2], [0] * 3)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=False)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #12
0
def test_reciprocal_grid_nd_shift_list():

    grid = odl.uniform_grid([0] * 3, [1] * 3, shape=(3, 4, 5))
    s = grid.stride
    n = np.array(grid.shape)
    shift = [False, True, False]

    true_recip_stride = 2 * np.pi / (s * n)

    # Shift only the even dimension, then zero must be contained
    rgrid = reciprocal_grid(grid, shift=shift, halfcomplex=False)
    noshift = np.where(np.logical_not(shift))

    assert all_equal(rgrid.shape, n)
    assert all_almost_equal(rgrid.stride, true_recip_stride)
    assert all_almost_equal(rgrid.min_pt[noshift], -rgrid.max_pt[noshift])
    assert all_almost_equal(rgrid[n // 2], [0] * 3)

    # Inverting should give back the original
    irgrid = realspace_grid(rgrid, grid.min_pt, halfcomplex=False)
    assert irgrid.approx_equals(grid, atol=1e-6)
Example #13
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]))
Example #14
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]))