pybind11を使ってみる

## はじめに
高速化や昔のプログラムの流用などで、PythonからのC++呼び出したくなることがある。
前々から興味があった[pybind11](https://pybind11.readthedocs.io/en/master/index.html)を試してみる。
この記事では公式PDF(2018.11.20時点)の4章を説明する。

## 環境
MacMacBook Pro (13-inch, Late 2016)
MacOS 10.12.6 Sierra

Python
pyenv 1.2.8
python 3.6.4

C++
clang-900.0.39.2

## 作業1: (4.3 Creating bindings for a simple function)

```shell
> pip install pybind11
```
でinstallできる。お手軽。
2018年11月20日の時点でinstallされるのはv2.2.4

とりあえず、説明の[PDF](https://media.readthedocs.org/pdf/pybind11/master/pybind11.pdf)の4章から見てみる。

```example.cpp
#include <pybind11/pybind11.h>

int add(int i, int j) {
  return i + j;
}

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring
  m.def("add", &add, "A function which adds two numbers");
}
```

を作って、

```shell
> clang++ -O3 -Wall -shared -std=c++11 -fPIC `python -m pybind11 --includes` -undefined dynamic_lookup example.cpp -o example`python3-config --extension-suffix`
```

そうすると、`example.cpython-36m-darwin.so`みたいなファイルが出来上がる。
なお、Macでやる時だけかもしれないけれど、`-undefined dynamic_lookup`のオプションをつけないとerrorが出るので注意。

さて、あとはPythonで

```python
>>> import example
>>> example.__doc__
'pybind11 example plugin'
>>> print(example.add.__doc__)
add(arg0: int, arg1: int) -> int

A function which adds two numbers
>>> example.add(1, 2)
3
>>> example.add(45, 23)
68
```

のようになる。

## 作業2: 脱線 速度計測
簡単な例で比較してみる。まずはpython側の重い和

``` test_py.py
from timeit import timeit

def sum_up(n):
    sum_ = 0
    for i in range(n):
        sum_ += i
    return sum_

if __name__ == '__main__':
    n_itr = 100
    n_sum = 10_000
    ave_ = timeit(lambda: sum_up(n_sum), number=n_itr)/n_itr
    print(f"{n_itr} loops, the average time is {ave_ * 1_000} ms.")
```

```shell
> python test_py.py
100 loops, the average time is 0.5563870701007545 ms.
```
次にC++側

```example2.cpp
#include <pybind11/pybind11.h>

int sum(int n) {
  int sum_ = 0;
  for(int i = 0; i < n; ++i){
    sum_ += i;
  }
  return sum_;
}

PYBIND11_MODULE(example2, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring
  m.def("sum", &sum, "A function which calcurates the sum of the sequence.");
}
```

``` test_cpp.py
from timeit import timeit
import example2

if __name__ == '__main__':
    n_itr = 100
    n_sum = 10_000
    ave_ = timeit(lambda: example2.sum(n_sum), number=n_itr)/n_itr
    print(f"{n_itr} loops, the average time is {ave_ * 1_000} ms.")
```

```shell
> python test_cpp.py
100 loops, the average time is 0.00564772984944284 ms.
```

556 µs vs 5.65 µsでだいたい100倍ぐらい？
ものすごく簡単な実装だし、for loopっていうpython不利な形でこれぐらいの差。

## 作業３:(4.4 Keyword arguments)
keyword argumentsを実装してみる。
作業１の`example.cpp`の実装では、

```python
>>> import example
>>> example.add(i=2, j=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add(): incompatible function arguments. The following argument types are supported:
    1. (arg0: int, arg1: int) -> int

Invoked with: kwargs: i=2, j=3
```
となってkeyword argsを受け付けてくれない。
pythonの関数は普通受け付けてくれるので、これをできるようにする。
次のように`example.cpp`を書き直す。

```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
        return i + j;
}

PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin";
        m.def("add", &add, "A function which adds two numbers",
              py::arg("i"), py::arg("j"));
}
```
すると、

```python
>>> import example
>>> example.add(i=2, j=3)
5
```
と受け付けるようになる。引数がどっちがどっちかわからなくなるので、引き算の`sub`関数も追加する。

```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
        return i + j;
}

int sub(int i, int j) {
        return i - j;
}

PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin";
        m.def("add", &add, "A function which adds two numbers",
              py::arg("i"), py::arg("j"));
        m.def("sub", &sub, "A function which subtracts the second arg from the first arg",
              py::arg("i"), py::arg("j"));
}
```
``` python
>>> import example
>>> example.sub(5, 2)
3
>>> example.sub(i=3, j=2)
1
>>> example.sub(j=3, i=2)  # jを先に入力した。
-1
```
ちなみに引数の名前は`i`, `j`にする必要はなく、


```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
        return i + j;
}

int sub(int i, int j) {
        return i - j;
}

PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin";
        m.def("add", &add, "A function which adds two numbers",
              py::arg("i"), py::arg("j"));
        m.def("sub", &sub, "A function which subtracts the second arg from the first arg",
              py::arg("minuend"), py::arg("subtrahend"));
}
```
とすれば、

```python
>>> import example
>>> example.sub(minuend=5, subtrahend=1)
4
>>> example.sub(subtrahend=1, minuend=19)
18
```
とできる。
なお、

```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
        return i + j;
}

int sub(int i, int j) {
        return i - j;
}

PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin";
        m.def("add", &add, "A function which adds two numbers",
              py::arg("i"), py::arg("j"));
        m.def("sub", &sub, "A function which subtracts the second arg from the first arg",
              py::arg("minuend"), py::arg("subtrahend"));
        using namespace pybind11::literals;
        m.def("sub2", &sub, "A function which subtracts the second arg from the first arg",
              "i"_a, "j"_a);
}
```
の`example.sub2`のように簡単な記法でkeyword argsを定義することもできる。

```python
>>> import example
>>> example.sub2(3, 2)
1
>>> example.sub2(i=6, j=2)
4
```

## 作業4:(4.5 Default arguments)
default argumentsを実装してみる。
pythonの関数定義ではkeyword argsにdefault valueを設定することがよくある。
例えば

```python
def func(i = 1, j = 2):
    return (i + 2) * j

func()
# ==> 6
func(1, 3)
# ==> 9
func(3)  # i = 3, j = 2
# ==> 10
func(j=3)
# ==> 9
```
という感じになる。これを。pybind11を用いて表そうとすると、次のようになる。

```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
using namespace pybind11::literals;

int func(int i, int j){
  return (i + 2) * j;
}

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin";
  // regular notation
  m.def("func1", &func, "A function",
        py::arg("i") = 1, py::arg("j") = 2);
  // shorthand
  m.def("func2", &func, "A function",
        "i"_a = 1, "j"_a = 2);
}
```

`arg("i") = 3`のように `def`の中に記述すれば良い。

## 作業5 (4.6 Exporting variables)
pythonのmoduleには定数なのが設定されているものがある。それらは、`attr` 関数を使うと表現することができる。

```example.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin";
  // integer
  m.attr("val1") = 34;
  // float/double
  m.attr("val2") = 3.4;
  // str
  m.attr("val3") = "This is 340.";
  // str
  py::object val4 = py::cast("This is another 340.");
  m.attr("val4") = val4;
}
```
これをコンパイルするとpythonの中で次のように呼べる。

```python
>>> import example
>>> example.val1
34
>>> example.val2
3.4
>>> example.val3
'This is 340.'
>>> example.val4
'This is another 340.'
```

## TODO
* 別の記事でC++側のobjectをbindする方法やnumpyやEigenをbindする方法などがPDFに書かれていたのでそれをやる。


## 参考資料
公式ページ([link](https://pybind11.readthedocs.io/en/master/index.html))
公式PDF([link](https://media.readthedocs.org/pdf/pybind11/master/pybind11.pdf))
[[python高速化]pybind11によるc++組み込み](https://qiita.com/exy81/items/e309df7e33d4ff20a91a)