Как ввести функцию в аргумент функции в Cython - PullRequest
0 голосов
/ 10 сентября 2018

Я сделал функцию для оптимизации в Python (назовем ее optimizer). Требуется оптимизировать функцию (назовем ее objective) в качестве одного из аргументов функции. objective - это функция, которая принимает одномерный np.ndarray и возвращает число float (которое совпадает с double в C ++?).

Я прочитал этот пост , но я не уверен, действительно ли это та же проблема, что и у меня, и когда я использую ctypedef int (*f_type)(int, str), но я получаю ошибку Cannot convert 'f_type' to Python object во время компиляции. Это работает только для функций C? Как набрать функцию Python?

Редактировать: Как выглядит мой код:

cpdef optimizer(objective, int num_particle, int dim,
             np.ndarray[double, ndim=1] lower_bound,
             np.ndarray[double, ndim=1] upper_bound):

    cdef double min_value
    cdef np.ndarray[double, ndim=2] positions = np.empty((num_particle,dim), dtype=np.double)
    cdef np.ndarray[double, ndim=1] fitness = np.empty(num_particle, dtype=np.double)
    cdef int i, j

    # do lots of stuff not shown here
    # involve the following code:
    for i in range(num_particle):
        fitness[i] = objective(positions[i])

    return min_value

Я хочу знать, можно ли набрать objective, чтобы заставить код работать быстрее.

1 Ответ

0 голосов
/ 10 сентября 2018

Я получаю сообщение об ошибке

Cannot convert Python object argument to type 'f_type'

, что, на мой взгляд, имеет гораздо больший смысл, чем тот, который вы утверждаете получить - вы пытаетесь передать объект Python в функцию. Убедитесь, что сообщения об ошибках, о которых вы сообщаете, действительно генерирует ваш код. Ваше описание типов, которые принимает objective, также не соответствует коду, который вы показываете.


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

Возможный альтернативный подход заключается в наследовании от cdef class с соответствующей функцией cdef, так что вы можете по крайней мере получить соответствующую производительность в определенных случаях:

# an abstract function pointer class
cdef class FPtr:
    cdef double function(self,double[:] x) except? 0.0:
        # I'm assuming you might want to pass exceptions back to Python - use 0.0 to indicate that there might have been an error
        raise NotImplementedError()

# an example class that inherits from the abstract pointer type    
cdef class SumSq(FPtr):
    cdef double function(self,double[:] x) except? 0.0:
        cdef double sum=0.0
        for i in range(x.shape[0]):
            sum += x[i]**2
        return sum

# an example class that just wraps a Python callable
# this will be no faster, but makes the code generically usable
cdef class PyFPtr(FPtr):
    cdef object f
    def __init__(self,f):
        self.f = f

    cdef double function(self,double[:] x) except? 0.0:
        return self.f(x) # will raise an exception if the types don't match

def example_function(FPtr my_callable):
    import numpy as np
    return my_callable.function(np.ones((10,)))

Использование этого example_function(SumSq()) работает как положено (и со скоростью Cython); example_function(PyFPtr(lambda x: x[0])) работает как положено (без скорости Cython в вызываемой); example_function(PyFPtr(lambda x: "hello")) выдает ошибку типа, как и ожидалось.

...