从文件中读取和计算公式列表时,加快Python计算速度

从文件中读取和计算公式列表时,加快Python计算速度,python,performance,numpy,Python,Performance,Numpy,我编写了一个简单的Python脚本,它从一个文本文件的不同行中读取大量代数表达式,计算每行的数学值,并将其放入一个numpy数组中。然后求出该矩阵的特征值。然后,参数A、B、C将被更改,程序将再次运行,因此使用一个函数来实现这一点 其中一些文本文件将包含数百万行公式,因此在分析代码之后,我发现eval命令大约占执行时间的99%。我意识到使用eval的危险,但此代码只会由我自己使用。除了调用eval之外,代码的所有其他部分都很快 下面是代码,其中mat_size设置为500,表示500*500数组

我编写了一个简单的Python脚本,它从一个文本文件的不同行中读取大量代数表达式,计算每行的数学值,并将其放入一个numpy数组中。然后求出该矩阵的特征值。然后,参数A、B、C将被更改,程序将再次运行,因此使用一个函数来实现这一点

其中一些文本文件将包含数百万行公式,因此在分析代码之后,我发现
eval
命令大约占执行时间的99%。我意识到使用
eval
的危险,但此代码只会由我自己使用。除了调用
eval
之外,代码的所有其他部分都很快

下面是代码,其中
mat_size
设置为500,表示500*500数组,表示从文件中读取250000行方程。我无法提供该文件,因为它的大小约为0.5GB,但下面提供了一个示例,说明了它的外观,并且它只使用基本的数学运算

import numpy as np
from numpy import *
from scipy.linalg import eigvalsh

mat_size = 500

# Read the file line by line
with open("test_file.txt", 'r') as f:
    lines = f.readlines() 

# Function to evaluate the maths and build the numpy array
def my_func(A,B,C):
    lst = []
    for i in lines:   
        # Strip the \n
        new = eval(i.rstrip())
        lst.append(new)
    # Build the numpy array
    AA = np.array(lst,dtype=np.float64)
    # Resize it to mat_size
    matt = np.resize(AA,(mat_size,mat_size))
    return matt 

# Function to find eigenvalues of matrix
def optimise(x):
    A,B,C = x
    test = my_func(A,B,C)
    ev=-1*eigvalsh(test)
    return ev[-(1)]   

# Define what A,B,C are, this can be changed each time the program is run
x0 = [7.65,5.38,4.00]

# Print result
print(optimise(x0))
示例输入文本文件的几行:(
mat_size
可以更改为2以运行此文件)

我知道
eval
通常是不好的练习,而且速度很慢,所以我寻找了其他方法来实现加速。我尝试了概述的方法,但这些方法似乎都不起作用。我也试着用sympy来解决这个问题,但这导致了大规模的减速。解决这个问题的更好方法是什么

编辑

从使用
numexpr
的建议中,我遇到了一个问题,与标准的
eval
相比,它已经停止运行。在某些情况下,矩阵元素包含相当多的代数表达式。这里是一个仅一个矩阵元素的示例,即文件中的一个方程式(它包含了上面代码中未定义的几个术语,但可以在代码顶部轻松定义):


当矩阵元素为这种形式时,
numexpr
会完全阻塞,而
eval
会立即对其求值。对于10*10矩阵(文件中有100个方程)
numexpr
处理文件大约需要78秒,而
eval
需要0.01秒。对使用
numexpr
的代码进行分析后发现,numexpr的
getExprnames
precompile
函数是导致问题的原因,
precompile
占用了73.5秒的总时间,
getExprnames
占用了3.5秒的时间。为什么预编译会在这个特定的计算和getExprname中造成这样的瓶颈?这个模块不是很适合长代数表达式吗?

我找到了一种方法,可以利用这个库在这个特定的实例中加快eval()的速度。我像往常一样读入文件,但随后将列表分成大小相等的子列表,这些子列表可以在不同的CPU上单独处理,并在最后重新组合评估的子列表。与原始方法相比,这提供了很好的加速效果。我相信下面的代码可以简化/优化;但现在它可以工作了(例如,如果列表元素的数量是素数怎么办?这意味着列表不相等)。一些粗略的基准测试表明,使用我笔记本电脑的4个CPU,速度大约快3倍。代码如下:

-71*A**3/(A+B)**7-61*B**3/(A+B)**7-3/2/B**2/C**2*A**6/(A+B)**7-7/4/B**3/m3*A**6/(A+B)**7-49/4/B**2/C*A**6/(A+B)**7+363/C*A**3/(A+B)**7*z3+451*B**3/C/(A+B)**7*z3-3/2*B**5/C/A**2/(A+B)**7-3/4*B**7/C/A**3/(A+B)**7-1/B/C**3*A**6/(A+B)**7-3/2/B**2/C*A**5/(A+B)**7-107/2/C/m3*A**4/(A+B)**7-21/2/B/C*A**4/(A+B)**7-25/2*B/C*A**2/(A+B)**7-153/2*B**2/C*A/(A+B)**7-5/2*B**4/C/m3/(A+B)**7-B**6/C**3/A/(A+B)**7-21/2*B**4/C/A/(A+B)**7-7/4/B**3/C*A**7/(A+B)**7+86/C**2*A**4/(A+B)**7*z3+90*B**4/C**2/(A+B)**7*z3-1/4*B**6/m3/A**3/(A+B)**7-149/4/B/C*A**5/(A+B)**7-65*B**2/C**3*A**4/(A+B)**7-241/2*B/C**2*A**4/(A+B)**7-38*B**3/C**3*A**3/(A+B)**7+19*B**2/C**2*A**3/(A+B)**7-181*B/C*A**3/(A+B)**7-47*B**4/C**3*A**2/(A+B)**7+19*B**3/C**2*A**2/(A+B)**7+362*B**2/C*A**2/(A+B)**7-43*B**5/C**3*A/(A+B)**7-241/2*B**4/C**2*A/(A+B)**7-272*B**3/C*A/(A+B)**7-25/4*B**6/C**2/A/(A+B)**7-77/4*B**5/C/A/(A+B)**7-3/4*B**7/C**2/A**2/(A+B)**7-23/4*B**6/C/A**2/(A+B)**7-11/B/C**2*A**5/(A+B)**7-13/B**2/m3*A**5/(A+B)**7-25*B/C**3*A**4/(A+B)**7-169/4/B/m3*A**4/(A+B)**7-27*B**2/C**3*A**3/(A+B)**7-47*B/C**2*A**3/(A+B)**7-27*B**3/C**3*A**2/(A+B)**7-38*B**2/C**2*A**2/(A+B)**7-131/4*B/m3*A**2/(A+B)**7-25*B**4/C**3*A/(A+B)**7-65*B**3/C**2*A/(A+B)**7-303/4*B**2/m3*A/(A+B)**7-5*B**5/C**2/A/(A+B)**7-49/4*B**4/m3/A/(A+B)**7-1/2*B**6/C**2/A**2/(A+B)**7-5/2*B**5/m3/A**2/(A+B)**7-1/2/B/C**3*A**7/(A+B)**7-3/4/B**2/C**2*A**7/(A+B)**7-25/4/B/C**2*A**6/(A+B)**7-45*B/C**3*A**5/(A+B)**7-3/2*B**7/C**3/A/(A+B)**7-123/2/C*A**4/(A+B)**7-37/B*A**4/(A+B)**7-53/2*B*A**2/(A+B)**7-75/2*B**2*A/(A+B)**7-11*B**6/C**3/(A+B)**7-39/2*B**5/C**2/(A+B)**7-53/2*B**4/C/(A+B)**7-7*B**4/A/(A+B)**7-7/4*B**5/A**2/(A+B)**7-1/4*B**6/A**3/(A+B)**7-11/C**3*A**5/(A+B)**7-43/C**2*A**4/(A+B)**7-363/4/m3*A**3/(A+B)**7-11*B**5/C**3/(A+B)**7-45*B**4/C**2/(A+B)**7-451/4*B**3/m3/(A+B)**7-5/C**3*A**6/(A+B)**7-39/2/C**2*A**5/(A+B)**7-49/4/B**2*A**5/(A+B)**7-7/4/B**3*A**6/(A+B)**7-79/2/C*A**3/(A+B)**7-207/2*B**3/C/(A+B)**7+22/B/C**2*A**5/(A+B)**7*z3+94*B/C**2*A**3/(A+B)**7*z3+76*B**2/C**2*A**2/(A+B)**7*z3+130*B**3/C**2*A/(A+B)**7*z3+10*B**5/C**2/A/(A+B)**7*z3+B**6/C**2/A**2/(A+B)**7*z3+3/B**2/C**2*A**6/(A+B)**7*z3+7/B**3/C*A**6/(A+B)**7*z3+52/B**2/C*A**5/(A+B)**7*z3+169/B/C*A**4/(A+B)**7*z3+131*B/C*A**2/(A+B)**7*z3+303*B**2/C*A/(A+B)**7*z3+49*B**4/C/A/(A+B)**7*z3+10*B**5/C/A**2/(A+B)**7*z3+B**6/C/A**3/(A+B)**7*z3-3/4*B**7/C/m3/A**3/(A+B)**7-7/4/B**3/C/m3*A**7/(A+B)**7-49/4/B**2/C/m3*A**6/(A+B)**7-149/4/B/C/m3*A**5/(A+B)**7-293*B/C/m3*A**3/(A+B)**7+778*B**2/C/m3*A**2/(A+B)**7-480*B**3/C/m3*A/(A+B)**7-77/4*B**5/C/m3/A/(A+B)**7-23/4*B**6/C/m3/A**2/(A+B)**7
from multiprocessing import Process, Queue

