我正在编写一个需要某种根查找程序的程序,但是我使用的每个根查找程序的速度都不令人满意。我正在寻找一种加快速度的方法。
我使用了SymPy的nsolve,尽管它产生非常精确的结果,但是却非常慢(如果我对程序进行12次迭代,则需要花费12个小时以上的时间才能运行)。我编写了自己的二等分方法,这种方法效果更好,但是仍然很慢(12次迭代需要大约1个小时才能运行)。我一直无法找到一个symengine求解器,否则我将使用它。我将发布两个程序(使用二分法和nsolve)。非常感谢您提供有关如何加快此速度的建议。
这是使用nsolve的代码:
from symengine import *
import sympy
from sympy import Matrix
from sympy import nsolve
trial = Matrix()
r, E1, E = symbols('r, E1, E')
H11, H22, H12, H21 = symbols("H11, H22, H12, H21")
S11, S22, S12, S21 = symbols("S11, S22, S12, S21")
low = 0
high = oo
integrate = lambda *args: sympy.N(sympy.integrate(*args))
quadratic_expression = (H11-E1*S11)*(H22-E1*S22)-(H12-E1*S12)*(H21-E1*S21)
general_solution = sympify(sympy.solve(quadratic_expression, E1)[0])
def solve_quadratic(**kwargs):
return general_solution.subs(kwargs)
def H(fun):
return -fun.diff(r, 2)/2 - fun.diff(r)/r - fun/r
psi0 = exp(-3*r/2)
trial = trial.row_insert(0, Matrix([psi0]))
I1 = integrate(4*pi*(r**2)*psi0*H(psi0), (r, low, high))
I2 = integrate(4*pi*(r**2)*psi0**2, (r, low, high))
E0 = I1/I2
print(E0)
for x in range(10):
f1 = psi0
f2 = r * (H(psi0)-E0*psi0)
Hf1 = H(f1).simplify()
Hf2 = H(f2).simplify()
H11 = integrate(4*pi*(r**2)*f1*Hf1, (r, low, high))
H12 = integrate(4*pi*(r**2)*f1*Hf2, (r, low, high))
H21 = integrate(4*pi*(r**2)*f2*Hf1, (r, low, high))
H22 = integrate(4*pi*(r**2)*f2*Hf2, (r, low, high))
S11 = integrate(4*pi*(r**2)*f1**2, (r, low, high))
S12 = integrate(4*pi*(r**2)*f1*f2, (r, low, high))
S21 = S12
S22 = integrate(4*pi*(r**2)*f2**2, (r, low, high))
E0 = solve_quadratic(
H11=H11, H22=H22, H12=H12, H21=H21,
S11=S11, S22=S22, S12=S12, S21=S21,
)
print(E0)
C = -(H11 - E0*S11)/(H12 - E0*S12)
psi0 = (f1 + C*f2).simplify()
trial = trial.row_insert(x+1, Matrix([[psi0]]))
# Free ICI Part
h = zeros(x+2, x+2)
HS = zeros(x+2, 1)
S = zeros(x+2, x+2)
for s in range(x+2):
HS[s] = H(trial[s]).simplify()
for i in range(x+2):
for j in range(x+2):
h[i, j] = integrate(4*pi*(r**2)*trial[i]*HS[j], (r, low, high))
for i in range(x+2):
for j in range(x+2):
S[i, j] = integrate(4*pi*(r**2)*trial[i]*trial[j], (r, low, high))
m = h - E*S
eqn = m.det()
roots = nsolve(eqn, float(E0))
print(roots)
这是使用我的二分法的代码:
from symengine import *
import sympy
from sympy import Matrix
from sympy import nsolve
trial = Matrix()
r, E1, E = symbols('r, E1, E')
H11, H22, H12, H21 = symbols("H11, H22, H12, H21")
S11, S22, S12, S21 = symbols("S11, S22, S12, S21")
low = 0
high = oo
integrate = lambda *args: sympy.N(sympy.integrate(*args))
quadratic_expression = (H11-E1*S11)*(H22-E1*S22)-(H12-E1*S12)*(H21-E1*S21)
general_solution = sympify(sympy.solve(quadratic_expression, E1)[0])
def solve_quadratic(**kwargs):
return general_solution.subs(kwargs)
def bisection(fun, a, b, tol):
NMax = 100000
f = Lambdify(E, fun)
FA = f(a)
for n in range(NMax):
p = (b+a)/2
FP = f(p)
if FP == 0 or abs(b-a)/2 < tol:
return p
if FA*FP > 0:
a = p
FA = FP
else:
b = p
print("Failed to converge to desired tolerance")
def H(fun):
return -fun.diff(r, 2)/2 - fun.diff(r)/r - fun/r
psi0 = exp(-3*r/2)
trial = trial.row_insert(0, Matrix([psi0]))
I1 = integrate(4*pi*(r**2)*psi0*H(psi0), (r, low, high))
I2 = integrate(4*pi*(r**2)*psi0**2, (r, low, high))
E0 = I1/I2
print(E0)
for x in range(11):
f1 = psi0
f2 = r * (H(psi0)-E0*psi0)
Hf1 = H(f1).simplify()
Hf2 = H(f2).simplify()
H11 = integrate(4*pi*(r**2)*f1*Hf1, (r, low, high))
H12 = integrate(4*pi*(r**2)*f1*Hf2, (r, low, high))
H21 = integrate(4*pi*(r**2)*f2*Hf1, (r, low, high))
H22 = integrate(4*pi*(r**2)*f2*Hf2, (r, low, high))
S11 = integrate(4*pi*(r**2)*f1**2, (r, low, high))
S12 = integrate(4*pi*(r**2)*f1*f2, (r, low, high))
S21 = S12
S22 = integrate(4*pi*(r**2)*f2**2, (r, low, high))
E0 = solve_quadratic(
H11=H11, H22=H22, H12=H12, H21=H21,
S11=S11, S22=S22, S12=S12, S21=S21,
)
print(E0)
C = -(H11 - E0*S11)/(H12 - E0*S12)
psi0 = (f1 + C*f2).simplify()
trial = trial.row_insert(x+1, Matrix([[psi0]]))
# Free ICI Part
h = zeros(x+2, x+2)
HS = zeros(x+2, 1)
S = zeros(x+2, x+2)
for s in range(x+2):
HS[s] = H(trial[s]).simplify()
for i in range(x+2):
for j in range(x+2):
h[i, j] = integrate(4*pi*(r**2)*trial[i]*HS[j], (r, low, high))
for i in range(x+2):
for j in range(x+2):
S[i, j] = integrate(4*pi*(r**2)*trial[i]*trial[j], (r, low, high))
m = h - E*S
eqn = m.det()
roots = bisection(eqn, E0 - 1, E0, 10**(-15))
print(roots)
正如我说的那样,它们都按预期工作,但是工作非常缓慢。
答案 0 :(得分:1)
以下是您代码的一些优化
Lambdify(E, fun, cse=True)
来消除通用子表达式pi = sympify(sympy.N(pi))
以使用数字值pi
。由于表达式较大,将pi
保留为象征性符号会很麻烦。.simplify
通话更改为.expand
通话。integrate(r**n * exp(-p*r), (r, 0, inf)
的特殊形式,可以很容易地集成。In [21]: var("n, r, p", positive=True)
Out[21]: (n, r, p)
In [22]: integrate(q*r**n*exp(-p*r), (r, 0, oo))
Out[22]: p**(-n)*q*gamma(n + 1)/p
您可以使用下面的hack来获得此优势。 (理想情况下,sympy应该可以更快地执行此操作,但是sympy不能对此做得很好。去年夏天,当我尝试以符号方式求解Dirac和Schrödinger方程式以调试我的数字代码时,我遇到了相同的问题。我假设您是尝试做类似的事情)
def integrate(*args):
args = list(args)
expr = args[0].expand()
r = sympy.S(args[1][0])
limits = args[1][1:]
p = sympy.Wild("p")
n = sympy.Wild("n")
q = sympy.Wild("q")
pattern = q * r**n * sympy.exp(p*r)
terms = expr.args
if not expr.is_Add:
terms = [expr]
result = 0
for arg in terms:
d = sympy.S(arg).match(pattern)
if d is None:
result += sympy.N(sympy.integrate(arg, args[1]))
continue
if d[p].is_number and d[q].is_number and d[n].is_number:
ex = d[q]*(-d[p])**(-d[n])/d[p]*sympy.lowergamma(d[n]+1, -d[p]*r)
result += sympify(sympy.factorial(d[n])*d[q]/(-d[p])**(d[n]+1))
else:
result += sympy.N(sympy.integrate(arg, args[1]))
return result
这4个更改将我的时间减少到16秒。