求解大型马尔可夫链

日期2008-12-28(最后修改),2008-12-28(创建)

本页展示了如何计算大型马尔可夫链的平稳分布 pi。示例是一个由两个 M/M/1 队列组成的串联系统。通常,马尔可夫链的转移矩阵 P 是稀疏的,因此我们可以使用 scipy.sparse 或 Pysparse。这里我们将演示如何使用这两个工具。

幂法

在本节中,我们将通过称为幂法的迭代方法找到 pi。更具体地说,给定一个(随机)转移矩阵 P 和一个初始向量 pi_0,迭代地计算 pi_n = pi_{n-1} P,直到 pi_n 和 pi_{n-1} 之间的距离(在某个范数中)足够小。

首先,我们构建相关马尔可夫链的生成矩阵 Q。然后,我们通过均匀化方法将 Q 转换为转移矩阵 P,即,我们将 P 定义为 I - Q/l,其中 I 是与 Q 相同大小的单位矩阵,l 是 Q 对角线上最小的元素。一旦我们有了 P,我们就可以通过迭代 pi_n = pi_0 P\^n 来近似 pi(满足 pi = pi P 的 P 的左特征向量),其中 pi_0 是某个初始向量。

关于上述方法的更多细节可以在(或多或少)任何关于概率和马尔可夫链的书籍中找到。一个很好的例子,包含许多很好的例子并关注马尔可夫链的数值解,是 G. Bolch 等人撰写的《排队网络与马尔可夫链》,John Wiley,第二版,2006 年。

您可以在此处获取本教程的源代码:tandemqueue.py

在 [ ]
#!/usr/bin/env python

labda, mu1, mu2 = 1., 1.01, 1.001
N1, N2 = 50, 50
size = N1*N2

from numpy import ones, zeros, empty
import scipy.sparse as sp
import  pysparse
from pylab import matshow, savefig
from scipy.linalg import norm
import time

这个简单的函数将状态 (i,j) 转换为更适合定义转移矩阵的形式,其中 (i,j) 表示第一个队列包含 i 个作业,第二个队列包含 j 个作业。

在 [ ]
def state(i,j):
    return j*N1 + i

构建生成矩阵 Q 的非对角元素。

在 [ ]
def fillOffDiagonal(Q):
    # labda
    for i in range(0,N1-1):
        for j in range(0,N2):
            Q[(state(i,j),state(i+1,j))]= labda
    # mu2
    for i in range(0,N1):
        for j in range(1,N2):
            Q[(state(i,j),state(i,j-1))]= mu2
    # mu1
    for i in range(1,N1):
        for j in range(0,N2-1):
            Q[(state(i,j),state(i-1,j+1))]= mu1
    print "ready filling"

在这个函数中,我们使用 scipy.sparse

在 [ ]
def computePiMethod1():
    e0 = time.time()
    Q = sp.dok_matrix((size,size))
    fillOffDiagonal(Q)
    # Set the diagonal of Q such that the row sums are zero
    Q.setdiag( -Q*ones(size) )
    # Compute a suitable stochastic matrix by means of uniformization
    l = min(Q.values())*1.001  # avoid periodicity, see the book of Bolch et al.
    P = sp.speye(size, size) - Q/l
    # compute Pi
    P =  P.tocsr()
    pi = zeros(size);  pi1 = zeros(size)
    pi[0] = 1;
    n = norm(pi - pi1,1); i = 0;
    while n > 1e-3 and i < 1e5:
        pi1 = pi*P
        pi = pi1*P   # avoid copying pi1 to pi
        n = norm(pi - pi1,1); i += 1
    print "Method 1: ", time.time() - e0, i
    return pi

现在使用 Pysparse。

在 [ ]
def computePiMethod2():
    e0 = time.time()
    Q = pysparse.spmatrix.ll_mat(size,size)
    fillOffDiagonal(Q)
    # fill diagonal
    x =  empty(size)
    Q.matvec(ones(size),x)
    Q.put(-x)
    # uniformize
    l = min(Q.values())*1.001
    P = pysparse.spmatrix.ll_mat(size,size)
    P.put(ones(size))
    P.shift(-1./l, Q)
    # Compute pi
    P = P.to_csr()
    pi = zeros(size);  pi1 = zeros(size)
    pi[0] = 1;
    n = norm(pi - pi1,1); i = 0;
    while n > 1e-3 and i < 1e5:
        P.matvec_transp(pi,pi1)
        P.matvec_transp(pi1,pi)
        n = norm(pi - pi1,1); i += 1
    print "Method 2: ", time.time() - e0, i
    return pi

输出结果。

在 [ ]
def plotPi(pi):
    pi = pi.reshape(N2,N1)
    matshow(pi)
    savefig("pi.eps")
if __name__ == "__main__":
    pi = computePiMethod1()
    pi = computePiMethod2()
    plotPi(pi)

以下是结果

本教程的改进

包括其他方法,例如雅可比方法或高斯-赛德尔方法。

章节作者:nokfi

附件