Подтвердить что ты не робот

Передача набора массивов NumPy в функцию C для ввода и вывода

Предположим, что у нас есть C-функция, которая принимает набор из одного или нескольких входных массивов, обрабатывает их и записывает свой вывод в набор выходных массивов. Подпись выглядит следующим образом (с count, представляющим количество обрабатываемых элементов массива):

void compute (int count, float** input, float** output)

Я хочу вызвать эту функцию из Python через ctypes и использовать ее для применения преобразования к набору массивов NumPy. Для функции с одним входом/одним выходом, определенной как

void compute (int count, float* input, float* output)

выполняются следующие работы:

import ctypes
import numpy

from numpy.ctypeslib import ndpointer

lib = ctypes.cdll.LoadLibrary('./block.so')
fun = lib.compute
fun.restype = None
fun.argtypes = [ctypes.c_int,
                ndpointer(ctypes.c_float),
                ndpointer(ctypes.c_float)]

data = numpy.ones(1000).astype(numpy.float32)
output = numpy.zeros(1000).astype(numpy.float32)
fun(1000, data, output)

Однако я не знаю, как создать соответствующий массив указателей для нескольких входов (и/или выходов). Любые идеи?

Изменить. Поэтому люди задавались вопросом, как compute знает, сколько ожидающих указателей массивов (поскольку count относится к числу элементов в массиве). Это, по сути, жестко закодировано; данный compute точно знает, сколько ожидаемых входов и выходов. Это задание вызывающего абонента для проверки того, что input и output указывают на правильное количество входов и выходов. Здесь пример compute, содержащий 2 входа и запись в 1 выходной массив:

virtual void compute (int count, float** input, float** output) {
    float* input0 = input[0];
    float* input1 = input[1];
    float* output0 = output[0];
    for (int i=0; i<count; i++) {
        float fTemp0 = (float)input1[i];
        fRec0[0] = ((0.09090909090909091f * fTemp0) + (0.9090909090909091f * fRec0[1]));
        float fTemp1 = (float)input0[i];
        fRec1[0] = ((0.09090909090909091f * fTemp1) + (0.9090909090909091f * fRec1[1]));
        output0[i] = (float)((fTemp0 * fRec1[0]) - (fTemp1 * fRec0[0]));
        // post processing
        fRec1[1] = fRec1[0];
        fRec0[1] = fRec0[0];
    }
}

Я не могу повлиять на подпись и реализацию compute. Я могу проверить (от Python!), Сколько входов и выходов требуется. Основная проблема заключается в том, как дать правильную функцию argtypes для функции и как создать соответствующие структуры данных в NumPy (массив указателей на массивы NumPy).

4b9b3361

Ответ 1

Чтобы сделать это специально с массивами Numpy, вы можете использовать:

import numpy as np
import ctypes

count = 5
size = 1000

#create some arrays
arrays = [np.arange(size,dtype="float32") for ii in range(count)] 

#get ctypes handles
ctypes_arrays = [np.ctypeslib.as_ctypes(array) for array in arrays]

#Pack into pointer array
pointer_ar = (ctypes.POINTER(C.c_float) * count)(*ctypes_arrays)

ctypes.CDLL("./libfoo.so").foo(ctypes.c_int(count), pointer_ar, ctypes.c_int(size))

Где может выглядеть C-сторона вещей:

# function to multiply all arrays by 2
void foo(int count, float** array, int size)
{
   int ii,jj;
   for (ii=0;ii<count;ii++){
      for (jj=0;jj<size;jj++)
         array[ii][jj] *= 2;    
   }

}

Ответ 2

В C, float** указывает на первый элемент в таблице/массиве указателей float*.

Предположительно каждый из этих float* указывает на первый элемент в таблице/массиве значений float.

В объявлении вашей функции есть 1 счетчик, однако неясно, к чему относится этот счет:

void compute (int count, float** input, float** output)
  • Двумерная матрица count x count по размеру?
  • count -размерный массив float*, каждый из которых каким-то образом завершен, например. с nan?
  • массив с нулевым завершением float* каждого из элементов count (разумное предположение)?

Пожалуйста, уточните свой вопрос, и я уточню свой ответ: -)

Предполагая последнюю интерпретацию API, здесь моя примерная вычислительная функция:

/* null-terminated array of float*, each points to count-sized array
*/
extern void compute(int count, float** in, float** out)
{
    while (*in)
    {
        for (int i=0; i<count; i++)
        {
            (*out)[i] = (*in)[i]*42;
        }
        in++; out++;
    }
}

Тестовый код для функции вычисления выборки:

#include <stdio.h>
extern void compute(int count, float** in, float** out);

int main(int argc, char** argv)
{
#define COUNT 3
    float ina[COUNT] = { 1.5, 0.5, 3.0 };
    float inb[COUNT] = { 0.1, -0.2, -10.0 };
    float outa[COUNT];
    float outb[COUNT];
    float* in[] = {ina, inb, (float*)0};
    float* out[] = {outa, outb, (float*)0};

    compute(COUNT, in, out);

    for (int row=0; row<2; row++)
        for (int c=0; c<COUNT; c++)
            printf("%d %d %f %f\n", row, c, in[row][c], out[row][c]);
    return 0;
}

И как вы используете то же самое с помощью ctypes в Python для count == 10 float subarrays и массива размера 2 float*, содержащего 1 действительный субарей и NULL-терминатор:

import ctypes

innertype = ctypes.ARRAY(ctypes.c_float, 10)
outertype = ctypes.ARRAY(ctypes.POINTER(ctypes.c_float), 2)

in1 = innertype(*range(10))
in_ = outertype(in1, None)
out1 = innertype(*range(10))
out = outertype(out1, None)

ctypes.CDLL("./compute.so").compute(10, in_, out)

for i in range(10): print in_[0][i], out[0][i]

Интерфейс Numpy для ctypes описан здесь http://www.scipy.org/Cookbook/Ctypes#head-4ee0c35d45f89ef959a7d77b94c1c973101a562f, arr.ctypes.shape [:] arr.ctypes.strides [:] и arr.ctypes.data - то, что вам нужно; вы можете отправить это прямо на ваш compute.

Вот пример:

In [55]: a = numpy.array([[0.0]*10]*2, dtype=numpy.float32)

In [56]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0]
Out[56]: 0.0

In [57]: ctypes.cast(a.ctypes.data, ctypes.POINTER(ctypes.c_float))[0] = 1234

In [58]: a
Out[58]: 
array([[ 1234.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.],
       [    0.,     0.,     0.,     0.,     0.,     0.,     0.,     0.,
            0.,     0.]], dtype=float32)