FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Numpy の配列を利用する C モジュールを作る

Python で大規模な配列を扱う作業をするときは Numpy モジュールによくお世話になります.大抵の場合は Numpy が提供する関数で十分な速度が得られますが,パフォーマンスのために配列へのアクセスをネイティブコードに落としたいことがあります.本記事では Python から C に Numpy 配列を渡し,C 側で結果用の配列を確保して返す方法を説明します.

Keywords: Python, Numpy C-API, 拡張モジュール



こちらの方が Numpy の配列を受け取る C モジュールの作り方を説明されています.本記事では新たな配列の作成と,その際の注意点についても説明します.

作業環境
Python 2.6.6
Numpy 1.6.1

1.関数を定義する

引数に2つの配列を受け取り,次元を増やして配列を連結する関数を例として作成します.ソースコードは以下の通りです.

mycalc.c
#include <Python.h>

#include <numpy/arrayobject.h>
#include <numpy/arrayscalars.h>

#include <stdio.h>
#include <stdlib.h>


static PyObject* mycalc_stack_array(PyObject *self, PyObject *args)
{
int err, type;
npy_intp i, ndim, stride;
npy_intp *dim1, *dim2, *dim;
PyObject *array1, *array2, *array;

// 1.1 parse arguments
err = PyArg_ParseTuple(
args, "O!O!",
&PyArray_Type, &array1,
&PyArray_Type, &array2
);
if(!err) return NULL;

// 1.2 type check
if(PyArray_TYPE(array1) != PyArray_TYPE(array2)) {
PyErr_SetString(PyExc_TypeError, "array types are different");
return NULL;
}

type = PyArray_TYPE(array1);

if(PyArray_NDIM(array1) != PyArray_NDIM(array2)) {
PyErr_SetString(PyExc_TypeError, "dimensions of arrays are different");
return NULL;
}

ndim = PyArray_NDIM(array1);

dim1 = PyArray_DIMS(array1);
dim2 = PyArray_DIMS(array2);
for(i=0; i<ndim; i++) {
if(dim1[i] != dim2[i]) {
PyErr_SetString(PyExc_TypeError, "shapes of arrays are different");
return NULL;
}
}

ndim += 1;
dim = (npy_intp*)malloc(ndim * sizeof(npy_intp));
dim[0] = 2;
for(i=1; i<ndim; i++) {
dim[i] = dim1[i-1];
}

// 1.3 create numpy array object
array = PyArray_SimpleNew(ndim, dim, type);
if(array == NULL) return NULL;

stride = PyArray_SIZE(array1) * PyArray_ITEMSIZE(array1);

memcpy(PyArray_BYTES(array), PyArray_DATA(array1), stride);
memcpy(PyArray_BYTES(array) + stride, PyArray_DATA(array2), stride);

free(dim);

return array;
}


static PyMethodDef methods[] = {
{
"stack_array", mycalc_stack_array, METH_VARARGS, ""
},
{NULL, NULL, 0, NULL}
};


PyMODINIT_FUNC initmycalc(void)
{
(void)Py_InitModule("mycalc", methods);
import_array();
}

1.1 配列を C 側に渡す
2つの配列オブジェクトを C 側に渡します.

int err;
PyObject *array1, *array2;

err = PyArg_ParseTuple(
args, "O!O!",
&PyArray_Type, &array1,
&PyArray_Type, &array2
);
if(!err) return NULL;

PyArg_ParseTuple関数で渡されたオブジェクトを取り出します.第2引数についてはドキュメントに書かれています.

1.2 引数チェック
PyArray_TYPE や PyArray_NDIM マクロなどを用いて引数のチェックを行います.

注意:
戻り値が npy_intp となるものは必ず npy_intp 型の変数で受けるようにしてください. npy_intp は 32bit と 64bit で大きさが変わるので,int などで受けると意図しない動作をすることになり,ハマります.

1.3 戻り値
PyArray_SimpleNew で配列オブジェクトを作成します.PyArray_DATA でメモリのポインタを取得してしまえば,データの操作は普通の C と同じです.第2引数は npy_intp 型であることに注意してください.


2.メソッドテーブルを定義する

公開する関数の名前や引数を指定します.下の例は,stack_array という名前で mycalc_stack_array を,すべての引数をタプルで受け取るように指定します.help で関数の説明をみると,"stack numpy array" と表示されます.

static PyMethodDef methods[] = {
{
"stack_array", mycalc_stack_array, METH_VARARGS, "stack numpy array"
},
{NULL, NULL, 0, NULL}
};


3.モジュールの初期化

初期化関数を定義します.関数名は init(Module Name) となります.numpy を扱うためには,Py_InitModule の後に import_array を呼び出す必要があります.

PyMODINIT_FUNC initmycalc(void)
{
(void)Py_InitModule("mycalc", methods);
import_array();
}


4.ビルド

setup.py を書いて拡張モジュールをビルドします.任意のディレクトリ内に setup.py と mycalc.c (拡張モジュールのソースコード)をいれて,次のコマンドを実行してください.

python setup.py build

setup.py
from distutils.core import setup, Extension
from numpy.distutils.misc_util import get_numpy_include_dirs

setup(
package_dir={'':''},
packages=[
],
ext_modules=[
Extension('mycalc',
sources=[
'mycalc.c'
],
include_dirs=[] + get_numpy_include_dirs(),
library_dirs=[],
libraries=[],
extra_compile_args=[],
extra_link_args=[]
)
]
)
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

Ishida Akihiko

Author:Ishida Akihiko
FC2ブログへようこそ!

免責事項
当サイトに掲載する記事内容は,必ずしも正確性,信頼性,妥当性,有用性,完成度などを保証しません.記事の利用はすべて自己責任でお願いします.当サイトに掲載された内容によって発生したいかなる損害に対しても,管理人は一切の責任を負いかねます.
最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
アクセスカウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。