简体   繁体   English

python中的递归函数定义

[英]Recursive function definition in python

So I am trying to implement a version of Hartree-Fock theory for a band system. 因此,我正在尝试为乐队系统实现Hartree-Fock理论的一种版本。 Basically, it's a matrix convergence problem. 基本上,这是一个矩阵收敛问题。 I have a matrix H0, from whose eigenvalues I can construct another matrix F. The procedure is then to define H1 = H0 + F and check if the eigenvalues of H1 is close to the ones of H0. 我有一个矩阵H0,我可以从该矩阵的特征值构造另一个矩阵F。然后,该过程将定义H1 = H0 + F,并检查H1的特征值是否接近H0的特征值。 If not, I construct a new F from eigenvalues of H1 and define H2 = H0 + F. Then check again and iterate. 如果不是,则根据H1的特征值构造一个新的F,并定义H2 = H0 +F。然后再次检查并进行迭代。

The problem is somewhat generic and my exact code seems not really relevant. 这个问题有点笼统,我的确切代码似乎并不重要。 So I am showing only this: 所以我只显示这个:

# define the matrix F
def generate_fock(H):
    def fock(k): #k is a 2D array
        matt = some prefactor*outer(eigenvectors of H(k) with itself) #error1
        return matt
    return fock

k0 = linspace(0,5*kt/2,51)
# H0 is considered defined
H = lambda k: H0(k)
evalold = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[the ones I care]
while True:
    fe = generate_fock(H)
    H = lambda k: H0(k)+fe(k) #error2
    evalnew = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[the ones I care]
    if allclose(evalnew, evalold): break
    else: evalold = evalnew

