web-dev-qa-db-ja.com

入力と出力のためにC関数にNumpy配列を渡す

ああ、私の言葉はばかです。関数を呼び出すときに2番目と3番目の引数を省略していました。馬鹿のように。それが私だからです。元の愚かな質問は次のとおりです。

これは非常に一般的なことのように思えますが、関連するチュートリアルが見つからず、Numpyctypesを自分で理解するにはあまりにも無知です。

ファイルctest.cにC関数があります。

#include <stdio.h>

void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {
    //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {
    const double * indata = (double *) indatav;
    double * outdata = (double *) outdatav;
    int i;
    puts("Here we go!");
    for (i = 0; i < rowcount * colcount; ++i) {
        outdata[i] = indata[i] * 2;
    }
    puts("Done!");
}

(ご想像のとおり、もともと引数はvoid *ではなくdouble *でしたが、Python側で何をすべきかわかりませんでした。確かに変更したいと思います。戻るが、私はそれが機能する限り好き嫌いではない。)

共有ライブラリを作成します。 gcc -fPIC -shared -o ctest.so ctest.c

それからPythonには、いくつかのnumpy配列があり、それらを入力として1つ、出力として1つ、C関数に渡したいと思います。

indata = numpy.ones((5,6), dtype=numpy.double)
outdata = numpy.zeros((5,6), dtype=numpy.double)
lib = ctypes.cdll.LoadLibrary('./ctest.so')
fun = lib.cfun
# Here comes the fool part.
fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

print 'indata: %s' % indata
print 'outdata: %s' % outdata

これはエラーを報告しませんが、出力します

>>> Here we go!
Done!
indata: [[ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]]
outdata: [[ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]]

Outdata配列は変更されません。実際、関数を再度呼び出すと、セグメンテーション違反が発生します。それは私を驚かせません-私は本当にここで何をしているのかわかりません。誰かが私を正しい方向に向けることができますか?

48
Tom Future

4つの引数すべてをC関数に渡すだけです。 Pythonコードを以下から変更します:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

に:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))
17
stderr

元の質問に対する直接的な答えではありませんが、関数を呼び出すよりはるかに便利な方法があります。まず、プレーンCで行うのとまったく同じようにC関数のプロトタイプを作成します。rowcountcolcountを別々に必要としないため、それらを単一のsizeパラメーター:

void cfun(const double *indatav, size_t size, double *outdatav) 
{
    size_t i;
    for (i = 0; i < size; ++i)
        outdatav[i] = indatav[i] * 2.0;
}

次に、次の方法でctypesプロトタイプを定義します。

import ctypes
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./ctest.so")
fun = lib.cfun
fun.restype = None
fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
                ctypes.c_size_t,
                ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]

これで、関数の呼び出しが非常に便利になります。

indata = numpy.ones((5,6))
outdata = numpy.empty((5,6))
fun(indata, indata.size, outdata)

ラッパーを定義して、これをさらに便利にすることもできます。

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)
72
Sven Marnach