Skip to content

Commit

Permalink
Merge pull request pyFFTW#189 from fredRos/cond-tests
Browse files Browse the repository at this point in the history
[setup, pyx] Build only the parts for which FFTW libraries were found
  • Loading branch information
hgomersall authored Feb 9, 2018
2 parents ad23f73 + a9e8714 commit 3f8a6c7
Show file tree
Hide file tree
Showing 22 changed files with 793 additions and 378 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ install:
- source activate test-environment
- python setup.py -v build_ext --inplace

script: python setup.py test
script:
- python setup.py test
- travis/partial.sh

before_deploy:
- rvm default
Expand Down
37 changes: 25 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ Requirements (i.e. what it was designed for)
--------------------------------------------
- Python 2.7 or greater (Python 3 is supported)
- Numpy 1.6
- FFTW 3.3 or higher (lower versions *may* work)
- FFTW 3.3 or higher (lower versions *may* work) libraries for single, double,
and long double precision in serial and multithreading (pthreads or openMP)
versions
- Cython 0.15 or higher (though the source release on PyPI loses this
dependency)

Expand Down Expand Up @@ -99,6 +101,16 @@ After you've run ``setup.py`` with cython available, you then have a
normal C extension in the ``pyfftw`` directory.
Further building does not depend on cython (as long as the .c file remains).

During configuration the available FFTW libraries are detected, so pay attention
to the output when running ``setup.py``. On certain platforms, for example the
long double precision is not available. pyFFTW still builds fine but will fail
at runtime if asked to perform a transform involving long double precision.

Regarding multithreading, if both posix and openMP FFTW libs are available, the
openMP libs are preferred. This preference can be reversed by defining the
environment variable ``PYFFTW_USE_PTHREADS`` prior to building. If neither
option is available, pyFFTW works in serial mode only.

For more ways of building and installing, see the
`distutils documentation <http://docs.python.org/distutils/builtdist.html>`_
and `setuptools documentation <https://pythonhosted.org/setuptools/>`_.
Expand All @@ -109,23 +121,23 @@ Platform specific build info
Windows
~~~~~~~

To build for windows from source, download the fftw dlls for your system
and the header file from `here <http://www.fftw.org/install/windows.html>`_
(they're in a zip file) and place them in the pyfftw
directory. The files are ``libfftw3-3.dll``, ``libfftw3l-3.dll``,
``libfftw3f-3.dll``. If you're using a version of FFTW other than 3.3, it may
be necessary to copy ``fftw3.h`` into ``include\win``.
To build for windows from source, download the fftw dlls for your system and the
header file from `here <http://www.fftw.org/install/windows.html>`_ (they're in
a zip file) and place them in the pyfftw directory. The files are
``libfftw3-3.dll``, ``libfftw3l-3.dll``, ``libfftw3f-3.dll``. These libs use
pthreads for multithreading. If you're using a version of FFTW other than 3.3,
it may be necessary to copy ``fftw3.h`` into ``include\win``.

The builds on PyPI use mingw for the 32-bit release and the Windows SDK
C++ compiler for the 64-bit release. The scripts should handle this
automatically. If you want to compile for 64-bit Windows, you have to use
the MS Visual C++ compiler. Set up your environment as described
`here <https://github.com/cython/cython/wiki/CythonExtensionsOnWindows>`_ and then
run `setup.py` with the version of python you wish to target and a suitable
run ``setup.py`` with the version of python you wish to target and a suitable
build command.

For using the MS Visual C++ compiler, you'll need to create a set of
suitable `.lib` files as described on the
suitable ``.lib`` files as described on the
`FFTW page <http://www.fftw.org/install/windows.html>`_.

Mac OSX
Expand All @@ -144,8 +156,9 @@ Now install pyfftw from pip::

pip install pyfftw

Notes: `pkgin <http://saveosx.org>`_ fftw package does not contain the long
or float implementations of fftw and so installation will fail.
Notes: `pkgin <http://saveosx.org>`_ fftw package does not contain the long or
float implementations of fftw and so pyFFTW can only perform double-precision
transforms.

It has been suggested that `macports <http://www.macports.org/>`_ might also
work fine. You should then replace the LD environmental variables above with the
Expand All @@ -163,7 +176,7 @@ Install FFTW from ports tree or ``pkg``:
- math/fftw3-float
- math/fftw3-long

Please install all of them.
Please install all of them, if possible.

Contributions
-------------
Expand Down
5 changes: 5 additions & 0 deletions pyfftw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
ones_aligned,
zeros_aligned,
next_fast_len,
_supported_types,
_supported_nptypes_complex,
_supported_nptypes_real,
_all_types_human_readable,
_all_types_np
)

