【问题标题】:use eval() in numba / NumbaLSODA?在 numba / NumbaLSODA 中使用 eval()?
【发布时间】:2021-11-24 02:44:51
【问题描述】:

我正在尝试使用 NumbaLSODA 来优化我的模型,这实质上是在解决初始值 ODE 问题(可能非常僵硬)。我的原始模型基于 scipy/solve_ivp,它工作得非常好,只是有点慢,即使提供了雅可比矩阵。想不出任何其他方法来优化可调用导数函数,所以我开始寻找其他选项,例如 NumbaLSODA。

我实现了 NumbaLSODA,我的衍生函数看起来像这样:

@cfunc(lsoda_sig)
def deriv(t,y,ydot,coeff):
    ydot[0] = <very long math expression>
    ydot[1] = <very long math expression>
    ydot[2] = <very long math expression>
    ...

其中 coeff 是作为 arg 传递给 deriv() 的数组。数学表达式可能如下所示:

coeff[0]*y[0]*y[1] + coeff[1]*y[2] - coeff[2]*y[3] ...

再次,这很好用,模型比 scipy/solve_ivp 版本快得多,非常有前途!

这里有一个问题。导数函数中的 ydot 数组可能非常长且复杂。因此,在我的基于 scipy/solve_ivp 的模型中,我有另一个 sn-p 来生成字符串列表,编译,然后在导函数中进行 eval。它可能比直接提供 ydot 数组要慢一些,但极大地改善了用户体验。

但是,eval() 函数似乎不适用于基于 NumbaLSODA 的模型:

@cfunc(lsoda_sig)
def deriv(t,y,ydot,coeff):
    ydot[0] = eval(<very long math expression>)
    ydot[1] = eval(<very long math expression>)
    ydot[2] = eval(<very long math expression>)
    ...

我最终得到以下错误消息:

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Untyped global name 'eval': cannot determine Numba type of <class 'builtin_function_or_method'>

我对 numba 并不完全熟悉。我想知道是否有办法让 eval() 工作

我知道某些功能/操作在 numba 中不起作用。如果 eval() 确实是这种情况,有没有办法在导函数中输入代码生成的 ydot 数组?

非常感谢您!

【问题讨论】:

    标签: python eval numba ode


    【解决方案1】:

    不,eval 调用不可能在 Numa 函数中工作

    eval 与 Numba 的工作方式根本不兼容:它在运行时使用 LLVM 等工具编译代码,以便获得二进制代码然后执行(通常多次)。为此,Numba 要求所有变量和表达式(包括函数调用)都是强类型的。 Numba 可以使用类型推断来自动确定类型,但这不可能通过 eval 函数提前完成。在运行时这样做会非常低效,因为每次评估表达式的内容发生变化时都需要重新编译函数。实际上更糟糕的是,评估的表达式也必须由 Python 解释器解析(Numba 也需要一个强制性的相对缓慢的步骤)。如果求值表达式的内容是常量,那么对 eval 的需要是不合理的。使用eval 通常被视为一种不好的做法,并且在纯 Python 代码中通常已经效率低下。

    如果在您的情况下使用eval 是强制性的,但表达式通常/总是相同并且它独立于函数参数,那么有一个技巧。您可以为目标表达式生成和编译一整套 Numba 函数,并将它们存储在缓存中以供以后重用。简而言之:评估 Numba 函数本身的代码

    请注意,Numba 仅适用于编译函数非常密集(通常是大循环)时。否则,调用 Numba 函数通常比使用标准 Python 解释器执行工作要慢。

    【讨论】:

    • 谢谢! eval() 本身并不是虚假的。只是我还没有找到更好的方法。我写了一个解析器(因为所有的数学表达式都是类似的格式),它比 eval 快,但比 compile then eval 慢。这就是为什么我一直在“滥用”评估。您能否详细说明 eval numba 函数?我试过 exec( '@cfunc(lsoda_sig) \ndef deriv(t,y,ydot,coeff) ...' ) 确实有效,但感觉很草率。
    • 是的,我正在考虑这样的exec 电话。如果表达式对于所有 deriv 调用都是唯一的,那么您可以生成包装在 numba 函数中的表达式(导致大字符串)和 exec 它提前生成编译的 numba 函数。然后可以在deriv 中调用 numba 函数,就像它们是经典函数一样。这有助于使deriv 代码更清晰。如果有多个可能的表达式,那么您可以将它们缓存在数据结构(例如数组或字典)中,但我不确定 Numba 会喜欢它。
    猜你喜欢
    • 2019-01-16
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多