有没有人有正确实现multivariate_normal pdf的示例numpy.fftn代码?

时间:2015-08-12 22:58:59

标签: python numpy fft normal-distribution

使用下面的代码,我试图实现Levy-Khintchine公式(https://en.wikipedia.org/wiki/L%C3%A9vy_process#L.C3.A9vy.E2.80.93Khintchine_representation)。在没有跳跃的限制下,Levy-Khitchine公式降低到多元正态分布。我的代码使用(多维)梯形积分规则(http://mathfaculty.fullerton.edu/mathews/n2003/SimpsonsRule2DMod.html)来近似特征函数的傅立叶变换作为离散傅里叶变换。对于1维情况,代码完美运行。对于二维案例,我找不到我做错的事。

有没有人有正确实现multivariate_normal pdf的示例numpy.fftn代码?

class LevyKhintchine:
    def __init__(self, mean, cov, jump_measure):
        self.mean = mean
        self.cov = cov
        self.jump_measure = jump_measure
        self.factors = mean.shape[0]
    def logCF(self, k):
        rolled = Roll(k)
        out = np.empty(Shape(k))
        return (self.jump_measure(k) -
            Dot(rolled, self.cov, rolled, out)*0.5 +
            np.sum(np.multiply(Roll(k), self.mean), axis=-1)*1j)
    def pdf_grid(self, J):
        diag = np.diagonal(self.cov)
        tmp = np.pi*2/J
        dk = np.sqrt(tmp/diag)
        dx = np.sqrt(tmp*diag)
        k = Grid(np.zeros(self.factors), dk, J)
        x0 = self.mean - dx*J*0.5
        f = np.exp(self.logCF(k) - Coef(dk, x0, J)*1j)
        for n in range(self.factors):
            f[ 0] *= 0.5
            f[-1] *= 0.5
            f = np.rollaxis(f, 0, factors)
        pdf = np.fft.fftn(f)
        return Grid(x0, dx, J), pdf.real*(np.product(dk)/np.pi)

def Grid(left, width, J):
    def Slice(slices, j):
        slices.append(slice(left[j], left[j] + width[j]*(J-1), 1j*J))
        return slices
    slices = reduce(Slice, range(len(left)), [])
    return np.mgrid[slices]

def Shape(grid):
    return np.asarray(grid).shape[1:]

def Roll(grid):
    grid = np.asarray(grid)
    try:
        rolled = np.rollaxis(grid, 0, len(grid)+1)
    except ValueError:
        rolled = grid
    return rolled

def Dot(x, cov, y, out): #x & y are "rolled"
    for j in np.ndindex(out.shape):
        out[j] = np.dot(x[j].T, np.dot(cov, y[j]))
    return out

def Coef(dks, x0s, J):
    factors = len(dks)
    coef = np.zeros((J,)*factors)
    for n, (dk, x0) in enumerate(zip(dks, x0s)):
        shape = np.ones(factors, dtype=int)
        shape[n] = J
        coef += np.arange(J).reshape(shape)*(dk*x0)
    return coef

以下是测试:

from scipy.stats import multivariate_normal

J = 64

factors = 1
mean = np.full((factors,), -1)
cov = np.identity(factors)
rv = LevyKhintchine(mean, cov, lambda k: 0)
rv0 = multivariate_normal(mean, cov)

x, pdf = rv.pdf_grid(J)
plt.plot(x[0], pdf, x[0], rv0.pdf(Roll(x)))


factors = 2
mean = np.full((factors,), 5)
cov = np.identity(factors)
rv = LevyKhintchine(mean, cov, lambda k: 0)
x, pdf = rv.pdf_grid(J)
rv0 = multivariate_normal(mean, cov)

fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
ax2.contourf(x[0], x[1], pdf)
fig3 = plt.figure()
ax3 = fig3.add_subplot(111)
ax3.contourf(x[0], x[1], rv0.pdf(Roll(x)))

1 个答案:

答案 0 :(得分:0)

我明白了:在1-d中我只能积极地积累正波数k,而在更高的维度上我不能。

以下是更正后的代码:

class LevyKhintchine:
    def __init__(self, mean, cov, jump_measure):
        self.mean = mean
        self.cov = cov
        self.jump_measure = jump_measure
        self.factors = mean.shape[0]
    def logCF(self, k):
        rolled = Roll(k)
        out = np.empty(Shape(k))
        return (self.jump_measure(k) -
            Dot(rolled, self.cov, rolled, out)*0.5 +
            np.sum(np.multiply(Roll(k), self.mean), axis=-1)*1j)
    def pdf_grid(self, J):
        diag = np.diagonal(self.cov)
        tmp = np.pi*2/J
        dk = np.sqrt(tmp/diag)
        dx = np.sqrt(tmp*diag)
        k0 = -0.5*dk*J
        x0 = -0.5*dx*J + self.mean
        k = Grid(k0, dk, J)
        x = Grid(x0, dx, J)
        f = np.exp(-1j*Coef(dk, x0, J) + self.logCF(k))
        for n in range(self.factors):
            f[ 0] *= 0.5
            f[-1] *= 0.5
            f = np.rollaxis(f, 0, factors)
        c = ((0.5/np.pi)**self.factors*np.product(dk)*np.exp(-1j*np.dot(k0, x0)))
        pdf = np.fft.fftn(f)*np.exp(-1j*Coef(k0, dx, J))*c
        return x, pdf.real

def Grid(left, width, J):
    def Slice(slices, j):
        slices.append(slice(left[j], left[j] + width[j]*(J-1), 1j*J))
        return slices
    slices = reduce(Slice, range(len(left)), [])
    return np.mgrid[slices]

def Shape(grid):
    return np.asarray(grid).shape[1:]

def Roll(grid):
    grid = np.asarray(grid)
    try:
        rolled = np.rollaxis(grid, 0, len(grid)+1)
    except ValueError:
        rolled = grid
    return rolled

def Dot(x, cov, y, out): #x & y are "rolled"
    for j in np.ndindex(out.shape):
        out[j] = np.dot(x[j].T, np.dot(cov, y[j]))
    return out

def Coef(dks, x0s, J):
    factors = len(dks)
    coef = np.zeros((J,)*factors)
    for n, (dk, x0) in enumerate(zip(dks, x0s)):
        shape = np.ones(factors, dtype=int)
        shape[n] = J
        coef += np.arange(J).reshape(shape)*(dk*x0)
    return coef

以下是测试:

from scipy.stats import multivariate_normal

J = 32

for factors in range(1, 4):
    mean = np.full((factors,), -1)
    cov = np.identity(factors)
    rv = LevyKhintchine(mean, cov, lambda k: 0)
    rv0 = multivariate_normal(mean, cov)
    x, pdf = rv.pdf_grid(J)
    pdf0 = rv0.pdf(Roll(x))
    print np.allclose(pdf, pdf0)

True
True
True