diff --git a/examples/cython/c_interface/setup.py b/examples/cython/c_interface/setup.py index 2f0ce429cdcec75676a2408ee08f4855c40e314e..15a0b895ac5fd8a1ed57d7f3d3b249c297f49ce1 100644 --- a/examples/cython/c_interface/setup.py +++ b/examples/cython/c_interface/setup.py @@ -1,29 +1,9 @@ import os -from setuptools import setup, Extension, Command +from setuptools import setup, Extension ext = Extension("foobar.hello", sources=["foobar/hello.pyx", "foobar/c_hello.c"]) -class CleanCommand(Command): - """Enable `python setup.py clean` to tidy up properly.""" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - os.system('rm -vrf build') - os.system('rm -vrf dist') - os.system('rm -vrf foobar/hello.c') - os.system('rm -vrf foobar/__pycache__') - os.system('rm -vrf foobar.egg-info') - os.system("find foobar -name '*.so' -delete -print") - os.system("find foobar -name '*.pyc' -delete -print") - - setup(name="foobar", ext_modules=[ext], - cmdclass={'clean': CleanCommand}) +) diff --git a/examples/cython/c_interface_shared_object/setup.py b/examples/cython/c_interface_shared_object/setup.py index 4abb29edeec6df214189708beff19eeb848b5fe3..f19d9cf7f838d34a9529768f191c9026841cea34 100644 --- a/examples/cython/c_interface_shared_object/setup.py +++ b/examples/cython/c_interface_shared_object/setup.py @@ -1,5 +1,5 @@ import os -from setuptools import setup, Extension, Command +from setuptools import setup, Extension libhello_dir = os.path.abspath("./libhello") @@ -16,34 +16,11 @@ ld_flags.append("-L"+libhello_dir) ld_flags.append("-Wl,-rpath,"+libhello_dir) ld_flags.append("-lhello") # link libhello - ext = Extension("wrap_libhello.hello", sources=["wrap_libhello/hello.pyx"], extra_link_args=ld_flags, include_dirs=include_dirs) - -class CleanCommand(Command): - """Enable `python setup.py clean` to tidy up properly.""" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - os.system('rm -vrf build') - os.system('rm -vrf dist') - os.system('rm -vrf wrap_libhello/__pycache__') - os.system('rm -vrf wrap_libhello/hello.c') - os.system('rm -vrf wrap_libhello.egg-info') - os.system("find wrap_libhello libhello -name '*.o' -delete -print") - os.system("find wrap_libhello libhello -name '*.so' -delete -print") - os.system("find wrap_libhello libhello -name '*.pyc' -delete -print") - - setup(name="wrap_libhello", ext_modules=[ext], - cmdclass={'clean': CleanCommand}) +) diff --git a/examples/cython/c_numpy/setup.py b/examples/cython/c_numpy/setup.py index ff6c7e895b7b26f8ee9d1f7e457ef029659e9809..57009d0eab05a6058dd7fd92544ad1e3486e327d 100644 --- a/examples/cython/c_numpy/setup.py +++ b/examples/cython/c_numpy/setup.py @@ -1,6 +1,6 @@ import os import numpy as np -from setuptools import setup, Extension, Command +from setuptools import setup, Extension # assuming gcc, set (aggressive) optimization flags, to be appended to the default compile line @@ -25,27 +25,7 @@ ext = Extension("ctonumpy.cube", extra_link_args=ld_flags, include_dirs=[np.get_include()]) -class CleanCommand(Command): - """Enable `python setup.py clean` to tidy up properly.""" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - # better: use plain Python instead of UNIX commands - os.system('rm -vrf build') - os.system('rm -vrf dist') - os.system('rm -vrf ctonumpy/cube.c') - os.system('rm -vrf ctonumpy/__pycache__') - os.system('rm -vrf ctonumpy.egg-info') - os.system("find ctonumpy -name '*.so' -delete -print") - os.system("find ctonumpy -name '*.pyc' -delete -print") - setup(name="ctonumpy", ext_modules=[ext], - cmdclass={'clean': CleanCommand}) +) diff --git a/examples/pybind11/setup.py b/examples/pybind11/setup.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5ef0a12dcc7c3f782aef80548d090f19774d5e8f 100644 --- a/examples/pybind11/setup.py +++ b/examples/pybind11/setup.py @@ -0,0 +1,16 @@ +from setuptools import setup +# run `pip install --user pybind11` if not yet installed +from pybind11.setup_helpers import Pybind11Extension, build_ext + +ext_modules = [ + Pybind11Extension( + "pybex", + ["src/pybex.cpp",], + ), +] + +setup( + cmdclass={"build_ext": build_ext}, + ext_modules=ext_modules +) + diff --git a/examples/pybind11/src/cube.cpp b/examples/pybind11/src/cube.cpp deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/examples/pybind11/src/pybex.cpp b/examples/pybind11/src/pybex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3700913d4ffa2fb975d3db274bfedaca5e2cdc89 --- /dev/null +++ b/examples/pybind11/src/pybex.cpp @@ -0,0 +1,32 @@ +// pybind11 example module 'pybex' + +#include <pybind11/pybind11.h> +#include <pybind11/numpy.h> + +namespace py = pybind11; + +// C/C++ implementation of the function to be wrapped +void c_cube(const double * v_in, double * v_out, size_t n_elem) { + for (size_t i=0; i<n_elem; ++i) { + v_out[i] = v_in[i] * v_in[i] * v_in[i]; + } +} + +// wrapper function, accepting a NumPy array as input and returning a NumPy array +py::array_t<double> py_cube(py::array_t<double, py::array::c_style> v_in) +{ + // allocate buffer to be returned as a NumPy array + auto v_out = py::array_t<double>(v_in.size()); + double * v_out_ptr = (double*) v_out.request().ptr; + + c_cube(v_in.data(), v_out_ptr, v_in.size()); + + return v_out; +} + +PYBIND11_MODULE(pybex, m) { + m.doc() = "pybind11 example module"; // module docstring + + m.def("cube", &py_cube, "a function that cubes a double-precision numpy array"); +} + diff --git a/notebooks/2e--Interfacing_with_C_and_F.ipynb b/notebooks/2e--Interfacing_with_C_and_F.ipynb index 09b51f5653463e12e6238b3ada9e89b99f8e2af1..39ac47ddd25a8e031024076a792082a74d1766f8 100644 --- a/notebooks/2e--Interfacing_with_C_and_F.ipynb +++ b/notebooks/2e--Interfacing_with_C_and_F.ipynb @@ -10,7 +10,7 @@ "source": [ "# Interfacing Python/NumPy with C/C++ and Fortran Code\n", "\n", - "**Python for HPC course**\n", + "**Python for HPC**\n", "\n", "2018-2021 Sebastian Ohlmann, Klaus Reuter\n", "\n", @@ -29,14 +29,14 @@ "\n", "* Call compiled C/C++ or Fortran code from the Python layer\n", " * Existing (legacy) code\n", - " * Newly developed and optimized code (hotspots, numerical kernels)\n", + " * Newly developed and optimized code (numerical kernels)\n", "\n", "$\\rightarrow$ combine the convenience of using high-level Python with high-performance compiled code\n", "\n", "## Steps\n", "\n", "* create software interface between Python/NumPy and C/C++/Fortran\n", - "* compile and link to a Python module" + "* compile and link to become a Python-importable module" ] }, { @@ -49,6 +49,8 @@ "source": [ "## Interfacing Options\n", "\n", + "We're looking into the following options:\n", + "\n", "* Fortran $\\longrightarrow$ **f2py**\n", "* C/C++/CUDA $\\longrightarrow$ **Cython**\n", "* C++ $\\longrightarrow$ **pybind11**\n", @@ -57,7 +59,7 @@ "\n", "* Swig, an interface generator\n", "* Boost.Python for C++ codes\n", - "* Python's low-level C API (different for Python 2 and 3!)" + "* Python's low-level C API" ] }, { @@ -74,9 +76,9 @@ " * is part of NumPy\n", " * scans the Fortran code and generates signature files (.pyf)\n", " * automatically takes care of type casting and non-contiguous arrays\n", - "* 2 ways of usage\n", - " * direct calling of `f2py`\n", - " * extension in `setup.py` using `numpy.distutils`\n", + "* two ways of usage\n", + " * direct calling of the `f2py` executable\n", + " * define an extension in `setup.py` using `numpy.distutils`\n", "* see examples in './f2py' (cf. the [f2py user guide](https://sysbio.ioc.ee/projects/f2py2e/usersguide/index.html))" ] }, @@ -88,7 +90,7 @@ } }, "source": [ - "### Compile and link Fortran code to Python module using `f2py`\n", + "### Compile and link Fortran code to a Python module using `f2py`\n", "\n", "* Given the Fortran file `fib.f90`\n", "\n", @@ -108,7 +110,7 @@ "end\n", "```\n", "\n", - "* Generate '.so' module file by calling `f2py -c fib.f90 -m fib` (second parameter is module name)" + "* Generate 'fib' Python module file by calling `f2py -c fib.f90 -m fib` (second parameter is module name)" ] }, { @@ -263,7 +265,7 @@ } }, "source": [ - "## Interfacing with C from NumPy-Code using Cython\n", + "## Interfacing with C code from NumPy code using Cython\n", "\n", "* Goals\n", " * Pass NumPy arrays down to C code to perform computation, \n", @@ -294,7 +296,7 @@ " * OpenMP threading (not affected by the Python GIL)\n", " * vectorization\n", " * cache optimization, etc.\n", - " * tweak the compiler flags in `setup.py`" + " * use optimizing compiler flags in `setup.py`" ] }, { @@ -305,9 +307,9 @@ } }, "source": [ - "## Interfacing with a C library (shared object) using Cython\n", + "## Interfacing with a C library ('.so', shared object) using Cython\n", "* Goal: Use a C library from Python\n", - " * third-party library for which no Python bindings exist, yet\n", + " * third-party library for which no Python bindings exist\n", " * legacy code you want to wrap into Python\n", " * CUDA code (compile into `.so` file independently using `nvcc`, first)\n", "* see the comprehensive example in `cython/c_interface_shared_object`" @@ -329,7 +331,7 @@ " * installation location passed as arguments to `setup.py`\n", " * installed at sytem location\n", " * deployment: add the RPATH to the link line pointing to library location, to avoid `LD_LIBRARY_PATH`\n", - "* for CUDA code, it is much easier to go via a library than using `setup.py` with `nvcc`" + "* hint: for CUDA code, it is often easier to go via a library than injecting `nvcc` into `setup.py`" ] }, { @@ -342,7 +344,7 @@ "source": [ "## Cython and C++\n", "\n", - "* Cython supports most of the C++ features: classes, templates, overloading\n", + "* Cython supports many object-oriented/advanced C++ features: classes, templates, overloading\n", "* typical use case: write a Python wrapper class for a C++ class\n", "* not covered here, please see for further details \n", " http://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html" @@ -356,7 +358,7 @@ } }, "source": [ - "### Cython provides interfaces to libc and c++ STL" + "### Cython provides interfaces to libc and C++ STL (!)" ] }, { @@ -430,15 +432,13 @@ } }, "source": [ - "## pybind11" + "## pybind11\n", + "\n", + "* lightweight header-only library to create interfaces to modern C++ code\n", + "* highlights: STL, iterators, classes and inheritance, smart pointers, move semantics, NumPy $\\leftrightarrow$ Eigen, etc.\n", + "* cf. the simple NumPy example in `pybind11`\n", + "* documentation: https://pybind11.readthedocs.io/en/latest/" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": {