通过python接口传递cython函数 [英] Pass cython functions via python interface

查看:165
本文介绍了通过python接口传递cython函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

能否将 cdef Cython函数从Python脚本传递给另一个(python def )Cython函数? / p>

最小示例:



test_module.pyx

  cpdef min_arg(f,int N):
cdef double x = 100000.
cdef int best_i = -1
$ i在范围(N)中的b $ b:如果f(i)< x:
x = f(i)
best_i = i
return best_i

def py_f(x):
return(x-5)** 2

cdef public api double cy_f(double x):
return(x-5)** 2

test.py

  import pyximport; pyximport.install()
导入测试模块

testmodule.min_arg(testmodule.py_f,100)

这很好用,但我希望也能做到

  testmodule.min_arg(testmodule .cy_f,100)

来自test.py,具有cython的速度(每个都没有Python开销) f(i)电话)。但是显然,Python不了解cy_f,因为它不是声明的 def cpdef



我希望这样的东西存在:

 从scipy import LowLevelCallable 
cy_f = LowLevelCallable.from_cython(testmodule,'cy_f')
testmodule.min_arg(cy_f,100)

但这会导致 TypeError:'LowLevelCallable'对象不可调用



请先谢谢您。

解决方案

LowLevelCallable 是一类必须由底层Python模块。这项工作已经在几个模块上完成,包括正交例程 scipy.integrate.quad



如果您希望使用相同的包装方法,则必须通过SciPy例程来使用它,例如 scipy.ndimage.generic_filter1d scipy.integrate.quad 。但是,代码位于编译后的扩展中。



如果您的问题在回调中定义得很合理,则替代方法是自己实现。我已经在我的代码之一中完成了此操作,因此为了简化起见,发布了链接:


  1. .pxd中文件,我定义接口 cyfunc_d_d https://github.com/pdebuyl/skl1/blob/master/skl1/core.pxd

  2. 我可以在基本 cython模块 https://中重用此接口github.com/pdebuyl/skl1/blob/master/skl1/euler.pyx ,也位于用户定义的模块中。

最终代码进行简单的 cython-cython调用,同时允许在Cython级别传递对象



我针对您的问题修改了代码:


  1. test_interface.pxd

      cdef类cyfunc: 
    cpdef double f(self,double x)

    cdef class pyfunc(cyfunc):
    cdef对象py_f
    cpdef double f(self,double x)


  2. test_interface.pyx

      cdef类cyfunc:
    cpdef double f(self,double x):
    return 0
    def __cinit__ (self):
    pass


    cdef class pyfunc(cyfunc):
    cpdef double f(self,double x):
    return self.py_f (x)
    def __init __(self,f):
    self .py_f = f


  3. setup.py

     从setuptools导入安装程序,扩展
    从Cython生成导入cythonize

    设置(
    ext_modules = cythonize((Extension('test_interface',[ test_interface.pyx]),
    Extension('test_module',[ test_module.pyx]))
    ))


  4. test_module.pyx

     来自test_interface cimport cyfunc,pyfunc 

    cpdef min_arg(f,int N):
    cdef double x =100000。
    cdef int best_i = -1
    cdef int i
    cdef double current_value

    cdef cyfunc py_f

    if isinstance(f,cyfunc):
    py_f = f
    print('cyfunc')
    elif可调用(f):
    py_f = pyfunc(f)
    print('no cyfunc')
    else:
    提高ValueError( f应该是可调用的或cyfunc)

    对于范围(N)中的i:
    current_value = py_f.f( i)如果current_value<
    x:
    x =当前值
    best_i = i
    return best_i

    def py_f(x):
    return(x-5)** 2

    cdef类cy_f(cyfunc):
    cpdef double f(self,double x):
    返回(x-5)** 2


要使用:

  python3 setup.py build_ext --inplace 
python3 -c'导入test_module; print(test_module.min_arg(test_module.cy_f(),10))’
python3 -c’import test_module; print(test_module.min_arg(test_module.py_f,10))’


Can a cdef Cython function be passed to another (python def) cython function from a Python script?

Minimal example:

test_module.pyx

cpdef min_arg(f, int N):
    cdef double x = 100000.
    cdef int best_i = -1

    for i in range(N):
        if f(i) < x:
            x = f(i)
            best_i = i
    return best_i

