Массив указателей с C ++ на NumPy через Cython - PullRequest
4 голосов
/ 21 апреля 2011

У меня есть библиотека на c ++, и я пытаюсь обернуть ее для python, используя Cython.Одна функция возвращает массив трехмерных векторов (float (* x) [3]), и я хочу получить доступ к этим данным из python.Я мог сделать это, выполнив что-то вроде

res = [
    (self.thisptr.x[j][0],self.thisptr.x[j][1],self.thisptr.x[j][2])
    for j in xrange(self.natoms)
]

, но я хотел бы получить доступ к нему как к массиву numpy, поэтому я попытался использовать numpy.array, и это было намного медленнее.Я также пытался

        cdef np.ndarray res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
        cdef int i
        for i in range(self.natoms):
            res[i][0] = self.thisptr.x[i][0]
            res[i][1] = self.thisptr.x[i][1]
            res[i][2] = self.thisptr.x[i][2]

Но примерно в три раза медленнее, чем версия списка.

Есть ли какие-либо предложения о том, как быстрее преобразовать список векторов в простой массив?

Полный код

cimport cython
import numpy as np
cimport numpy as np


ctypedef np.float_t ftype_t
cdef extern from "ccxtc.h" namespace "ccxtc":
    cdef cppclass xtc:
        xtc(char []) except +
        int next()
        int natoms
        float (*x)[3]
        float time


cdef class pyxtc:
    cdef xtc *thisptr

    def __cinit__(self, char fname[]):
        self.thisptr = new xtc(fname)

    def __dealloc__(self):
        del self.thisptr

    property natoms:
        def __get__(self):
            return self.thisptr.natoms

    property x:
        def __get__(self):
            cdef np.ndarray res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
            cdef int i
            for i in range(self.natoms):
                res[i][0] = self.thisptr.x[i][0]
                res[i][1] = self.thisptr.x[i][1]
                res[i][2] = self.thisptr.x[i][2]
            return res
            #return [ (self.thisptr.x[j][0],self.thisptr.x[j][1],self.thisptr.x[j][2])  for j in xrange(self.natoms)]

    @cython.boundscheck(False)
    def next(self):
        return self.thisptr.next()

Ответы [ 2 ]

2 голосов
/ 27 мая 2011
  1. Определите тип разрешения:

    cdef np.ndarray[np.float64_t, ndim=2] res = ...
    
  2. Использовать полный индекс:

    res[i,0] = ...
    
  3. Поворотбез ограничений и сноса

    @cython.boundscheck(False)
    @cython.wraparound(False) 
    
1 голос
/ 21 января 2014

Чтобы суммировать сказанное HYRY и убедиться, что Cython может генерировать код быстрой индексации, попробуйте что-то вроде следующего:

cimport cython
import numpy as np
cimport numpy as np


ctypedef np.float_t ftype_t
cdef extern from "ccxtc.h" namespace "ccxtc":
    cdef cppclass xtc:
        xtc(char []) except +
        int next()
        int natoms
        float (*x)[3]
        float time


cdef class pyxtc:
    cdef xtc *thisptr

    def __cinit__(self, char fname[]):
        self.thisptr = new xtc(fname)

    def __dealloc__(self):
        del self.thisptr

    property natoms:
        def __get__(self):
            return self.thisptr.natoms

    @cython.boundscheck(False)
    @cython.wraparound(False)
    cdef _ndarray_from_x(self):
        cdef np.ndarray[np.float_t, ndim=2] res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
        cdef int i
        for i in range(self.thisptr.natoms):
            res[i,0] = self.thisptr.x[i][0]
            res[i,1] = self.thisptr.x[i][1]
            res[i,2] = self.thisptr.x[i][2]
        return res

    property x:
        def __get__(self):
            return self._ndarray_from_x()

    @cython.boundscheck(False)
    def next(self):
        return self.thisptr.next()

Все, что я сделал, это поместил быстрый материал в метод cdef, поместил в него правильные оптимизирующие декораторы и назвал его внутри свойства __get__(). Вы также должны обязательно ссылаться на self.thisptr.natoms внутри вызова range(), а не на свойство natoms, с которым связано лотов накладных расходов Python.

...