From 5759530a34b84aba95094c99996fd1629e7c3a0f Mon Sep 17 00:00:00 2001
From: Repo Updater <noreply@mpcdf.mpg.de>
Date: Fri, 1 Oct 2021 10:17:28 +0200
Subject: [PATCH] b10c604c polish pybind11 example, add README.md to f2py
 example

---
 .gitignore                                    |  6 ++--
 examples/cython/c_interface/README.md         |  7 ++--
 examples/cython/c_interface/setup.py          |  3 +-
 .../c_interface_shared_object/README.md       |  6 ++--
 .../libhello/Makefile                         |  4 +--
 .../cython/c_interface_shared_object/setup.py |  4 +--
 examples/cython/c_numpy/README.md             |  2 +-
 examples/cython/c_numpy/setup.py              |  7 ++--
 examples/f2py/README.md                       |  9 +++++
 .../f2py/{fibonacci.py => test_fibonacci.py}  |  0
 examples/pybind11/README.md                   |  5 +++
 examples/pybind11/setup.py                    |  5 +++
 examples/pybind11/src/pybex.cpp               | 30 +++++++++++++----
 examples/pybind11/test_pybex.py               | 33 +++++++++++++++++++
 14 files changed, 95 insertions(+), 26 deletions(-)
 create mode 100644 examples/f2py/README.md
 rename examples/f2py/{fibonacci.py => test_fibonacci.py} (100%)
 create mode 100644 examples/pybind11/README.md
 create mode 100755 examples/pybind11/test_pybex.py

diff --git a/.gitignore b/.gitignore
index 5b28155..552d0b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,11 @@
 # directories
-__pycache__
+#__pycache__
 .unison
 .ipynb_checkpoints
 
 # files
-*.so
-*.o
+#*.so
+#*.o
 *.lprof
 *.tmp
 
diff --git a/examples/cython/c_interface/README.md b/examples/cython/c_interface/README.md
index af8026d..ef81727 100644
--- a/examples/cython/c_interface/README.md
+++ b/examples/cython/c_interface/README.md
@@ -2,11 +2,10 @@
 
 ## Features
 
-* Python module "foobar" lives in the directory "foobar" (has `__index__.py`)
+* Python module "foobar.hello" lives in the directory "foobar" (has `__index__.py`)
 * `c_hello.c` contains a hello-world implementation in C
-* `hello.pyx` provides the sub-module 'foobar.hello' and contains the Python interface written in Cython
+* `hello.pyx` provides the module 'foobar.hello' and contains the Python interface written in Cython
 * `setup.py` contains code to compile the extension, both the source files are compiled into the same shared object
-* `setup.py` demonstrates how to implement a clean command
 
 ## Usage
 
@@ -15,5 +14,5 @@
   `python -c "from foobar import hello; hello.say_hello()"`
 * or install the package, and run the extension from anywhere
   `python setup.py install --user`
-* alternatively, run `test_foobar.py` to call the compiled code example
+* run `test_foobar.py` to call the compiled code example
 
diff --git a/examples/cython/c_interface/setup.py b/examples/cython/c_interface/setup.py
index 15a0b89..c8db113 100644
--- a/examples/cython/c_interface/setup.py
+++ b/examples/cython/c_interface/setup.py
@@ -2,7 +2,8 @@ import os
 from setuptools import setup, Extension
 
 ext = Extension("foobar.hello",
-                sources=["foobar/hello.pyx", "foobar/c_hello.c"])
+                sources=["foobar/hello.pyx", "foobar/c_hello.c"]
+)
 
 setup(name="foobar",
       ext_modules=[ext],
diff --git a/examples/cython/c_interface_shared_object/README.md b/examples/cython/c_interface_shared_object/README.md
index dcffb69..8eb9e40 100644
--- a/examples/cython/c_interface_shared_object/README.md
+++ b/examples/cython/c_interface_shared_object/README.md
@@ -6,10 +6,10 @@
 * `hello.pyx` contains the Python interface written in Cython
 * `c_hello.c` contains a hello-world implementation in C, however, in contrast
   to the previous simple example it is located in a different library (.so,
-  shared object) in the directory `libhello`.  Use `make` to generate the
-  shared object.
+  shared object) in the directory `libhello`.
+  Use `make` to generate the shared object in that directory.
 * `setup.py` contains code to compile the extension, demonstrating how to link