def py_f(x):
    return (x-5)**2

cdef public api double cy_f(double x):
    return (x-5)**2

test.py

import pyximport; pyximport.install()
import testmodule

testmodule.min_arg(testmodule.py_f, 100)

This works well, but I want to be able to also do

testmodule.min_arg(testmodule.cy_f, 100)

from a test.py, to have cython's speed (no Python overhead for each f(i) call). But obviously, Python doesn't know about cy_f, because it's not def or cpdef declared.

I was hoping something like this existed:

from scipy import LowLevelCallable
cy_f = LowLevelCallable.from_cython(testmodule, 'cy_f')
testmodule.min_arg(cy_f, 100)

But this gives TypeError: 'LowLevelCallable' object is not callable.

Thank you in advance.

解决方案

The LowLevelCallable is a class of functions that must be accepted by the underlying Python module. This work has been done for a few modules, including the quadrature routine scipy.integrate.quad

If you wish to use the same wrapping method, you must either go through the SciPy routines that make use of it, such as scipy.ndimage.generic_filter1d or scipy.integrate.quad. The code sits in compiled extensions, however.

The alternative, if your problem is reasonably well defined for the callback, is to implement this yourself. I have done this in one of my codes, so I post the link for simplicity:

  1. In a .pxd file, I define the interface cyfunc_d_d: https://github.com/pdebuyl/skl1/blob/master/skl1/core.pxd
  2. I can re-use this interface in the "base" cython module https://github.com/pdebuyl/skl1/blob/master/skl1/euler.pyx and also in a "user-defined" module.

The final code makes plain "cython-cython" calls while allowing the passing of objects at the Cython level

I adapted the code to your problem:

  1. test_interface.pxd

    cdef class cyfunc:                                                                                                                         
        cpdef double f(self, double x)                                                                                                         
    
    cdef class pyfunc(cyfunc):                                                                                                                 
        cdef object py_f                                                                                                                       
        cpdef double f(self, double x)                                                                                                         
    

  2. test_interface.pyx

    cdef class cyfunc:
        cpdef double f(self, double x):
            return 0
        def __cinit__(self):
            pass
    
    
    cdef class pyfunc(cyfunc):
        cpdef double f(self, double x):
            return self.py_f(x)
        def __init__(self, f):
            self.py_f = f
    

  3. setup.py

    from setuptools import setup, Extension                                                                                                    
    from Cython.Build import cythonize                                                                                                         
    
    setup(                                                                                                                                     
        ext_modules=cythonize((Extension('test_interface', ["test_interface.pyx"]),                                                            
                              Extension('test_module', ["test_module.pyx"]))                                                                   
                          )                                                                                                                    
    )                                                                                                                                          
    

  4. test_module.pyx

    from test_interface cimport cyfunc, pyfunc                                                                                                 
    
    cpdef min_arg(f, int N):                                                                                                                   
        cdef double x = 100000.                                                                                                                
        cdef int best_i = -1                                                                                                                   
        cdef int i                                                                                                                             
        cdef double current_value                                                                                                              
    
        cdef cyfunc py_f                                                                                                                       
    
        if isinstance(f, cyfunc):                                                                                                              
            py_f = f                                                                                                                           
            print('cyfunc')                                                                                                                    
        elif callable(f):                                                                                                                      
            py_f = pyfunc(f)                                                                                                                   
            print('no cyfunc')                                                                                                                 
        else:                                                                                                                                  
            raise ValueError("f should be a callable or a cyfunc")                                                                             
    
        for i in range(N):                                                                                                                     
            current_value = py_f.f(i)                                                                                                          
            if current_value < x:                                                                                                              
                x = current_value                                                                                                              
                best_i = i                                                                                                                     
        return best_i                                                                                                                          
    
    def py_f(x):                                                                                                                               
        return (x-5)**2                                                                                                                        
    
    cdef class cy_f(cyfunc):                                                                                                                   
        cpdef double f(self, double x):                                                                                                        
            return (x-5)**2                                                                                                                    
    

To use:

python3 setup.py build_ext --inplace
python3 -c 'import test_module ; print(test_module.min_arg(test_module.cy_f(), 10))'
python3 -c 'import test_module ; print(test_module.min_arg(test_module.py_f, 10))'

这篇关于通过python接口传递cython函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