Skip to content

Commit f4dc5e3

Browse files
committed
Add CMake examples
1 parent 188caf3 commit f4dc5e3

File tree

12 files changed

+248
-8
lines changed

12 files changed

+248
-8
lines changed

.azure-pipelines/unix-build.yml

+19-2
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

+10-2
Original file line numberDiff line numberDiff line change
@@ -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,6 +86,14 @@ 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**
@@ -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

+100
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>`_.
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)
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+
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+
}
+16
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)
+8
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

+10
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+
}
+32
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+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "mymodule.hpp"
2+
#include <pybind11/pybind11.h>
3+
#define FORCE_IMPORT_ARRAY
4+
#include <xtensor-python/pyarray.hpp>
5+
6+
PYBIND11_MODULE(mymodule, m)
7+
{
8+
xt::import_numpy();
9+
m.doc() = "Test module for xtensor python bindings";
10+
m.def("times_dimension", &mymodule::times_dimension<xt::pyarray<double>>);
11+
}

docs/source/index.rst

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ What are ``xtensor`` and ``xtensor-python``?
1616
- ``xtensor`` is a C++ library for multi-dimensional arrays enabling numpy-style broadcasting and lazy computing.
1717
- ``xtensor-python`` enables inplace use of numpy arrays with all the benefits from ``xtensor``
1818

19-
- C++ universal functions and broadcasting
19+
- C++ universal functions and broadcasting
2020
- STL - compliant APIs.
2121

2222

@@ -62,6 +62,7 @@ This software is licensed under the BSD-3-Clause license. See the LICENSE file f
6262
basic_usage
6363
array_tensor
6464
numpy_capi
65+
examples
6566
cookiecutter
6667

6768
.. toctree::
@@ -79,7 +80,7 @@ This software is licensed under the BSD-3-Clause license. See the LICENSE file f
7980

8081
.. _NumPy: http://www.numpy.org
8182
.. _`Buffer Protocol`: https://docs.python.org/3/c-api/buffer.html
82-
.. _`numpy to xtensor cheat sheet`: http://xtensor.readthedocs.io/en/latest/numpy.html
83+
.. _`numpy to xtensor cheat sheet`: http://xtensor.readthedocs.io/en/latest/numpy.html
8384
.. _xtensor: https://github.com/xtensor-stack/xtensor
84-
.. _pybind11: https://github.com/pybind/pybind11
85-
.. _xtensor-python-cookiecutter: https://github.com/xtensor-stack/xtensor-python-cookiecutter
85+
.. _pybind11: https://github.com/pybind/pybind11
86+
.. _xtensor-python-cookiecutter: https://github.com/xtensor-stack/xtensor-python-cookiecutter

0 commit comments

Comments
 (0)