【问题标题】:Speed up random matrix computation加快随机矩阵计算
【发布时间】:2013-04-24 18:17:56
【问题描述】:

我正在创建随机 Toeplitz 矩阵来估计它们可逆的概率。我当前的代码是

import random
from scipy.linalg import toeplitz
import numpy as np
for n in xrange(1,25):
    rankzero = 0
    for repeats in xrange(50000):
        column = [random.choice([0,1]) for x in xrange(n)]
        row = [column[0]]+[random.choice([0,1]) for x in xrange(n-1)]
        matrix = toeplitz(column, row)
        if  (np.linalg.matrix_rank(matrix) < n):
            rankzero += 1
    print n, (rankzero*1.0)/50000

这可以加速吗?

我想增加值 50000 以获得更高的准确性,但目前这样做太慢了。

仅使用 for n in xrange(10,14) 节目进行分析

  400000    9.482    0.000    9.482    0.000 {numpy.linalg.lapack_lite.dgesdd}
  4400000    7.591    0.000   11.089    0.000 random.py:272(choice)
   200000    6.836    0.000   10.903    0.000 index_tricks.py:144(__getitem__)
        1    5.473    5.473   62.668   62.668 toeplitz.py:3(<module>)
   800065    4.333    0.000    4.333    0.000 {numpy.core.multiarray.array}
   200000    3.513    0.000   19.949    0.000 special_matrices.py:128(toeplitz)
   200000    3.484    0.000   20.250    0.000 linalg.py:1194(svd)
6401273/6401237    2.421    0.000    2.421    0.000 {len}
   200000    2.252    0.000   26.047    0.000 linalg.py:1417(matrix_rank)
  4400000    1.863    0.000    1.863    0.000 {method 'random' of '_random.Random' objects}
  2201015    1.240    0.000    1.240    0.000 {isinstance}
[...]

【问题讨论】:

    标签: python performance math numpy scipy


    【解决方案1】:

    一种方法是通过缓存放置值的索引来节省重复调用 toeplitz() 函数的一些工作。以下代码比原始代码快约 30%。其余的表现在排名计算中...... 而且我不知道对于 0 和 1 的 toeplitz 矩阵是否存在更快的秩计算。

    (更新)如果将 matrix_rank 替换为 scipy.linalg.det() == 0,则代码实际上快 4 倍(行列式比小矩阵的秩计算更快)

    import random
    from scipy.linalg import toeplitz, det
    import numpy as np,numpy.random
    
    class si:
        #cache of info for toeplitz matrix construction
        indx = None
        l = None
    
    def xtoeplitz(c,r):
        vals = np.concatenate((r[-1:0:-1], c))
        if si.indx is None or si.l != len(c):
            a, b = np.ogrid[0:len(c), len(r) - 1:-1:-1]
            si.indx = a + b
            si.l = len(c)
        # `indx` is a 2D array of indices into the 1D array `vals`, arranged so
        # that `vals[indx]` is the Toeplitz matrix.
        return vals[si.indx]
    
    def doit():
        for n in xrange(1,25):
            rankzero = 0
            si.indx=None
    
            for repeats in xrange(5000):
    
                column = np.random.randint(0,2,n)
                #column=[random.choice([0,1]) for x in xrange(n)] # original code
    
                row = np.r_[column[0], np.random.randint(0,2,n-1)]
                #row=[column[0]]+[random.choice([0,1]) for x in xrange(n-1)] #origi
    
                matrix = xtoeplitz(column, row)
                #matrix=toeplitz(column,row) # original code
    
                #if  (np.linalg.matrix_rank(matrix) < n): # original code
                if  np.abs(det(matrix))<1e-4: # should be faster for small matrices
                    rankzero += 1
            print n, (rankzero*1.0)/50000
    

    【讨论】:

    • 非常感谢。你知道什么时候rank变得比det快吗?一个很小的东西,5000应该和底部的50000匹配。
    • det() 与 rank() - 这可能取决于您的 CPU。我只是建议做一个小测试 %timeit det(np.random.randint(0,2,size=(25,25)) vs %timeit matrix_rank(np.random.randint(0,2,size=(25, 25)) 关于 5000 与 50000,我故意将其缩小以便于测试
    • det(np.random.randint(0,2,size=(25,25))) 大约是 42 我们和 matrix_rank(np.random.randint(0,2,size=(25 ,25))) 约为 190 我们。很清楚。
    【解决方案2】:

    构建 0 和 1 列表的这两行代码:

    column = [random.choice([0,1]) for x in xrange(n)]
    row = [column[0]]+[random.choice([0,1]) for x in xrange(n-1)]
    

    有许多低效之处。他们不必要地构建、扩展和丢弃大量列表,并在列表上调用 random.choice() 以获取真正只是一个随机位的内容。我像这样将它们加快了大约 500%:

    column = [0 for i in xrange(n)]
    row = [0 for i in xrange(n)]
    
    # NOTE: n must be less than 32 here, or remove int() and lose some speed
    cbits = int(random.getrandbits(n))
    rbits = int(random.getrandbits(n))
    
    for i in xrange(n):
        column[i] = cbits & 1
        cbits >>= 1
        row[i] = rbits & 1
        rbits >>= 1
    
    row[0] = column[0]
    

    【讨论】:

      【解决方案3】:

      看起来您的原始代码正在调用 lapack 例程 dgesdd 通过首先计算输入矩阵的 LU 分解来求解线性系统。

      matrix_rank 替换为det 使用lapack 的dgetrf 计算行列式,它仅计算输入矩阵的LU 分解(http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.det.html)。

      matrix_rankdet 调用的渐近复杂度因此为 O(n^3),即 LU 分解的复杂度。

      然而,Toepelitz 系统可以在 O(n^2) 中求解(根据维基百科)。所以,如果你想在大型矩阵上运行你的代码,编写一个 python 扩展来调用一个专门的库是有意义的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-30
        • 1970-01-01
        • 2015-09-17
        • 1970-01-01
        • 2019-02-22
        • 2011-11-15
        • 2020-03-21
        • 1970-01-01
        相关资源
        最近更新 更多