Lambda函数不返回正确的值

时间:2016-10-17 21:56:29

标签: python numpy lambda

我试图制作Gillespie算法的变体,并确定反应倾向我试图使用lambda表达式自动生成倾向向量。但是在创建SSA.P时出错了。代码块中的最后一个循环PROPLOOP返回两个倾向,其中使用P_alternative生成的那个是正确的。问题是:如何为SSA.P获得与SSA.P_alternative相同的值?

import numpy as np
from numpy.random import uniform
class Markov:
  def __init__(self,z0,t0,tf,rates,stoich):
    self.S=stoich

    self.z0=z0
    self.rates=rates

    self.P=self.propensities()
    self.P_alternative=[
      lambda z,rate:(0.5*rate[0]*z[0]*(z[0]-1)),
      lambda z,rate:rate[1]*np.prod(z[0]),
      lambda z,rate:rate[2]*np.prod(z[1]),
      lambda z,rate:rate[3]*np.prod(z[1]),
      lambda z,rate:rate[4]*np.prod(z[np.array([0,1])]),
      lambda z,rate:rate[5]]

    self.t0=t0
    self.tf=tf


  def propensities(self):
    prop=[]
    for i,reac in enumerate(self.S.T):
      if all(z>=0 for z in reac):
        prop.append(lambda z,rate:rate[i])

      if any(z==-1 for z in reac):
        j=np.where(reac==-1)[0]
        prop.append(lambda z,rate:rate[i]*np.prod(z[j]))

      if any(z==-2 for z in reac):
        j=np.where(reac==-2)[0][0]
        prop.append(lambda z,rate:(0.5*rate[i]*z[j]*(z[j]-1))[0])

    return prop


stoich=np.array([
        [-2, -1,  2,  0, -1,  0],
        [ 1,  0, -1, -1, -1,  1],
        [ 0,  0,  0,  1,  1,  0]])

rates=np.array([1.0,0.02,200.0,0.0004,0.9,0.9])

z0=np.array([540,730,0])

SSA=Markov(z0=z0,t0=0,tf=100,rates=rates,stoich=stoich)

#PROPLOOP; the values should be equal for both SSA.P and SSA.P_alternative, where SSA.P_alternative is the correct one
for i in xrange(len(SSA.P)):
  print "Inexplicably wrong",SSA.P[i](z0,rates)
  print "Correct answer",SSA.P_alternative[i](z0,rates), "\n"

输出是:

Inexplicably wrong 130977.0
Correct answer 145530.0 

Inexplicably wrong 354780.0
Correct answer 10.8 

Inexplicably wrong 354780.0
Correct answer 146000.0 

Inexplicably wrong 354780.0
Correct answer 0.292 

Correct answer 354780.0
Correct answer 354780.0 

Inexplicably wrong 0.9
Correct answer 0.9 

1 个答案:

答案 0 :(得分:3)

问题在于您在循环中创建了lambda个函数,并且它们引用了随着循环继续而变化的变量ij

lambda在创建时不会复制ij的值,它只保留对它们所定义的命名空间的引用。当它使用变量时稍后会调用它,它会在该命名空间中查找它们。由于你的lambdas在循环之后被调用(事实上,整个函数)已经结束,它们都会看到给出变量的最终值,这不是你想要的。这解释了为什么代码的两个版本在最后一次迭代中给出相同的输出。 ij的最终值是上一个lambda函数的预期值。

您可以通过lambda保留ij当前值的副本来解决此问题。最简单的方法是使用默认参数:

for i,reac in enumerate(self.S.T):
  if all(z>=0 for z in reac):
    prop.append(lambda z, rate, i=i: rate[i]) # add i=i here and further down

  if any(z==-1 for z in reac):
    j=np.where(reac==-1)[0]
    prop.append(lambda z, rate, i=i, j=j: rate[i]*np.prod(z[j]))

  if any(z==-2 for z in reac):
    j=np.where(reac==-2)[0][0]
    prop.append(lambda z, rate, i=i, j=j: (0.5*rate[i]*z[j]*(z[j]-1))[0])

lambda定义中的i=i(以及j=j必要时)​​使lambda函数的变量参数具有默认值,即当前值i(和{{ 1}})在外部命名空间中。由于在调用lambda函数时只传递了两个参数,因此将使用保存的默认值。