with open("test.txt", 'r') as h:
    linesHH = h.readlines()


# Get the number of list elements
size = len(linesHH)

# Break apart the list into the desired number of chunks
chunk_size = size/4
chunks = [linesHH[x:x+chunk_size] for x in xrange(0, len(linesHH), chunk_size)]

# Declare variables
A = 0.1
B = 2
C = 2.1
m3 = 1
z3 = 2

# Declare all the functions that process the substrings
def my_funcHH1(A,B,C,que):   #add a argument to function for assigning a queue to each chunk function
    lstHH1 = []
    for i in chunks[0]:
        HH1 = eval(i)
        lstHH1.append(HH1)
    que.put(lstHH1)

def my_funcHH2(A,B,C,que):    
    lstHH2 = []
    for i in chunks[1]:
        HH2 = eval(i)
        lstHH2.append(HH2)
    que.put(lstHH2)

def my_funcHH3(A,B,C,que):    
    lstHH3 = []
    for i in chunks[2]:
        HH3 = eval(i)
        lstHH3.append(HH3)
    que.put(lstHH3)

def my_funcHH4(A,B,C,que):    
    lstHH4 = []
    for i in chunks[3]:
        HH4 = eval(i)
        lstHH4.append(HH4)
    que.put(lstHH4)

queue1 = Queue()    
queue2 = Queue()  
queue3 = Queue()  
queue4 = Queue()  

# Declare the processes
p1 = Process(target= my_funcHH1, args= (A,B,C,queue1))    
p2 = Process(target= my_funcHH2, args= (A,B,C,queue2))
p3 = Process(target= my_funcHH3, args= (A,B,C,queue3))
p4 = Process(target= my_funcHH4, args= (A,B,C,queue4))

# Start them
p1.start()
p2.start()
p3.start()
p4.start()

HH1 = queue1.get()
HH2 = queue2.get()
HH3 = queue3.get()
HH4 = queue4.get()
p1.join()
p2.join()
p3.join()
p4.join()

# Obtain the final result by combining lists together again.
mergedlist = HH1 + HH2 + HH3 + HH4

我会使用
numexpr.evaluate()
-这是安全的、非常快速的,并且支持numpy数组…@MaxU请参阅编辑以了解numexpr与eval的有趣行为。
from multiprocessing import Process, Queue

with open("test.txt", 'r') as h:
    linesHH = h.readlines()


# Get the number of list elements
size = len(linesHH)

# Break apart the list into the desired number of chunks
chunk_size = size/4
chunks = [linesHH[x:x+chunk_size] for x in xrange(0, len(linesHH), chunk_size)]

# Declare variables
A = 0.1
B = 2
C = 2.1
m3 = 1
z3 = 2

# Declare all the functions that process the substrings
def my_funcHH1(A,B,C,que):   #add a argument to function for assigning a queue to each chunk function
    lstHH1 = []
    for i in chunks[0]:
        HH1 = eval(i)
        lstHH1.append(HH1)
    que.put(lstHH1)

def my_funcHH2(A,B,C,que):    
    lstHH2 = []
    for i in chunks[1]:
        HH2 = eval(i)
        lstHH2.append(HH2)
    que.put(lstHH2)

def my_funcHH3(A,B,C,que):    
    lstHH3 = []
    for i in chunks[2]:
        HH3 = eval(i)
        lstHH3.append(HH3)
    que.put(lstHH3)

def my_funcHH4(A,B,C,que):    
    lstHH4 = []
    for i in chunks[3]:
        HH4 = eval(i)
        lstHH4.append(HH4)
    que.put(lstHH4)

queue1 = Queue()    
queue2 = Queue()  
queue3 = Queue()  
queue4 = Queue()  

# Declare the processes
p1 = Process(target= my_funcHH1, args= (A,B,C,queue1))    
p2 = Process(target= my_funcHH2, args= (A,B,C,queue2))
p3 = Process(target= my_funcHH3, args= (A,B,C,queue3))
p4 = Process(target= my_funcHH4, args= (A,B,C,queue4))

# Start them
p1.start()
p2.start()
p3.start()
p4.start()

HH1 = queue1.get()
HH2 = queue2.get()
HH3 = queue3.get()
HH4 = queue4.get()
p1.join()
p2.join()
p3.join()
p4.join()

# Obtain the final result by combining lists together again.
mergedlist = HH1 + HH2 + HH3 + HH4