-  the shared object in a stable way
+  the shared object in a stable way.
 
 ## Usage
 
diff --git a/examples/cython/c_interface_shared_object/libhello/Makefile b/examples/cython/c_interface_shared_object/libhello/Makefile
index e0f11f1..46f10f9 100644
--- a/examples/cython/c_interface_shared_object/libhello/Makefile
+++ b/examples/cython/c_interface_shared_object/libhello/Makefile
@@ -1,8 +1,8 @@
 all: libhello.so
 
 libhello.so: c_hello.c
-	gcc -Wall -fPIC -c c_hello.c
-	gcc -shared -o $@ c_hello.o
+	$(CC) -Wall -fPIC -c c_hello.c
+	$(CC) -shared -o $@ c_hello.o
 
 .PHONY: clean
 clean:
diff --git a/examples/cython/c_interface_shared_object/setup.py b/examples/cython/c_interface_shared_object/setup.py
index f19d9cf..1646a16 100644
--- a/examples/cython/c_interface_shared_object/setup.py
+++ b/examples/cython/c_interface_shared_object/setup.py
@@ -1,7 +1,6 @@
 import os
 from setuptools import setup, Extension
 
-
 libhello_dir = os.path.abspath("./libhello")
 
 # We need to specify the location of the include file.
@@ -19,7 +18,8 @@ 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)
+                include_dirs=include_dirs
+)
 
 setup(name="wrap_libhello",
       ext_modules=[ext],
diff --git a/examples/cython/c_numpy/README.md b/examples/cython/c_numpy/README.md
index c3a6dbe..6ccd25c 100644
--- a/examples/cython/c_numpy/README.md
+++ b/examples/cython/c_numpy/README.md
@@ -2,7 +2,7 @@
 
 ## Features
 
-* Python module "ctonumpy" lives in the directory "ctonumpy" (has `__init__.py`)
+* Python module "ctonumpy.cube" lives in the directory "ctonumpy" (has `__init__.py`)
 * `c_cube.c` contains a OMP parallel loop-based computation of the third power of an array
 * `cube.pyx` contains the Python interface written in Cython
 * `setup.py` contains code to compile the extension with optimizing compiler flags
diff --git a/examples/cython/c_numpy/setup.py b/examples/cython/c_numpy/setup.py
index 57009d0..0ee82e9 100644
--- a/examples/cython/c_numpy/setup.py
+++ b/examples/cython/c_numpy/setup.py
@@ -16,14 +16,15 @@ c_flags.append("-fopt-info")
 include_dirs = []
 include_dirs.append(np.get_include())
 
-# extra link flags are possible as well; we leave them empty here
-ld_flags = []
+# extra link flags are possible as well; here we use the same as for compilation to link to libgomp
+ld_flags = c_flags
 
 ext = Extension("ctonumpy.cube",
                 sources=["ctonumpy/cube.pyx", "ctonumpy/c_cube.c"],
                 extra_compile_args=c_flags,
                 extra_link_args=ld_flags,
-                include_dirs=[np.get_include()])
+                include_dirs=[np.get_include()]
+)
 
 setup(name="ctonumpy",
       ext_modules=[ext],
diff --git a/examples/f2py/README.md b/examples/f2py/README.md
new file mode 100644
index 0000000..1738f5f
--- /dev/null
+++ b/examples/f2py/README.md
@@ -0,0 +1,9 @@
+# Minimal example on how-to interface with Fortran code using 'f2py'
+
+Options:
+
+* compile on the command line using `f2py`, cf. 'compile_with_f2py.sh'
+* compile using 'setup.py', using the command `python setup.py build_ext --inplace`
+
+Run the module using 'test_fibonacci.py'.
+
diff --git a/examples/f2py/fibonacci.py b/examples/f2py/test_fibonacci.py
similarity index 100%
rename from examples/f2py/fibonacci.py
rename to examples/f2py/test_fibonacci.py
diff --git a/examples/pybind11/README.md b/examples/pybind11/README.md
new file mode 100644
index 0000000..4a8cf22
--- /dev/null
+++ b/examples/pybind11/README.md
@@ -0,0 +1,5 @@
+# Minimal example showing how to wrap C/C++ code into Python/Numpy using pybind11
+
+* you may need to run `pip install --user pybind11` if not yet installed
+* run `python test_pybex.py` to see the 'pybex' module in action
+
diff --git a/examples/pybind11/setup.py b/examples/pybind11/setup.py
index 5ef0a12..402e322 100644
--- a/examples/pybind11/setup.py
+++ b/examples/pybind11/setup.py
@@ -2,10 +2,15 @@ from setuptools import setup
 # run `pip install --user pybind11` if not yet installed
 from pybind11.setup_helpers import Pybind11Extension, build_ext
 
+# extra compile/link args may be injected, e.g. here for optimization and openmp with gcc
+extra_gcc_args=['-O3', '-march=native', '-fopenmp']
+
 ext_modules = [
     Pybind11Extension(
         "pybex",
         ["src/pybex.cpp",],
+        extra_compile_args=extra_gcc_args,
+        extra_link_args=extra_gcc_args
     ),
 ]
 
diff --git a/examples/pybind11/src/pybex.cpp b/examples/pybind11/src/pybex.cpp
index 3700913..8f1f90b 100644
--- a/examples/pybind11/src/pybex.cpp
+++ b/examples/pybind11/src/pybex.cpp
@@ -1,29 +1,45 @@
 // pybind11 example module 'pybex'
 
+#include <vector>
+#include <numeric>
+#include <functional>
+
 #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) {
+    #pragma omp parallel for simd default(none) shared(v_in, v_out, n_elem)
     for (size_t i=0; i<n_elem; ++i) {
         v_out[i] = v_in[i] * v_in[i] * v_in[i];
     }
 }
 
