numpy 使用lambda ify和函数求值优化运行时

e1xvtsh3  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(120)

我目前正在优化我的代码的运行时,但它仍然不在我希望的时间消耗范围内。()在执行高斯求积时,对我的渐近矩阵表达式进行计算,并对所得的lambda函数**进行求值。代码的所有其他方面都得到了充分优化,因此,我希望有人能帮助我优化我的代码中的“瓶颈”,即对sympy表达式进行lambifying和求值。
代码是在64位Windows 7机器上使用Python 3.5.2(下面的示例说明了代码,在Jupyter QtConsole上执行)和以下模块版本编写的:

  • 症状:1.0
  • 麻木:1.11.1
  • 数字:0.27

Lambdify(第一页)

我认为lambdify()使用大量时间的原因是symmy表达式的复杂性(它涉及symy Piecewise()表达式的乘法)。这些表达式的简化是不可能的,因为它们是使用标准Alpert算法从勒让德尺度函数创建的小波函数。下面给出了这样一个矩阵的较小示例以及与lambdify一个“更简单”矩阵的时间比较:

from sympy import *
import numpy as np
import timeit

xi1 = symbols('xi1')
xi2 = symbols('xi2')
M = Matrix([[-0.0015625*(3.46410161513775*(0.00624999999999998*xi2 - 
           0.99375)*Piecewise((-1, 0.00624999999999998*xi2 - 0.99375 >= 0), 
           (1, 0.00624999999999998*xi2 - 0.99375 < 0)) + 
           1.73205080756888)*Piecewise((1, And(0.00624999999999998*xi2 - 
           0.99375 <= 1, 0.00624999999999998*xi2 - 
           0.99375 >= -1)), (0, True))], 
          [-0.00156249999999999*(0.0187499999999999*xi2 + 2.0*Piecewise((-1, 
           0.00624999999999998*xi2 - 0.99375 >= 0), (1, 
           0.00624999999999998*xi2 - 0.99375 < 0)) - 2.98125)*Piecewise((1, 
           And(0.00624999999999998*xi2 - 0.99375 <= 1, 
           0.00624999999999998*xi2 - 0.99375 >= -1)), (0, True))], 
          [-0.00270632938682636*xi1*(3.46410161513775*
           (0.00624999999999998*xi2 - 0.99375)*Piecewise((-1, 
           0.00624999999999998*xi2 - 0.99375 >= 0), (1, 
           0.00624999999999998*xi2 - 0.99375 < 0)) + 
           1.73205080756888)*Piecewise((1, And(0.00624999999999998*xi2 - 
           0.99375 <= 1, 0.00624999999999998*xi2 - 0.99375 >= -1)), (0, 
           True))]])
M_simpl = Matrix([(xi2**2),(xi2**2)*xi1,(xi2**2)*(xi1**2)])

时间比较得出:

import timeit

%timeit lambdify([xi1,xi2], M, 'numpy')
10 loops, best of 3: 23 ms per loop
%timeit lambdify([xi1,xi2], M_simpl, 'numpy')
100 loops, best of 3: 2.47 ms per loop

这表明,处理更复杂的表达式比处理更简单的Matrix慢近10倍,这在使用lambdify()被应用于这些类型的矩阵中的几个。研究我所了解到的更快的ufunction的主题()函数,它似乎在使用Fortran或C后端时工作得最好。然而,在我的情况下,这不是最好的选择,由于该函数尚未扩展到sympy Matrices,我希望代码足够通用。其他Windows用户修改代码时不需要安装C编译器等。那么,是否可以在不使用其他编译器的情况下,提高这些类型sympy表达式的lambdify()函数的速度?

Lambda函数求值

当在特定坐标下求值时,上述渐近矩阵的λ函数也会表现出不同的性能。下面的简单5点求积示例说明了这一点:

# Quadrature coordinates
xi_v = np.array([[-1,-1], [-0.5,-0.5], [0,0], [0.5,0.5], [1,1]])
# Quadrature weights
w = np.array([3, 2, 1, 2, 3])

# Quadrature
def quad_func(func, xi_v, w):
    G = np.zeros((3, 1))
    for i in range(0, len(w), 1):
        G += w[i]*func(*xi_v[i,:])
    return G

# Testing time usage
f = lambdify([xi1,xi2], M, 'numpy')
%timeit quad_func(f, xi_v, w)
1000 loops, best of 3: 852 µs per loop
f_simpl = lambdify([xi1,xi2], M_simpl, 'numpy')
%timeit quad_func(f_simpl, xi_v, w)
10000 loops, best of 3: 33.9 µs per loop

我的第一React是从numba模块中引入jit来加快计算速度,然而,这导致了一个弹出窗口,声明python已经停止工作,内核被重新启动(f和f_simple都会发生):

import numba

quad_func_jit = numba.jit(quad_func)
quad_func_jit(f, xi_v, w)

Kernel died, restarting

那么,有没有办法加快这些lambda函数的求值,以减少总的运行时间?或者有没有办法避免numba.jit崩溃?

qv7cva1a

qv7cva1a1#

我对lambdify生成的代码很感兴趣(lambdify将sympy语法转换为numpy代码),所以我使用inspect模块打印了它:

f = lambdify([xi1,xi2], M, 'numpy')

import inspect
lines = inspect.getsource(f)
print(lines)

(The f的代码可以从下面的问题中得到,为了简洁起见,我在这里不再重复)print语句给了我这个巨大的函数,它似乎是正确的:

def _lambdifygenerated(xi1, xi2):
    return array(
        [[(-0.0015625*(0.0216506350946109*xi2 - 3.44245098004314)
           *select([greater_equal(0.00624999999999998*xi2 - 0.99375, 0),True], 
                   [-1,1], default=nan) - 0.00270632938682638)
          *select([logical_and.reduce((greater_equal(0.00624999999999998*xi2 
                                                     - 0.99375, -1),
                                       less_equal(0.00624999999999998*xi2 
                                                  - 0.99375, 1))),True], [1,0], 
                  default=nan)], [
                      (-2.92968749999997e-5*xi2 - 0.00312499999999998
                       *select([
                           greater_equal(0.00624999999999998*xi2 - 0.99375, 0),True], [-1,1], 
                           default=nan) + 0.00465820312499997)
                      *select([logical_and.
                               reduce((greater_equal(0.00624999999999998*xi2 - 0.99375, -1),
                                                   less_equal(0.00624999999999998*xi2 - 0.99375, 1))),
                               True], [1,0], default=nan)],
                      [-0.00270632938682636*xi1*((0.0216506350946109*xi2     
                     - 3.44245098004314)*
                select([greater_equal(0.00624999999999998*
                xi2 - 0.99375, 0),True], [-1,1], default=nan) 
              + 1.73205080756888)*select([logical_and.reduce((greater_equal(
                0.00624999999999998*xi2 - 0.99375, -1),
                  less_equal(0.00624999999999998*xi2 - 0.99375, 1))),
                  True], [1,0], default=nan)]])

然而,这个函数使用了很多numba不支持的numpy函数,比如select,这使得numba无法使用,所以回答你的问题:不,(遗憾的是)不可能将lambdify和numba结合起来创建JIT编译的symmy答案

相关问题