用于编写与`scipy.integrate.odeint'交互的Python类的设计启发法? [英] Design heuristics for writing Python classes that interact with `scipy.integrate.odeint`?
问题描述
简介
scipy.integrate.odeint
要求作为其第一个参数,该函数计算我们要变量的导数集成(从现在开始,我将其称为 d_func
,用于微分函数)。
d_func
必须由用户使用Python代码编写。使用Numba提升性能的一种好方法是 @jit
d_func
(因为 d_func
在集成过程中被称为很多次。
我对如何在 d_func
非常复杂,它后面需要一个Python类对象。
代码设置
这里是我代码的卡通:
- 在此模块内部有一个名为
DynamicBox.py
- 的模块一个Python类,
DynamicBox
-
DynamicBox
有很多属性 - 其中一些属性是相位变量,即它们是我要积分的数量
- 其中一些属性是参数,也就是说,我用它们来
计算相位变量的导数
我将有一堆函数,这些函数将使用 DynamixBox
相位变量或参数属性,以便计算导数中的相关项。那就是:
- 我将有一个
d_func
-
d_func
本身将使用DynamixBox $ c调用许多小的辅助函数来
计算导数中的相关项$ c>
属性
设计选择
我有选择以下选项:
- 我可以使
d_func
以及
DynamicBox
的所有辅助函数方法; - 或者我只能使用
DynamicBox
,$ b的方法制作d_func
$ b及其所有辅助功能与
DynamicBox
在同一模块中,但不是DynamicBox
; - 或仅辅助函数是
DynamicBox
的方法,而
d_func
只是在同一模块(DynamicBox.py
)中,而不是DynamicBox $ c的
方法$ c>; - 或辅助功能或d_func都不是
DynamicBox的方法。
问题
我对Python不够了解,无法确定哪种选择是最好的。我认为需要回答以下问题。
-
进行实例属性调用以获取属性是否昂贵?仅当您使用的不是类的方法
时,它才昂贵吗? -
如果Numba怎么办?在玩吗?例如,如果我使用
@jit
-ting普通函数而不是类方法,Numba会更喜欢吗?
我可以对此问题的Numba部分发表评论。
<正如其他用户所提到的那样,在Numba中进行属性访问会带来一些开销。例如,您可能很想编写这样的代码:
class Foo(object):
def __init __( self,x):
self.x = x
@ numba.jit
def dosomething(self,y):
for i in range(len(self .x)):
self.x [i] + = y [i]
这会很慢,因为Numba每次遇到 self.x
都要调用Python层进行属性访问。
做同一件事的更好方法是:
class Foo(object):
def __init __(self,x):
self.x = x
def dosomething(self,y):
_dosomething(self.x,y)
@ numba.jit(nopython = True)
def _dosomething(x,y):
for i in range(len(x)):
x [i] + = y [i ]
此处循环内没有属性访问权限,此外,我们还可以添加 nopython = True
参数,w如果函数必须依靠任何(慢速)Python代码,则hich将导致Numba引发错误。此 nopython
参数是确保Numba函数尽可能高效的好方法。
Introduction
scipy.integrate.odeint
requires as its first argument, a function that computes the derivatives of the variables we want to integrate over (which I'll refer to as d_func
, for "derivative function" from now on).
d_func
has to be written by the user, in Python code. A great way to get a boost of performance using Numba is to @jit
the d_func
(because d_func
is called many times during integration).
I have questions about how to write performant code when d_func
is complicated enough that it needs a Python class object behind it.
Code setup
Here is a "cartoon" of my code:
- there is a module called
DynamicBox.py
- inside this module is a Python class,
DynamicBox
DynamicBox
has a bunch of attributes- some of these attributes are "phase variables" -- that is, they are the quantities I am interested in integrating
- some of these attributes are "parameters" -- that is, I use them to calculate the derivatives of the phase variables
I will have a bunch of functions that will take DynamixBox
phase variable or parameter attributes, in order to calculate relevant terms in the derivatives. That is:
- I will have a
d_func
d_func
itself will call lots of little helper functions to calculate relevant terms in the derivative, usingDynamixBox
attributes
Design choices
I have to make a choice, with the following options:
- either I can make
d_func
and all its helper functions methods ofDynamicBox
; - or I can make only
d_func
a method ofDynamicBox
, and all of its helper functions are in the same module asDynamicBox
, but not methods ofDynamicBox
; - or only the helper functions are methods of
DynamicBox
, butd_func
is just in the same module (DynamicBox.py
), and not a method ofDynamicBox
; - or neither the helper functions, nor d_func, are methods of DynamicBox.
Question
I do not know enough about Python to figure out which choice is best. The following questions I think would need answering.
Is it expensive to make instance attribute calls to get attributes or is it expensive only if you are in a function that is not a method of the class?
What if Numba is in play? For instance, will Numba like it better if I am
@jit
-ting normal functions instead of class methods?
I can comment on the Numba portion of this question.
As other users have mentioned, attribute access in Numba leads to some overhead. For example, you might be tempted to write code like this:
class Foo(object):
def __init__(self, x):
self.x = x
@numba.jit
def dosomething(self, y):
for i in range(len(self.x)):
self.x[i] += y[i]
This will be slow, because Numba has to call into the Python layer for attribute access each time it encounters self.x
.
A better way to do the same thing would be this:
class Foo(object):
def __init__(self, x):
self.x = x
def dosomething(self, y):
_dosomething(self.x, y)
@numba.jit(nopython=True)
def _dosomething(x, y):
for i in range(len(x)):
x[i] += y[i]
Here there is no attribute access within the loop, and additionally we're able to add the nopython=True
argument, which will cause Numba to raise an error if the function has to fall back on any (slow) Python code. This nopython
argument is a great way to make sure that your Numba functions are as efficient as possible.
这篇关于用于编写与`scipy.integrate.odeint'交互的Python类的设计启发法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!