from . import builders
Expand Down
69 changes: 43 additions & 26 deletions pyfftw/builders/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@
Certainly, users may encounter instances of
:class:`~pyfftw.builders._utils._FFTWWrapper`.
These everything documented in this module is *not* part of the public API
Everything documented in this module is *not* part of the public API
and may change in future versions.
'''

import pyfftw
import numpy
import warnings

__all__ = ['_FFTWWrapper', '_rc_dtype_pairs', '_default_dtype', '_Xfftn',
'_setup_input_slicers', '_compute_array_shapes', '_precook_1d_args',
Expand All @@ -60,18 +61,35 @@
_valid_efforts = ('FFTW_ESTIMATE', 'FFTW_MEASURE',
'FFTW_PATIENT', 'FFTW_EXHAUSTIVE')

# Looking up a dtype in here returns the complex complement of the same
# precision.
# Looking up a real dtype in here returns the complex complement of the same
# precision, and vice versa.
# It is necessary to use .char as the keys due to MSVC mapping long
# double to double and the way that numpy handles this.
_rc_dtype_pairs = {numpy.dtype('float32').char: numpy.dtype('complex64'),
_rc_dtype_pairs = {}
_default_dtype = None

# Double precision is the default default precision. Prefer casting to higher precision if
# possible. If missing, long double is mapped to double and so we lose less
# precision than by converting to single.
if '64' in pyfftw._supported_types:
_default_dtype = numpy.dtype('float64')
_rc_dtype_pairs.update({
numpy.dtype('float64').char: numpy.dtype('complex128'),
numpy.dtype('longdouble').char: numpy.dtype('clongdouble'),
numpy.dtype('complex64').char: numpy.dtype('float32'),
numpy.dtype('complex128').char: numpy.dtype('float64'),
numpy.dtype('clongdouble').char: numpy.dtype('longdouble')}

_default_dtype = numpy.dtype('float64')
numpy.dtype('complex128').char: numpy.dtype('float64')})
if 'ld' in pyfftw._supported_types:
if _default_dtype is None:
_default_dtype = numpy.dtype('longdouble')
_rc_dtype_pairs.update({
numpy.dtype('longdouble').char: numpy.dtype('clongdouble'),
numpy.dtype('clongdouble').char: numpy.dtype('longdouble')})
if '32' in pyfftw._supported_types:
if _default_dtype is None:
_default_dtype = numpy.dtype('float32')
_rc_dtype_pairs.update({
numpy.dtype('float32').char: numpy.dtype('complex64'),
numpy.dtype('complex64').char: numpy.dtype('float32')})
if _default_dtype is None:
raise NotImplementedError("No default precision available")


def _unitary(norm):
Expand Down Expand Up @@ -117,24 +135,23 @@ def _Xfftn(a, s, axes, overwrite_input,

a_is_complex = numpy.iscomplexobj(a)

# Make the input dtype correct
# Make the input dtype correct by transforming to an available type
if a.dtype.char not in _rc_dtype_pairs:
if a.dtype == numpy.dtype('float16'):
# convert half-precision to single precision rather than double
if not real or inverse:
a = numpy.asarray(
a, dtype=_rc_dtype_pairs[numpy.dtype('float32').char])
else:
a = numpy.asarray(a, dtype=numpy.dtype('float32').char)
else:
# We make it the default dtype
if not real or inverse:
# It's going to be complex
a = numpy.asarray(
a, dtype=_rc_dtype_pairs[_default_dtype.char])
else:
a = numpy.asarray(a, dtype=_default_dtype)
dtype = _default_dtype
if a.dtype == numpy.dtype('float16') and '32' in pyfftw._supported_types:
# convert half-precision to single precision, if available
dtype = numpy.dtype('float32')

# warn when losing precision but not when using a higher precision
if dtype.itemsize < a.dtype.itemsize:
warnings.warn("Narrowing conversion from %s to %s precision" % (a.dtype, dtype))

if not real or inverse:
# It's going to be complex
dtype = numpy.dtype(_rc_dtype_pairs[dtype.char])

# finally convert the input array
a = numpy.asarray(a, dtype=dtype)
elif not (real and not inverse) and not a_is_complex:
# We need to make it a complex dtype
a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char])
Expand Down
16 changes: 9 additions & 7 deletions pyfftw/interfaces/numpy_fft.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@
directly from :mod:`numpy.fft`.
It is notable that unlike :mod:`numpy.fftpack`, these functions will
generally return an output array with the same precision as the input
array, and the transform that is chosen is chosen based on the precision
of the input array. That is, if the input array is 32-bit floating point,
then the transform will be 32-bit floating point and so will the returned
array. Half precision input will be converted to single precision. Otherwise,
if any type conversion is required, the default will be double precision.
It is notable that unlike :mod:`numpy.fftpack`, these functions will generally
return an output array with the same precision as the input array, and the
transform that is chosen is chosen based on the precision of the input array.
That is, if the input array is 32-bit floating point, then the transform will
be 32-bit floating point and so will the returned array. Half precision input
will be converted to single precision. Otherwise, if any type conversion is
required, the default will be double precision. If pyFFTW was not built with
support for double precision, the default is long double precision. If that is not
available, it defaults to single precision.
One known caveat is that repeated axes are handled differently to
:mod:`numpy.fft`; axes that are repeated in the axes argument are considered
Expand Down
Loading

0 comments on commit 3f8a6c7

Please sign in to comment.