Skip to content

Commit b013cee

Browse files
authored
Merge pull request #256 from tdegeus/example
Add (CMake) example
2 parents aa916ec + f4dc5e3 commit b013cee

File tree

12 files changed

+253
-13
lines changed

12 files changed

+253
-13
lines changed

.azure-pipelines/unix-build.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ steps:
1111
mkdir build
1212
cd build
1313
cmake -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DPYTHON_EXECUTABLE=`which python` -DDOWNLOAD_GTEST=ON $(Build.SourcesDirectory)
14+
make install
1415
displayName: Configure xtensor-python
1516
workingDirectory: $(Build.BinariesDirectory)
16-
17+
1718
- script: |
1819
source activate xtensor-python
1920
make -j2 test_xtensor_python
2021
displayName: Build xtensor-python
2122
workingDirectory: $(Build.BinariesDirectory)/build
22-
23+
2324
- script: |
2425
source activate xtensor-python
2526
cd test
@@ -32,3 +33,19 @@ steps:
3233
py.test -s
3334
displayName: Test xtensor-python (Python)
3435
workingDirectory: $(Build.SourcesDirectory)
36+
37+
- script: |
38+
source activate xtensor-python
39+
cmake . -DPYTHON_EXECUTABLE=`which python`
40+
cmake --build .
41+
python example.py
42+
displayName: Example - readme 1
43+
workingDirectory: $(Build.SourcesDirectory)/docs/source/examples/readme_example_1
44+
45+
- script: |
46+
source activate xtensor-python
47+
cmake . -DPYTHON_EXECUTABLE=`which python`
48+
cmake --build .
49+
python example.py
50+
displayName: Example - SFINAE
51+
workingDirectory: $(Build.SourcesDirectory)/docs/source/examples/sfinae

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ Both containers enable the numpy-style APIs of xtensor (see [the numpy to xtenso
4949

5050
```cpp
5151
#include <numeric> // Standard library import for std::accumulate
52-
#include "pybind11/pybind11.h" // Pybind11 import to define Python bindings
53-
#include "xtensor/xmath.hpp" // xtensor import for the C++ universal functions
52+
#include <pybind11/pybind11.h> // Pybind11 import to define Python bindings
53+
#include <xtensor/xmath.hpp> // xtensor import for the C++ universal functions
5454
#define FORCE_IMPORT_ARRAY
55-
#include "xtensor-python/pyarray.hpp" // Numpy bindings
55+
#include <xtensor-python/pyarray.hpp> // Numpy bindings
5656

5757
double sum_of_sines(xt::pyarray<double>& m)
5858
{
@@ -77,7 +77,7 @@ import xtensor_python_test as xt
7777
7878
v = np.arange(15).reshape(3, 5)
7979
s = xt.sum_of_sines(v)
80-
s
80+
print(s)
8181
```
8282

8383
**Outputs**
@@ -86,14 +86,22 @@ s
8686
1.2853996391883833
8787
```
8888

89+
**Working example**
90+
91+
Get the working example here:
92+
93+
* [`CMakeLists.txt`](docs/source/examples/readme_example_1/CMakeLists.txt)
94+
* [`main.cpp`](docs/source/examples/readme_example_1/main.cpp)
95+
* [`example.py`](docs/source/examples/readme_example_1/example.py)
96+
8997
### Example 2: Create a universal function from a C++ scalar function
9098

9199
**C++ code**
92100

93101
```cpp
94-
#include "pybind11/pybind11.h"
102+
#include <pybind11/pybind11.h>
95103
#define FORCE_IMPORT_ARRAY
96-
#include "xtensor-python/pyvectorize.hpp"
104+
#include <xtensor-python/pyvectorize.hpp>
97105
#include <numeric>
98106
#include <cmath>
99107

@@ -122,7 +130,7 @@ import xtensor_python_test as xt
122130
x = np.arange(15).reshape(3, 5)
123131
y = [1, 2, 3, 4, 5]
124132
z = xt.vectorized_func(x, y)
125-
z
133+
print(z)
126134
```
127135

128136
**Outputs**

docs/source/examples.rst

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
2+
****************
3+
(CMake) Examples
4+
****************
5+
6+
Basic example (from readme)
7+
===========================
8+
9+
Consider the following C++ code:
10+
11+
:download:`main.cpp <examples/readme_example_1/main.cpp>`
12+
13+
.. literalinclude:: examples/readme_example_1/main.cpp
14+
:language: cpp
15+
16+
There are several options to build the module,
17+
whereby we will CMake here with the following ``CMakeLists.txt``:
18+
19+
:download:`CMakeLists.txt <examples/readme_example_1/CMakeLists.txt>`
20+
21+
.. literalinclude:: examples/readme_example_1/CMakeLists.txt
22+
:language: cmake
23+
24+
Then we can test the module:
25+
26+
:download:`example.py <examples/readme_example_1/example.py>`
27+
28+
.. literalinclude:: examples/readme_example_1/example.py
29+
:language: cmake
30+
31+
.. note::
32+
33+
Since we did not install the module,
34+
we should compile and run the example from the same folder.
35+
To install, please consult
36+
`this pybind11 / CMake example <https://github.com/pybind/cmake_example>`_.
37+
38+
39+
Type restriction with SFINAE
40+
============================
41+
42+
.. seealso::
43+
44+
`Medium post by Johan Mabille <https://medium.com/@johan.mabille/designing-language-bindings-with-xtensor-f32aa0f20db>`__
45+
This example covers "Option 4".
46+
47+
In this example we will design a module with a function that accepts an ``xt::xtensor`` as argument,
48+
but in such a way that an ``xt::pyxtensor`` can be accepted in the Python module.
49+
This is done by having a templated function
50+
51+
.. code-block:: cpp
52+
53+
template <class T>
54+
void times_dimension(T& t);
55+
56+
As this might be a bit too permissive for your liking, we will show you how to limit the
57+
scope to *xtensor* types, and allow other overloads using the principle of SFINAE
58+
(Substitution Failure Is Not An Error).
59+
In particular:
60+
61+
:download:`mymodule.hpp <examples/sfinae/mymodule.hpp>`
62+
63+
.. literalinclude:: examples/sfinae/mymodule.hpp
64+
:language: cpp
65+
66+
Consequently from C++, the interaction with the module's function is trivial
67+
68+
:download:`main.cpp <examples/sfinae/main.cpp>`
69+
70+
.. literalinclude:: examples/sfinae/main.cpp
71+
:language: cpp
72+
73+
For the Python module we just have to specify the template to be
74+
``xt::pyarray`` or ``xt::pytensor``. E.g.
75+
76+
:download:`src/python.cpp <examples/sfinae/python.cpp>`
77+
78+
.. literalinclude:: examples/sfinae/python.cpp
79+
:language: cpp
80+
81+
We will again use CMake to compile, with the following ``CMakeLists.txt``:
82+
83+
:download:`CMakeLists.txt <examples/sfinae/CMakeLists.txt>`
84+
85+
.. literalinclude:: examples/sfinae/CMakeLists.txt
86+
:language: cmake
87+
88+
Then we can test the module:
89+
90+
:download:`example.py <examples/readme_example_1/example.py>`
91+
92+
.. literalinclude:: examples/readme_example_1/example.py
93+
:language: cmake
94+
95+
.. note::
96+
97+
Since we did not install the module,
98+
we should compile and run the example from the same folder.
99+
To install, please consult
100+
`this pybind11 / CMake example <https://github.com/pybind/cmake_example>`_.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 3.1..3.19)
2+
3+
project(mymodule)
4+
5+
find_package(pybind11 CONFIG REQUIRED)
6+
find_package(xtensor REQUIRED)
7+
find_package(xtensor-python REQUIRED)
8+
find_package(Python REQUIRED COMPONENTS NumPy)
9+
10+
pybind11_add_module(mymodule main.cpp)
11+
target_link_libraries(mymodule PUBLIC pybind11::module xtensor-python Python::NumPy)
12+
13+
target_compile_definitions(mymodule PRIVATE VERSION_INFO=0.1.0)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import mymodule
2+
import numpy as np
3+
4+
a = np.array([1, 2, 3])
5+
assert np.isclose(np.sum(np.sin(a)), mymodule.sum_of_sines(a))
6+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <numeric>
2+
#include <xtensor.hpp>
3+
#include <pybind11/pybind11.h>
4+
#define FORCE_IMPORT_ARRAY
5+
#include <xtensor-python/pyarray.hpp>
6+
7+
double sum_of_sines(xt::pyarray<double>& m)
8+
{
9+
auto sines = xt::sin(m); // sines does not actually hold values.
10+
return std::accumulate(sines.begin(), sines.end(), 0.0);
11+
}
12+
13+
PYBIND11_MODULE(mymodule, m)
14+
{
15+
xt::import_numpy();
16+
m.doc() = "Test module for xtensor python bindings";
17+
m.def("sum_of_sines", sum_of_sines, "Sum the sines of the input values");
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cmake_minimum_required(VERSION 3.1..3.19)
2+
3+
project(mymodule)
4+
5+
find_package(pybind11 CONFIG REQUIRED)
6+
find_package(xtensor REQUIRED)
7+
find_package(xtensor-python REQUIRED)
8+
find_package(Python REQUIRED COMPONENTS NumPy)
9+
10+
pybind11_add_module(mymodule python.cpp)
11+
target_link_libraries(mymodule PUBLIC pybind11::module xtensor-python Python::NumPy)
12+
13+
target_compile_definitions(mymodule PRIVATE VERSION_INFO=0.1.0)
14+
15+
add_executable(myexec main.cpp)
16+
target_link_libraries(myexec PUBLIC xtensor)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import mymodule
2+
import numpy as np
3+
4+
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64)
5+
b = np.array(a, copy=True)
6+
mymodule.times_dimension(b) # changing in-place!
7+
assert np.allclose(2 * a, b)
8+

docs/source/examples/sfinae/main.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "mymodule.hpp"
2+
#include <xtensor/xio.hpp>
3+
4+
int main()
5+
{
6+
xt::xtensor<size_t, 2> a = xt::arange<size_t>(2 * 3).reshape({2, 3});
7+
mymodule::times_dimension(a);
8+
std::cout << a << std::endl;
9+
return 0;
10+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <xtensor/xtensor.hpp>
2+
3+
namespace mymodule {
4+
5+
template <class T>
6+
struct is_std_vector
7+
{
8+
static const bool value = false;
9+
};
10+
11+
template <class T>
12+
struct is_std_vector<std::vector<T> >
13+
{
14+
static const bool value = true;
15+
};
16+
17+
// any xtensor object
18+
template <class T, std::enable_if_t<xt::is_xexpression<T>::value, bool> = true>
19+
void times_dimension(T& t)
20+
{
21+
using value_type = typename T::value_type;
22+
t *= (value_type)(t.dimension());
23+
}
24+
25+
// an std::vector
26+
template <class T, std::enable_if_t<is_std_vector<T>::value, bool> = true>
27+
void times_dimension(T& t)
28+
{
29+
// do nothing
30+
}
31+
32+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy