Python 2.7 Cython中的二项式迭代期望

Python 2.7 Cython中的二项式迭代期望,python-2.7,numpy,cython,Python 2.7,Numpy,Cython,我是Cython的新手。如何将下面名为Values的Python函数转换为Cython?在因子=2和i=60的情况下,在我的大Linux机器上需要2.8秒。目标是1秒以下,系数=2,i=360 这是密码。谢谢 import numpy as np import itertools class Numeraire: def __init__(self, rate): self.rate = rate def __call__(self, timenext, tim

我是Cython的新手。如何将下面名为Values的Python函数转换为Cython?在因子=2和i=60的情况下,在我的大Linux机器上需要2.8秒。目标是1秒以下,系数=2,i=360

这是密码。谢谢

import numpy as np
import itertools

class Numeraire:
    def __init__(self, rate):
        self.rate = rate
    def __call__(self, timenext, time, state):
        return np.exp(-self.rate*(timenext - time))

def Values(values, i1, i0=0, numeraire=Numeraire(0.)):
    factors=len(values.shape)
    norm=0.5**factors
    for i in np.arange(i1-1, i0-1, -1):
        for j in itertools.product(np.arange(i+1), repeat=factors):
            value = 0.
            for k in itertools.product(np.arange(2), repeat=factors):
                value += values[tuple(np.array(j) + np.array(k))]
            values[j] = value*norm*numeraire(i+1, i, j)
    return values

factors = 2
i = 60
values = np.ones([i+1]*factors)
Values(values, i, numeraire=Numeraire(0.05/12))
print values[(0,)*factors], np.exp(-0.05/12*i)

在使用Cython之前,应该使用Numpy优化代码。在这里,为循环对第三个和第二个内部
进行矢量化,可产生x40加速

In [1]: import numpy as np
   ...: import itertools
   ...: 
   ...:  # define Numaire and Values functions from the question above
   ...:
   ...: def Values2(values, i1, i0=0, numeraire=Numeraire(0.)):
   ...:     factors=len(values.shape)
   ...:     norm=0.5**factors
   ...:     k = np.array(list(itertools.product(np.arange(2), repeat=factors)))
   ...:     for i in np.arange(i1-1, i0-1, -1):
   ...:         j = np.array(list(itertools.product(np.arange(i+1), repeat=factors)))
   ...:         mask_all = j[:,:,np.newaxis] + k.T[np.newaxis, :, :]
   ...:         mask_x, mask_y = np.swapaxes(mask_all, 2, 1).reshape(-1, 2).T
   ...:      
   ...:         values_tmp = values[mask_x, mask_y].reshape((j.shape[0], k.shape[0]))
   ...:         values_tmp = values_tmp.sum(axis=1)
   ...:         values[j[:,0], j[:,1]] = values_tmp*norm*numeraire(i+1, i, j)
   ...:     return values
   ...:
   ...: factors = 2
   ...: i = 60
   ...: values = lambda : np.ones([i+1]*factors)
   ...: print values()[(0,)*factors], np.exp(-0.05/12*i)
   ...:
   ...: res = Values(values(), i, numeraire=Numeraire(0.05/12))
   ...: res2 = Values2(values(), i, numeraire=Numeraire(0.05/12))
   ...: np.testing.assert_allclose(res, res2)
   ...:
   ...: %timeit  Values(values(), i, numeraire=Numeraire(0.05/12))
   ...: %timeit  Values2(values(), i, numeraire=Numeraire(0.05/12))
   ...:
1.0 0.778800783071
1 loops, best of 3: 1.26 s per loop
10 loops, best of 3: 31.8 ms per loop
下一步是更换生产线

j = np.array(list(itertools.product(np.arange(i+1), repeat=factors)
与之相当的是Numpy,取自此(不太漂亮)

这导致x160的整体速度提高,代码在我的笔记本电脑上以1.2秒的时间运行
i=360
factors=2

在最后一个版本中,我认为如果你把它移植到Cython上,速度不会有多大提高,因为只剩下一个循环,而且只有360次迭代。相反,应该执行一些微调的Python/Numpy优化,以进一步提高速度


或者,您可以尝试将Cython应用于原始实现。但是,由于它基于
itertools.product
,在循环中重复调用时速度很慢,Cython对此没有帮助。

在使用Cython之前,您应该使用Numpy优化代码。在这里,为
循环对第三个和第二个内部
进行矢量化,可产生x40加速

In [1]: import numpy as np
   ...: import itertools
   ...: 
   ...:  # define Numaire and Values functions from the question above
   ...:
   ...: def Values2(values, i1, i0=0, numeraire=Numeraire(0.)):
   ...:     factors=len(values.shape)
   ...:     norm=0.5**factors
   ...:     k = np.array(list(itertools.product(np.arange(2), repeat=factors)))
   ...:     for i in np.arange(i1-1, i0-1, -1):
   ...:         j = np.array(list(itertools.product(np.arange(i+1), repeat=factors)))
   ...:         mask_all = j[:,:,np.newaxis] + k.T[np.newaxis, :, :]
   ...:         mask_x, mask_y = np.swapaxes(mask_all, 2, 1).reshape(-1, 2).T
   ...:      
   ...:         values_tmp = values[mask_x, mask_y].reshape((j.shape[0], k.shape[0]))
   ...:         values_tmp = values_tmp.sum(axis=1)
   ...:         values[j[:,0], j[:,1]] = values_tmp*norm*numeraire(i+1, i, j)
   ...:     return values
   ...:
   ...: factors = 2
   ...: i = 60
   ...: values = lambda : np.ones([i+1]*factors)
   ...: print values()[(0,)*factors], np.exp(-0.05/12*i)
   ...:
   ...: res = Values(values(), i, numeraire=Numeraire(0.05/12))
   ...: res2 = Values2(values(), i, numeraire=Numeraire(0.05/12))
   ...: np.testing.assert_allclose(res, res2)
   ...:
   ...: %timeit  Values(values(), i, numeraire=Numeraire(0.05/12))
   ...: %timeit  Values2(values(), i, numeraire=Numeraire(0.05/12))
   ...:
1.0 0.778800783071
1 loops, best of 3: 1.26 s per loop
10 loops, best of 3: 31.8 ms per loop
下一步是更换生产线

j = np.array(list(itertools.product(np.arange(i+1), repeat=factors)
与之相当的是Numpy,取自此(不太漂亮)

这导致x160的整体速度提高,代码在我的笔记本电脑上以1.2秒的时间运行
i=360
factors=2

在最后一个版本中,我认为如果你把它移植到Cython上,速度不会有多大提高,因为只剩下一个循环,而且只有360次迭代。相反,应该执行一些微调的Python/Numpy优化,以进一步提高速度

或者,您可以尝试将Cython应用于原始实现。但是,因为它基于
itertools.product
,在循环中重复调用时速度很慢,Cython对此没有帮助。

下面是我的最新答案(没有Cython!),对于
因子=2
i=360
的情况,它在125毫秒内运行

import numpy as np
import itertools

slices = (slice(None, -1, None), slice(1, None, None))

def Expectation(values, numeraire, i, i0=0):
    def Values(values, i):
        factors = values.ndim
        expect = np.zeros((i,)*factors)
        for j in itertools.product(slices, repeat=factors):
            expect += values[j]
        return expect*0.5**factors*numeraire(i, i-1)
    return reduce(Values, range(i, i0, -1), values)

class Numeraire:
    def __init__(self, factors, rate=0):
        self.factors = factors
        self.rate = rate
    def __call__(self, timenext, time):
        return np.full((time+1,)*factors, np.exp(-self.rate*(timenext - time)))

factors = 2
i = 360
values, numeraire = np.ones((i+1,)*factors), Numeraire(factors, 0.05/12)
%timeit Expectation(values, numeraire, i)
Expectation(values, numeraire, i)[(0,)*factors], np.exp(-0.05/12*i)
这是我的最新答案(不是Cython!),对于
factor=2
i=360
的情况,它在125毫秒内运行

import numpy as np
import itertools

slices = (slice(None, -1, None), slice(1, None, None))

def Expectation(values, numeraire, i, i0=0):
    def Values(values, i):
        factors = values.ndim
        expect = np.zeros((i,)*factors)
        for j in itertools.product(slices, repeat=factors):
            expect += values[j]
        return expect*0.5**factors*numeraire(i, i-1)
    return reduce(Values, range(i, i0, -1), values)

class Numeraire:
    def __init__(self, factors, rate=0):
        self.factors = factors
        self.rate = rate
    def __call__(self, timenext, time):
        return np.full((time+1,)*factors, np.exp(-self.rate*(timenext - time)))

factors = 2
i = 360
values, numeraire = np.ones((i+1,)*factors), Numeraire(factors, 0.05/12)
%timeit Expectation(values, numeraire, i)
Expectation(values, numeraire, i)[(0,)*factors], np.exp(-0.05/12*i)

在引入Cython之前,您可能应该尝试使用标准的NumPy矢量化操作。特别是,除非你开始以矢量化的方式计算,或者将其移动到Cython中,否则你很快就会被
numeraire
束缚。我认为如果你解释一下这段代码的作用,包括对小数组的影响,你会得到更多的帮助。对于您的示例,
在左上角最小,在“圆圈”中在右侧和底部增加到1。为什么?因为它试图实现一个二项式晶格,用于期权定价。这里有一个参考:在引入Cython之前,您可能应该尝试使用标准的NumPy矢量化操作。特别是,除非你开始以矢量化的方式计算,或者将其移动到Cython中,否则你很快就会被
numeraire
束缚。我认为如果你解释一下这段代码的作用,包括对小数组的影响,你会得到更多的帮助。对于您的示例,
在左上角最小,在“圆圈”中在右侧和底部增加到1。为什么?因为它试图实现一个二项式晶格,用于期权定价。这里有一个参考:哇,谢谢你的精彩回答!在我的机器上,您的解决方案只需1.4秒!抱歉贪心,但是,您的函数是否可以修改为允许因子=1的情况,例如?@SteveSchulist Aww,是的,对不起,我没有意识到
因子
列,这确实是针对
因子=2
的情况(即2D数组)而做的,并且需要推广到ND数组(或至少1D). 不幸的是,我不认为我可以在这周看…哇,谢谢你的精彩回答!在我的机器上,您的解决方案只需1.4秒!抱歉贪心,但是,您的函数是否可以修改为允许因子=1的情况,例如?@SteveSchulist Aww,是的,对不起,我没有意识到
因子
列,这确实是针对
因子=2
的情况(即2D数组)而做的,并且需要推广到ND数组(或至少1D). 不幸的是,我不认为这周我可以调查。。。