def __init__(self, inverse, domain, range=None, impl=None, **kwargs):
        """Initialize a new instance.

        All parameters are given according to the specifics of the forward
        transform. The ``inverse`` parameter is used to control conversions
        for the inverse transform.

        Parameters
        ----------
        inverse : bool
            If ``True``, create the inverse transform, otherwise the forward
            transform.
        domain : `DiscreteLp`
            Domain of the Fourier transform. If the
            `DiscreteLp.exponent` of ``domain`` and ``range`` are equal
            to 2.0, this operator has an adjoint which is equal to its
            inverse.
        range : `DiscreteLp`, optional
            Range of the Fourier transform. If not given, the range
            is determined from ``domain`` and the other parameters. The
            exponent is chosen to be the conjugate ``p / (p - 1)``,
            which reads as 'inf' for p=1 and 1 for p='inf'.
        impl : {'numpy', 'pyfftw'}, optional
            Backend for the FFT implementation. The 'pyfftw' backend
            is faster but requires the ``pyfftw`` package.
            ``None`` selects the fastest available backend.
        axes : int or sequence of ints, optional
            Dimensions along which to take the transform.
            Default: all axes
        sign : {'-', '+'}, optional
            Sign of the complex exponent. Default: '-'
        halfcomplex : bool, optional
            If ``True``, calculate only the negative frequency part
            along the last axis for real input. If ``False``,
            calculate the full complex FFT.
            For complex ``domain``, it has no effect.
            Default: ``True``
        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``

        Other Parameters
        ----------------
        tmp_r : `DiscreteLpElement` or `numpy.ndarray`, optional
            Temporary for calculations in the real space (domain of
            this transform). It is shared with the inverse.

            Variants using this: R2C, R2HC, C2R (inverse)

        tmp_f : `DiscreteLpElement` or `numpy.ndarray`, optional
            Temporary for calculations in the frequency (reciprocal)
            space. It is shared with the inverse.

            Variants using this: R2C, C2R (inverse), HC2R (inverse)

        Notes
        -----
        * The transform variants are:

          - **C2C**: complex-to-complex.
            The default variant, one-to-one and unitary.

          - **R2C**: real-to-complex.
            This variants adjoint and inverse may suffer
            from information loss since the result is cast to real.

          - **R2HC**: real-to-halfcomplex.
            This variant stores only a half-space of frequencies and
            is guaranteed to be one-to-one (invertible).

        * The `Operator.range` of this operator always has the
          `ComplexNumbers` as `LinearSpace.field`, i.e. if the
          field of ``domain`` is the `RealNumbers`, this operator's adjoint
          is defined by identifying real and imaginary parts with
          the components of a real product space element.
          See the `mathematical background documentation
          <odlgroup.github.io/odl/math/trafos/fourier_transform.html#adjoint>`_
          for details.
        """
        if not isinstance(domain, DiscreteLp):
            raise TypeError('domain {!r} is not a `DiscreteLp` instance'
                            ''.format(domain))
        if domain.impl != 'numpy':
            raise NotImplementedError(
                'Only Numpy-based data spaces are supported, got {}'
                ''.format(domain.tspace))

        # axes
        axes = kwargs.pop('axes', np.arange(domain.ndim))
        self.__axes = normalized_axes_tuple(axes, domain.ndim)

        # Implementation
        if impl is None:
            impl = _DEFAULT_FOURIER_IMPL
        impl, impl_in = str(impl).lower(), impl
        if impl not in _SUPPORTED_FOURIER_IMPLS:
            raise ValueError("`impl` '{}' not supported".format(impl_in))
        self.__impl = impl

        # Handle half-complex yes/no and shifts
        halfcomplex = kwargs.pop('halfcomplex', True)
        shift = kwargs.pop('shift', True)
        if all(domain.grid.is_uniform_byaxis[i] for i in self.axes):
            if domain.field == ComplexNumbers():
                self.__halfcomplex = False
            else:
                self.__halfcomplex = bool(halfcomplex)

            self.__shifts = normalized_scalar_param_list(
                shift, length=len(self.axes), param_conv=bool)
        else:
            raise NotImplementedError('non-uniform grids not yet supported')

        sign = kwargs.pop('sign', '-')
        if sign not in ('+', '-'):
            raise ValueError("`sign` '{}' not understood".format(sign))
        fwd_sign = ('-' if sign == '+' else '+') if inverse else sign
        if fwd_sign == '+' and self.halfcomplex:
            raise ValueError("cannot combine sign '+' with a half-complex "
                             "transform")
        self.__sign = sign

        # Need to filter out this situation since the pre-processing step
        # casts to complex otherwise, and then no half-complex transform
        # is possible.
        if self.halfcomplex and not self.shifts[-1]:
            raise ValueError('`shift` must be `True` in the halved (last) '
                             'axis in half-complex transforms')

        # Storing temporaries directly as arrays
        tmp_r = kwargs.pop('tmp_r', None)
        tmp_f = kwargs.pop('tmp_f', None)

        # Before starting to calculate stuff, we check for bad arguments
        if kwargs:
            raise TypeError('got unexpected keyword arguments: {}'
                            ''.format(kwargs))

        # Infer the range if necessary
        if range is None:
            # self._halfcomplex and self._axes need to be set for this
            range = reciprocal_space(domain, axes=self.axes,
                                     halfcomplex=self.halfcomplex,
                                     shift=self.shifts)

        if inverse:
            super(FourierTransformBase, self).__init__(
                range, domain, linear=True)
        else:
            super(FourierTransformBase, self).__init__(
                domain, range, linear=True)
        self._fftw_plan = None

        if tmp_r is not None:
            tmp_r = domain.element(tmp_r).asarray()
        if tmp_f is not None:
            tmp_f = range.element(tmp_f).asarray()

        self._tmp_r = tmp_r
        self._tmp_f = tmp_f