I am using inner functions, hoping that python would not find my definitions are recursive (I am not sure if I am using the word correctly). 我正在使用内部函数,希望python不会发现我的定义是递归的(我不确定我是否正确使用了该词)。 But python knows :( Any suggestions? 但是python知道:(有什么建议吗?

Edit1: The error message is highlighting the lines I labeled error1 and error2 and showing the following: Edit1:错误消息突出显示了我标记为error1error2的行,并显示以下内容:

RecursionError: maximum recursion depth exceeded while calling a Python object

I think this comes from my way of defining the functions: In loop n, F(k) depends on H(k) of the previous loop and H(k) in the next step depends on F(k) again. 我认为这来自于我定义函数的方式:在循环n中,F(k)取决于上一个循环的H(k),而下一步中的H(k)再次取决于F(k)。 My question is how do I get around this? 我的问题是我该如何解决?

Edit2&3: Let me add more details to the code as suggested. Edit2&3:让我根据建议向代码中添加更多详细信息。 This is the shortest thing I can come up with that exactly reproduce my problem. 这是我想出的最短的东西,可以准确地重现我的问题。

from numpy import *
from scipy import linalg

# Let's say H0 is any 2m by 2m Hermitian matrix. m = 4 in this case.
# Here are some simplified parameters 
def h(i,k):
    return -6*linalg.norm(k)*array([[0,exp(1j*(angle(k@array([1,1j]))+(-1)**i*0.1/2))],
                                    [exp(-1j*(angle(k@array([1,1j]))+(-1)**i*0.1/2)),0]])
T = array([ones([2,2]),
          [[exp(-1j*2*pi/3),1],[exp(1j*2*pi/3),exp(-1j*2*pi/3)]],
          [[exp(1j*2*pi/3),1],[exp(-1j*2*pi/3),exp(1j*2*pi/3)]]])
g = array([[ 0.27023695,  0.46806412], [-0.27023695,  0.46806412]])
kt = linalg.norm(g[0])
def H0(k):
    "one example"
    matt = linalg.block_diag(h(1,k),h(2,k+g[0]),h(2,k+g[1]),h(2,k+g[0]+g[1]))/2
    for j in range(3): matt[0:2,2*j+2:2*j+4] = T[j]
    return array(matrix(matt).getH())+matt
dim = 4
def bz(x):
    "BZ centered at 0 with (2x)^2 points in it"
    tempList = []
    for i in range(-x,x):
        for j in range(-x,x):
            tempList.append(i*g[0]/2/x+j*g[1]/2/x)
    return tempList
def V(i,G):
    "2D Coulomb interaction"
    if linalg.norm(G)==0: return 0
    if i>=dim: t=1
    else: t=0
    return 2*pi/linalg.norm(G)*exp(0.3*linalg.norm(G)*(-1+(-1)**t)/2)

# define the matrix F for some H
def generate_fock(H):
    def fock(k): #k is a 2D array
        matf = zeros([2*dim,2*dim],dtype=complex128)
        for pt in bz(1): #bz is a list of 2D arrays
            matt = zeros([2*dim,2*dim],dtype=complex128)
            eig_vals1, eig_vecs1 = linalg.eigh(H(pt)) #error1
            idx = eig_vals1.argsort()[::]
            vecs1 = eig_vecs1[:,idx][:dim]
            for vec in vecs1:
                matt = matt + outer(conjugate(vec),vec)
            matt = matt.transpose()/len(bz(1))
            for i in range(2*dim):
                for j in range(2*dim):
                    matf[i,j] = V(j-i,pt-k)*matt[i,j] #V is some prefactor
        return matf
    return fock

k0 = linspace(0,5*kt/2,51)
H = lambda k: H0(k)
evalold = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[dim-1:dim+1]
while True:
    fe = generate_fock(H)
    H = lambda k: H0(k)+fe(k) #error2
    evalnew = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[dim-1:dim+1]
    if allclose(evalnew, evalold): break
    else: evalold = evalnew

The problem is these lines: 问题是这些行:

while True:
    fe = generate_fock(H)
    H = lambda k: H0(k)+fe(k) #error2

In each iteration, you are generating a new function referencing the older one rather than the final output of that older function, so it has to keep them all on the stack. 在每次迭代中,您将生成一个引用较旧函数的新函数,而不是该较旧函数的最终输出,因此它必须将所有这些函数都保留在堆栈中。 This will also be very slow, since you have to back multiply all your matrices every iteration. 这也将非常慢,因为您必须在每次迭代后将所有矩阵都相乘。

What you want to do is keep the output of the old values, probably by making a list from the results of the prior iteration and then applying functions from that list. 您想要做的是保留旧值的输出,可能是通过根据先前迭代的结果创建一个列表,然后应用该列表中的函数。

Potentially you could even do this with a cache, though it might get huge. 即使它可能变得巨大,您甚至可能还可以使用缓存来执行此操作。 Keep a dictionary of inputs to the function and use that. 保留该函数的输入字典并使用它。 Something like this: 像这样:

# define the matrix F
def generate_fock(H):
    d = {}
    def fock(k): #k is a 2D array
        if k in d:
            return d[k]
        matt = some prefactor*outer(eigenvectors of H(k) with itself) #error1
        d[k] = matt
        return matt
    return fock

Then it should hopefully only have to reference the last version of the function. 然后,它应该只需要引用该函数的最新版本。

EDIT: Give this a try. 编辑:试试看。 As well as caching the result, keep an index into an array of functions instead of a reference. 在缓存结果的同时,将索引保留在函数数组中,而不是引用中。 This should prevent a recursion depth overflow. 这应该防止递归深度溢出。

hList = []

# define the matrix F
def generate_fock(n):
    d = {}
    def fock(k): #k is a 2D array
        if k in d:
            return d[k]
        matt = some prefactor*outer(eigenvectors of hList[n](k) with itself) #error1
        d[k] = matt
        return matt
    return fock

k0 = linspace(0,5*kt/2,51)
# H0 is considered defined
HList.append(lambda k: H0(k))
H = HList[0]
evalold = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[the ones I care]
n = 0
while True:
    fe = generate_fock(n)
    n += 1
    hList.append(lambda k: H0(k)+fe(k)) #error2
    H = hList[-1]
    evalnew = array([sort(linalg.eigvalsh(H(array([0,2*k-kt/2])))) for k in k0[:]])[the ones I care]
    if allclose(evalnew, evalold): break
    else: evalold = evalnew

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM