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='+')
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='+')
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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]))
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]))