Lambda 函数未返回正确的值

Pat*_*ens 4 python lambda numpy

我正在尝试制作 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"
Run Code Online (Sandbox Code Playgroud)

输出是:

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 
Run Code Online (Sandbox Code Playgroud)

Blc*_*ght 6

问题是您lambda在循环中创建函数,它们引用变量i,并且j变量可能会随着循环的进行而改变。

lambda在创建时不会复制i或的值,它只是保留对定义它们的名称空间的引用。当稍后调用它时使用变量时,它会在该名称空间中查找它们。j由于您的 lambda 在循环(实际上是整个函数)结束后被调用,因此它们都会看到给定变量的最终值,这不是您想要的。这解释了为什么代码的两个版本在最后一次迭代中给出相同的输出。i和的最终值j是最后一个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])
Run Code Online (Sandbox Code Playgroud)

lambda 定义中的( i=iandj=j必要时) 使 lambda 函数的变量参数具有默认值,该默认值是外部命名空间中i(and ) 的当前值。j由于调用 lambda 函数时仅传递两个参数,因此将使用保存的默认值。