Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python是一个带有if语句的jit函数_Python_Numpy_Jit_Numba_Boolean Indexing - Fatal编程技术网

Python是一个带有if语句的jit函数

Python是一个带有if语句的jit函数,python,numpy,jit,numba,boolean-indexing,Python,Numpy,Jit,Numba,Boolean Indexing,我有一个包含3个部分的分段函数,我正试图用Python使用Numba@jit指令编写它。函数是通过数组计算的。该功能的定义如下: @njit(parallel=True) def f(x_vec): N=len(x_vec) y_vec=np.zeros(N) for i in prange(N): x=x_vec[i] if x<=2000: y=64/x elif x>=4000:

我有一个包含3个部分的分段函数,我正试图用Python使用Numba@jit指令编写它。函数是通过数组计算的。该功能的定义如下:

@njit(parallel=True)
def f(x_vec):
    N=len(x_vec)
    y_vec=np.zeros(N)
    for i in prange(N):
        x=x_vec[i]
        if x<=2000:
            y=64/x
        elif x>=4000:
            y=np.log(x)
        else:
            y=np.log(1.2*x)
        y_vec[i]=y
    return y_vec
对于完成论,称为以下库:

import numpy as np
from numba import njit, prange
因此,在这种情况下,功能将是:

def f1(x):
    return 64/x
def f2(x):
    return np.log(x)
def f3(x):
    return np.log(1.2*x)
实际函数为以下函数,用于层流、过渡和湍流状态下的光滑管道摩擦系数:

@njit
def f1(x):
    return 64/x

@njit
def f2(x):
    #x is the Reynolds number(Re), y is the Darcy friction(f)
    #for transition, we can assume Re=4000 (max possible friction)
    y=0.02
    y=(-2/np.log(10))*np.log(2.51/(4000*np.sqrt(y)))
    return 1/(y*y)

@njit
def f3(x): #colebrook-white approximation
    #x is the Reynolds number(Re), y is the Darcy friction(f)
    y=0.02
    y=(-2/np.log(10))*np.log(2.51/(x*np.sqrt(y)))
    return 1/(y*y)
谢谢大家的贡献。这是numpy解决方案(由于某些原因,最后的树线速度较慢,但不需要预热):

y=np.empty\u like(x\u vec)
a1=np.其中(x_vec=4000,真,假)
a2=~(a1 | a3)
y[a1]=f1(x_向量[a1])
y[a2]=f2(x_向量[a2])
y[a3]=f3(x_vec[a3])
最快的Numba解决方案,允许传递函数名并利用prange(但受到jit预热的阻碍),可以与第一个解决方案一样快(问题之首):

@njit(parallel=True)
定义f(x_向量,f1,f2,f3):
N=len(x_vec)
y_vec=np.零(N)
对于prange中的i(N):
x=x_向量[i]
如果x=4000:
y=f3(x)
其他:
y=f2(x)
y_vec[i]=y
返回y_vec

这太慢了吗?这可以在纯numpy中完成,方法是避免循环并使用掩码进行索引:

def f(x):
    y = np.empty_like(x)
    
    mask = x <= 2000
    y[mask] = 64 / x[mask]
    
    mask = (x > 2000) & (x < 4000)
    y[mask] = np.log(1.2 * x[mask])
    
    mask = x >= 4000
    y[mask] = np.log(x[mask])

    return y
我得到(带6+6VT内核的i7-8850H笔记本电脑)

如果您预期的子功能主要是numpy操作,这仍然很快。

您可以编写
f()
来接受功能参数,例如:

@njit
def f(arr, f1, f2, f3):
    N = len(arr)
    y_vec = np.zeros(N)
    for i in range(N):
        x = x_vec[i]
        if x <= 2000:
            y = f1(x)
        elif x >= 4000:
            y = f2(x)
        else:
            y = f3(x)
        y_vec[i] = y
    return y_vec
@njit
def f(arr、f1、f2、f3):
N=长度(arr)
y_vec=np.零(N)
对于范围(N)中的i:
x=x_向量[i]
如果x=4000:
y=f2(x)
其他:
y=f3(x)
y_vec[i]=y
返回y_vec

确保您传递的函数与Numba兼容。

如果您的子函数也被(并且可以)写入,它们仍然会很快。您可能还想使用
numba.prange
而不是
range
。为了能够回答这个问题,我们可能需要查看您想要的子功能
f1
f2
f3
。@JanChristopherasa the prange已经让它变得更快了。f1、f2和f3与问题中定义的相同。我在问题中添加了子功能。(我可以发布实际的函数,但它们可能很长,但它们基本上是由一堆乘法和日志调用组成的)。如果使用
math.log(),可能会快一点在标量参数的NUBBA中,也考虑选项内联=“总是”来始终内嵌这些小函数和ErrRoMyMease=“NoMPY”,通过零校验来禁用除法,这也有一些开销。另外,在覆盖每个条目之前用零初始化
y_vec
,没有多大意义。用
np.empty
分配内存就足够了。谢谢Jan,这非常有帮助,事实上,你的回答回答了我很久以来一直在想的一个问题,即使用mask,我可以将函数实际应用到数组的一部分,并完全避免if条件(这是一种奇特的索引,对吧?)。这仍然比jit实现慢,但我可能会发现它的用处,特别是如果我用f1、f2和f3与jit一起尝试的话。可以使用ufuncs的
where
参数,而不是布尔索引,以提高速度。另外,为其中两个条件定义mask1和mask2。我很确定
~(mask1 | mask2)
是获取else掩码的一种更快的方法。也可以用单个buffer@dani. 布尔索引。奇特的索引是整数索引。如果使用最快的计算(即
64/x
)作为基本情况,您可能会获得更快的
f_else
。@Mad物理学家感谢
where
提示,非常有用@也许是诺洛克。我并没有特别优化速度,我只是想证明,对于可以用纯numpy表述的问题,通常不需要NUBA。如果JIT的“预热”时间支配着您的运行时间,则尤其如此。此外,您可能还希望参数化阈值。谢谢,它也可以在不传递函数名的情况下工作。但是我想传递名称会给我以后更多的灵活性,因为我将能够传递不同的函数给它。
@njit(parallel=True)
def f(x_vec,f1,f2,f3):
    N = len(x_vec)
    y_vec = np.zeros(N)
    for i in prange(N):
        x=x_vec[i]
        if x<=2000:
            y=f1(x)
        elif x>=4000:
            y=f3(x)
        else:
            y=f2(x)
        y_vec[i]=y
    return y_vec
def f(x):
    y = np.empty_like(x)
    
    mask = x <= 2000
    y[mask] = 64 / x[mask]
    
    mask = (x > 2000) & (x < 4000)
    y[mask] = np.log(1.2 * x[mask])
    
    mask = x >= 4000
    y[mask] = np.log(x[mask])

    return y
def f_else(x):
    y = np.log(1.2 * x)
    
    mask = x <= 2000
    y[mask] = 64 / x[mask]
    
    mask = x >= 4000
    y[mask] = np.log(x[mask])

    return y
Np=10000000
x_vec=100*np.power(1e8/100,np.random.rand(Np))
f1: 1 loop, best of 5: 294 ms per loop
f_else: 1 loop, best of 5: 400 ms per loop
@njit
def f(arr, f1, f2, f3):
    N = len(arr)
    y_vec = np.zeros(N)
    for i in range(N):
        x = x_vec[i]
        if x <= 2000:
            y = f1(x)
        elif x >= 4000:
            y = f2(x)
        else:
            y = f3(x)
        y_vec[i] = y
    return y_vec