Lambdify与Python一起使用,但在Cython

时间:2016-03-24 00:20:47

标签: python optimization cython sympy lambdify

我的网站运行这个Python脚本,如果使用Cython,将会更加优化。最近我需要添加Sympy with Lambdify,这对Cython来说并不顺利。

所以我把问题剥离到最小的工作示例。在代码中,我有一个包含字符串键的字典,其值为列表。我想将这些键用作变量。在下面的简化示例中,只有1个变量,但通常我需要更多。请检查以下示例:

import numpy as np
from sympy.parsing.sympy_parser import parse_expr
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import S, Symbol
from sympy.utilities.autowrap import ufuncify


def CreateMagneticFieldsList(dataToSave,equationString,DSList):

    expression  = S(equationString)
    numOfElements = len(dataToSave["MagneticFields"])

    #initialize the magnetic field output array
    magFieldsArray    = np.empty(numOfElements)
    magFieldsArray[:] = np.NaN

    lam_f = lambdify(tuple(DSList),expression,modules='numpy')
    try:
        # pass
        for i in range(numOfElements):
            replacementList = np.zeros(len(DSList))


            for j in range(len(DSList)):
                replacementList[j] = dataToSave[DSList[j]][i]

            try:
                val = np.double(lam_f(*replacementList))

            except:
                val = np.nan
            magFieldsArray[i] = val
    except:
        print("Error while evaluating the magnetic field expression")
    return magFieldsArray


list={"MagneticFields":[1,2,3,4,5]}

out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])

print(out)

我们称之为test.py。这非常有效。现在我想对此进行cythonize,所以我使用以下脚本:

#!/bin/bash

cython --embed -o test.c test.py
gcc -pthread -fPIC -fwrapv -Ofast -Wall -L/lib/x86_64-linux-gnu/ -lpython3.4m -I/usr/include/python3.4 -o test.exe test.c

现在,如果我执行./test.exe,它会抛出异常!这是例外:

Traceback (most recent call last):
  File "test.py", line 42, in init test (test.c:1811)
    out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])
  File "test.py", line 19, in test.CreateMagneticFieldsList (test.c:1036)
    lam_f = lambdify(tuple(DSList),expression,modules='numpy')
  File "/usr/local/lib/python3.4/dist-packages/sympy/utilities/lambdify.py", line 363, in lambdify
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
AttributeError: 'NoneType' object has no attribute 'f_locals'

所以问题是:如何让lambdify与Cython一起使用?

注意:我想指出我有Debian Jessie,这就是我使用Python 3.4的原因。另外我想指出,在不使用lambdify时,我对Cython没有任何问题。另外我想指出Cython已经使用pip3 install cython --upgrade更新到最后一个版本。

2 个答案:

答案 0 :(得分:4)

这是一个解决实际问题的方法(在评论和@jjhakala的答案中确定)Cython不会为编译函数生成完整的回溯/内省信息。我从你的评论中收集到你希望保留大部分程序用Cython编译的速度原因。

“解决方案”是将Python解释器仅用于需要调用lambdify的单个函数,而将其余函数留在Cython中。您可以使用exec

执行此操作

这个想法的一个非常简单的例子是

exec("""
def f(func_to_call):
    return func_to_call()""")

# a Cython compiled version    
def f2(func_to_call):
    return func_to_call())

这可以编译为Cython模块并导入,在导入时,Python解释器运行字符串中的代码,并正确地将f添加到模块全局变量。如果我们创建一个纯Python函数

def g():
    return inspect.currentframe().f_back.f_locals

调用cython_module.f(g)为我提供了一个包含键func_to_call的字典(正如预期的那样),而cython_module.f2(g)为我提供了__main__模块全局字符(但这是因为我正在运行翻译,而不是使用--embed)。

修改完整示例,基于您的代码

from sympy import S, lambdify # I'm assuming "S" comes from sympy
import numpy as np

CreateMagneticFieldsList = None # stops a compile error about CreateMagneticFieldsList being undefined

exec("""def CreateMagneticFieldsList(dataToSave,equationString,DSList):

    expression  = S(equationString)
    numOfElements = len(dataToSave["MagneticFields"])

    #initialize the magnetic field output array
    magFieldsArray    = np.empty(numOfElements)
    magFieldsArray[:] = np.NaN

    lam_f = lambdify(tuple(DSList),expression,modules='numpy')
    try:
        # pass
        for i in range(numOfElements):
            replacementList = np.zeros(len(DSList))


            for j in range(len(DSList)):
                replacementList[j] = dataToSave[DSList[j]][i]

            try:
                val = np.double(lam_f(*replacementList))

            except:
                val = np.nan
            magFieldsArray[i] = val
    except:
        print("Error while evaluating the magnetic field expression")
    return magFieldsArray""")


list={"MagneticFields":[1,2,3,4,5]}

out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])
print(out)

使用脚本编译时会打印

  

[5.1 10.2 15.3 20.4 25.5]

基本上我所做的全部是将函数包装在exec语句中,因此它由Python解释器执行。这部分不会看到Cython的任何好处,但是你的程序的其余部分仍然会。如果你想最大化用Cython编译的数量,你可以把它分成多个函数,这样只有包含lambdify的小部分在exec

答案 1 :(得分:2)

cython docs - limitations中陈述

  

堆叠帧

     

目前我们生成虚假的追踪作为例外的一部分   传播,但不填写当地人,不能填写co_code。成为   完全兼容,我们将不得不生成这些堆栈框架对象   在函数调用时(具有潜在的性能损失)。我们可能   有一个选项可以启用它来进行调试。

中的

f_locals

AttributeError: 'NoneType' object has no attribute 'f_locals'

似乎指向了这个不可解决的问题。