def __init__ ( self , inverse , domain , range = None , impl = None , ** kwargs ) : if not isinstance ( domain , DiscreteLp ) : raise TypeError ( 'domain {!r} is not a `DiscreteLp` instance' '' . format ( domain ) ) if domain . impl != 'numpy' : raise NotImplementedError ( 'Only Numpy-based data spaces are supported, got {}' '' . format ( domain . tspace ) ) axes = kwargs . pop ( 'axes' , np . arange ( domain . ndim ) ) self . __axes = normalized_axes_tuple ( axes , domain . ndim ) if impl is None : impl = _DEFAULT_FOURIER_IMPL impl , impl_in = str ( impl ) . lower ( ) , impl if impl not in _SUPPORTED_FOURIER_IMPLS : raise ValueError ( "`impl` '{}' not supported" . format ( impl_in ) ) self . __impl = impl halfcomplex = kwargs . pop ( 'halfcomplex' , True ) shift = kwargs . pop ( 'shift' , True ) if all ( domain . grid . is_uniform_byaxis [ i ] for i in self . axes ) : if domain . field == ComplexNumbers ( ) : self . __halfcomplex = False else : self . __halfcomplex = bool ( halfcomplex ) self . __shifts = normalized_scalar_param_list ( shift , length = len ( self . axes ) , param_conv = bool ) else : raise NotImplementedError ( 'non-uniform grids not yet supported' ) sign = kwargs . pop ( 'sign' , '-' ) if sign not in ( '+' , '-' ) : raise ValueError ( "`sign` '{}' not understood" . format ( sign ) ) fwd_sign = ( '-' if sign == '+' else '+' ) if inverse else sign if fwd_sign == '+' and self . halfcomplex : raise ValueError ( "cannot combine sign '+' with a half-complex " "transform" ) self . __sign = sign if self . halfcomplex and not self . shifts [ - 1 ] : raise ValueError ( '`shift` must be `True` in the halved (last) ' 'axis in half-complex transforms' ) tmp_r = kwargs . pop ( 'tmp_r' , None ) tmp_f = kwargs . pop ( 'tmp_f' , None ) if kwargs : raise TypeError ( 'got unexpected keyword arguments: {}' '' . format ( kwargs ) ) if range is None : range = reciprocal_space ( domain , axes = self . axes , halfcomplex = self . halfcomplex , shift = self . shifts ) if inverse : super ( FourierTransformBase , self ) . __init__ ( range , domain , linear = True ) else : super ( FourierTransformBase , self ) . __init__ ( domain , range , linear = True ) self . _fftw_plan = None if tmp_r is not None : tmp_r = domain . element ( tmp_r ) . asarray ( ) if tmp_f is not None : tmp_f = range . element ( tmp_f ) . asarray ( ) self . _tmp_r = tmp_r self . _tmp_f = tmp_f
Initialize a new instance.

        All parameters are given according to the specifics of the forward
        transform. The ``inverse`` parameter is used to control conversions
        for the inverse transform.

        Parameters
        ----------
        inverse : bool
            If ``True``, create the inverse transform, otherwise the forward
            transform.
        domain : `DiscreteLp`
            Domain of the Fourier transform. If the
            `DiscreteLp.exponent` of ``domain`` and ``range`` are equal
            to 2.0, this operator has an adjoint which is equal to its
            inverse.
        range : `DiscreteLp`, optional
            Range of the Fourier transform. If not given, the range
            is determined from ``domain`` and the other parameters. The
            exponent is chosen to be the conjugate ``p / (p - 1)``,
            which reads as 'inf' for p=1 and 1 for p='inf'.
        impl : {'numpy', 'pyfftw'}, optional
            Backend for the FFT implementation. The 'pyfftw' backend
            is faster but requires the ``pyfftw`` package.
            ``None`` selects the fastest available backend.
        axes : int or sequence of ints, optional
            Dimensions along which to take the transform.
            Default: all axes
        sign : {'-', '+'}, optional
            Sign of the complex exponent. Default: '-'
        halfcomplex : bool, optional
            If ``True``, calculate only the negative frequency part
            along the last axis for real input. If ``False``,
            calculate the full complex FFT.
            For complex ``domain``, it has no effect.
            Default: ``True``
        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``

        Other Parameters
        ----------------
        tmp_r : `DiscreteLpElement` or `numpy.ndarray`, optional
            Temporary for calculations in the real space (domain of
            this transform). It is shared with the inverse.

            Variants using this: R2C, R2HC, C2R (inverse)

        tmp_f : `DiscreteLpElement` or `numpy.ndarray`, optional
            Temporary for calculations in the frequency (reciprocal)
            space. It is shared with the inverse.

            Variants using this: R2C, C2R (inverse), HC2R (inverse)

        Notes
        -----
        * The transform variants are:

          - **C2C**: complex-to-complex.
            The default variant, one-to-one and unitary.

          - **R2C**: real-to-complex.
            This variants adjoint and inverse may suffer
            from information loss since the result is cast to real.

          - **R2HC**: real-to-halfcomplex.
            This variant stores only a half-space of frequencies and
            is guaranteed to be one-to-one (invertible).

        * The `Operator.range` of this operator always has the
          `ComplexNumbers` as `LinearSpace.field`, i.e. if the
          field of ``domain`` is the `RealNumbers`, this operator's adjoint
          is defined by identifying real and imaginary parts with
          the components of a real product space element.
          See the `mathematical background documentation
          <odlgroup.github.io/odl/math/trafos/fourier_transform.html#adjoint>`_
          for details.