Example #1
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 #2
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 #3
0
def reciprocal_space(space, axes=None, halfcomplex=False, shift=True,
                     **kwargs):
    """Return the range of the Fourier transform on ``space``.

    Parameters
    ----------
    space : `DiscreteLp`
        Real space whose reciprocal is calculated. It must be
        uniformly discretized.
    axes : sequence of ints, optional
        Dimensions along which the Fourier transform is taken.
        Default: all axes
    halfcomplex : bool, optional
        If ``True``, take only the negative frequency part along the last
        axis for. For ``False``, use the full frequency space.
        This option can only be used if ``space`` is a space of
        real-valued functions.
    shift : bool or sequence of bools, optional
        If ``True``, the reciprocal grid is shifted by half a stride in
        the negative direction. With a boolean sequence, this option
        is applied separately to each axis.
        If a sequence is provided, it must have the same length as
        ``axes`` if supplied. Note that this must be set to ``True``
        in the halved axis in half-complex transforms.
        Default: ``True``
    impl : string, optional
        Implementation back-end for the created space.
        Default: ``'numpy'``
    exponent : float, optional
        Create a space with this exponent. By default, the conjugate
        exponent ``q = p / (p - 1)`` of the exponent of ``space`` is
        used, where ``q = inf`` for ``p = 1`` and vice versa.
    dtype : optional
        Complex data type of the created space. By default, the
        complex counterpart of ``space.dtype`` is used.

    Returns
    -------
    rspace : `DiscreteLp`
        Reciprocal of the input ``space``. If ``halfcomplex=True``, the
        upper end of the domain (where the half space ends) is chosen to
        coincide with the grid node.
    """
    if not isinstance(space, DiscreteLp):
        raise TypeError('`space` {!r} is not a `DiscreteLp` instance'
                        ''.format(space))
    if axes is None:
        axes = tuple(range(space.ndim))
    axes = normalized_axes_tuple(axes, space.ndim)

    if not all(space.is_uniform_byaxis[axis] for axis in axes):
        raise ValueError('`space` is not uniformly discretized in the '
                         '`axes` of the transform')

    if halfcomplex and space.field != RealNumbers():
        raise ValueError('`halfcomplex` option can only be used with real '
                         'spaces')

    exponent = kwargs.pop('exponent', None)
    if exponent is None:
        exponent = conj_exponent(space.exponent)

    dtype = kwargs.pop('dtype', None)
    if dtype is None:
        dtype = complex_dtype(space.dtype)
    else:
        if not is_complex_floating_dtype(dtype):
            raise ValueError('{} is not a complex data type'
                             ''.format(dtype_repr(dtype)))

    impl = kwargs.pop('impl', 'numpy')

    # Calculate range
    recip_grid = reciprocal_grid(space.grid, shift=shift,
                                 halfcomplex=halfcomplex, axes=axes)

    # Make a partition with nodes on the boundary in the last transform axis
    # if `halfcomplex == True`, otherwise a standard partition.
    if halfcomplex:
        max_pt = {axes[-1]: recip_grid.max_pt[axes[-1]]}
        part = uniform_partition_fromgrid(recip_grid, max_pt=max_pt)
    else:
        part = uniform_partition_fromgrid(recip_grid)

    # Use convention of adding a hat to represent fourier transform of variable
    axis_labels = list(space.axis_labels)
    for i in axes:
        # Avoid double math
        label = axis_labels[i].replace('$', '')
        axis_labels[i] = '$\^{{{}}}$'.format(label)

    recip_spc = uniform_discr_frompartition(part, exponent=exponent,
                                            dtype=dtype, impl=impl,
                                            axis_labels=axis_labels)

    return recip_spc
Example #4
0
def reciprocal_space(space, axes=None, halfcomplex=False, shift=True,
                     **kwargs):
    """Return the range of the Fourier transform on ``space``.

    Parameters
    ----------
    space : `DiscreteLp`
        Real space whose reciprocal is calculated. It must be
        uniformly discretized.
    axes : sequence of ints, optional
        Dimensions along which the Fourier transform is taken.
        Default: all axes
    halfcomplex : bool, optional
        If ``True``, take only the negative frequency part along the last
        axis for. For ``False``, use the full frequency space.
        This option can only be used if ``space`` is a space of
        real-valued functions.
    shift : bool or sequence of bools, optional
        If ``True``, the reciprocal grid is shifted by half a stride in
        the negative direction. With a boolean sequence, this option
        is applied separately to each axis.
        If a sequence is provided, it must have the same length as
        ``axes`` if supplied. Note that this must be set to ``True``
        in the halved axis in half-complex transforms.
        Default: ``True``
    impl : string, optional
        Implementation back-end for the created space.
        Default: ``'numpy'``
    exponent : float, optional
        Create a space with this exponent. By default, the conjugate
        exponent ``q = p / (p - 1)`` of the exponent of ``space`` is
        used, where ``q = inf`` for ``p = 1`` and vice versa.
    dtype : optional
        Complex data type of the created space. By default, the
        complex counterpart of ``space.dtype`` is used.

    Returns
    -------
    rspace : `DiscreteLp`
        Reciprocal of the input ``space``. If ``halfcomplex=True``, the
        upper end of the domain (where the half space ends) is chosen to
        coincide with the grid node.
    """
    if not isinstance(space, DiscreteLp):
        raise TypeError('`space` {!r} is not a `DiscreteLp` instance'
                        ''.format(space))
    if not space.is_uniform:
        raise ValueError('`space` is not uniformly discretized')

    if axes is None:
        axes = tuple(range(space.ndim))

    axes = normalized_axes_tuple(axes, space.ndim)

    if halfcomplex and space.field != RealNumbers():
        raise ValueError('`halfcomplex` option can only be used with real '
                         'spaces')

    exponent = kwargs.pop('exponent', None)
    if exponent is None:
        exponent = conj_exponent(space.exponent)

    dtype = kwargs.pop('dtype', None)
    if dtype is None:
        dtype = complex_dtype(space.dtype)
    else:
        if not is_complex_floating_dtype(dtype):
            raise ValueError('{} is not a complex data type'
                             ''.format(dtype_repr(dtype)))

    impl = kwargs.pop('impl', 'numpy')

    # Calculate range
    recip_grid = reciprocal_grid(space.grid, shift=shift,
                                 halfcomplex=halfcomplex, axes=axes)

    # Make a partition with nodes on the boundary in the last transform axis
    # if `halfcomplex == True`, otherwise a standard partition.
    if halfcomplex:
        max_pt = {axes[-1]: recip_grid.max_pt[axes[-1]]}
        part = uniform_partition_fromgrid(recip_grid, max_pt=max_pt)
    else:
        part = uniform_partition_fromgrid(recip_grid)

    # Use convention of adding a hat to represent fourier transform of variable
    axis_labels = list(space.axis_labels)
    for i in axes:
        # Avoid double math
        label = axis_labels[i].replace('$', '')
        axis_labels[i] = '$\^{{{}}}$'.format(label)

    recip_spc = uniform_discr_frompartition(part, exponent=exponent,
                                            dtype=dtype, impl=impl,
                                            axis_labels=axis_labels)

    return recip_spc