+
+namespace py = pybind11;
+
 // 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)
+py::array_t<double> py_cube(py::array_t<double, py::array::c_style> np_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;
+    // obtain information about the input array
+    py::buffer_info pybuf_in = np_in.request();
+    auto shape = pybuf_in.shape;
+
+    // create output NumPy array of the same size and dimension
+    py::array_t<double, py::array::c_style> np_out(shape);
 
-  c_cube(v_in.data(), v_out_ptr, v_in.size());
+    // call C/C++ function with proper arguments
+    double * pybuf_in_ptr = (double*) pybuf_in.ptr;
+    double * pybuf_out_ptr = (double*) np_out.request().ptr;
+    size_t count = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
+    c_cube(pybuf_in_ptr, pybuf_out_ptr, count);
 
-  return v_out;
+    return np_out;
 }
 
+
+// define Python module, expose py_cube function as "cube" to python
 PYBIND11_MODULE(pybex, m) {
     m.doc() = "pybind11 example module"; // module docstring
 
diff --git a/examples/pybind11/test_pybex.py b/examples/pybind11/test_pybex.py
new file mode 100755
index 0000000..932fa15
--- /dev/null
+++ b/examples/pybind11/test_pybex.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import numpy as np
+from time import time
+from pybex import cube
+
+
+def test_pybex_cube(N=384):
+    v = np.full((N, N, N), fill_value=2.0)
+
+    # (1) compute the result using our C extension
+    t0 = time()
+    w_ext = cube(v)
+    dt0 = time() - t0
+
+    # (2) compute the result using NumPy, note the relative inefficiency
+    t0 = time()
+    w_ref = v * v * v
+    dt1 = time() - t0
+
+    # (3) compare both the results, be careful with NumPy temporary arrays
+    try:
+        assert(np.allclose(w_ext, w_ref))
+        #assert(np.allclose(w_ext[-1,-1,-1], w_ref[-1,-1,-1]))
+    except:
+        raise
+    else:
+        print("OK! t(C)=%f, t(NumPy)=%f" % (dt0, dt1))
+
+
+if __name__ == "__main__":
+    test_pybex_cube()
+
-- 
GitLab