使用SymPy进行长符号表达式代换的性能优化问题
问题
数学上需要计算函数$f_m(k)$,其中$k$取0、1…m-1的有限值。固定$m$时可生成对应每个$k$的符号表达式,但$m$较大时表达式会变得复杂,最终目标是完成数值计算。
原本采用「先生成符号表达式,再代入数值计算」的思路:用SymPy推导包含500+单项式的多项式表达式,但调用expr.subs([(x,x_val),(y,y_val)])进行代换时耗时极久——当m=100时,代换至少需要数分钟(甚至仍在计算中)。
求加快代换的方法,或替代方案。
附生成$f_m$的示例代码:
# Define parameters of interest - working with Sympy import sympy as sym from sympy.abc import V import scipy.special m = 100 V = V phi = sym.symbols('Phi') diag = sym.zeros(1,m) diag[0] = 1 for j in range(2,m+1): # j = 1, i.e. m = 2 f_j = sym.zeros(1,j) # Computing f as an array, so that f_j[k] is... #...desired symbolic expression of f_j at k for k in range(j-1): # k = 0,1 f_j[k] = scipy.special.binom(j-1,k)*(phi*V**(k))**(j-k)*diag[k] - phi*V g = sum(f_j) f_j[j-1] = 1/2 - g diag[j-1] = 1/2 - g # Output of code is f_m. Doing example substituition... f_j.subs([(V,1),(phi,2)])
解决方案
方案1:用lambdify将符号表达式转为数值计算函数
SymPy的subs本身不适合处理超大规模符号表达式的批量代换,而lambdify可以把符号表达式编译成基于NumPy的数值函数,大幅降低计算开销。
修改示例:
# 在生成f_j之后执行 # 将f_j转为接收V和phi参数的数值函数 f_numeric = sym.lambdify((V, phi), f_j, 'numpy') # 直接传入数值计算 result = f_numeric(1, 2)
方案2:全程数值递推,跳过符号表达式生成
既然最终目标是数值计算,完全可以绕开符号推导步骤,直接用数值方法递推计算,这是提速效果最显著的方案。
重写后的数值版代码:
import numpy as np from scipy.special import binom m = 100 V_val = 1 phi_val = 2 # 初始化数值数组存储中间结果 diag = np.zeros(m) diag[0] = 1 for j in range(2, m+1): f_j = np.zeros(j) for k in range(j-1): # 直接计算数值,无符号操作 term = binom(j-1, k) * (phi_val * (V_val ** k)) ** (j - k) * diag[k] f_j[k] = term - phi_val * V_val g = np.sum(f_j[:j-1]) f_j[j-1] = 0.5 - g diag[j-1] = 0.5 - g # f_j即为m=100时的数值结果 print(f_j)
该方案毫秒级即可完成m=100的计算,彻底规避符号计算的性能瓶颈。
方案3:化简符号表达式后再代换
如果必须保留符号表达式生成步骤,可以先对表达式进行化简,减少单项式数量后再代换:
# 生成f_j后先执行化简 f_j_simplified = sym.simplify(f_j) # 再进行代换 result = f_j_simplified.subs([(V,1),(phi,2)])
注:该方法的提速效果取决于表达式的可化简程度,对超大规模多项式的优化幅度有限。
内容的提问来源于stack exchange,提问作者algebroo
相关产品推荐
相关产品推荐

