检查python中的大矩阵是否是对角矩阵

时间:2017-05-10 05:27:29

标签: python numpy matrix

我计算了一个非常大的矩阵M,其中有许多简并特征向量(具有相同特征值的不同特征向量)。我使用QR分解来确保这些特征向量是正交的,因此Q是M的正交特征向量,并且Q ^ { - 1} MQ = D,其中D是对角矩阵。现在我想检查D是否是真正的对角矩阵,但是当我打印D时,矩阵太大而无法显示所有这些矩阵,那么我怎么知道它是否是真正的对角矩阵?

8 个答案:

答案 0 :(得分:2)

移除对角线并计算非零元素:

np.count_nonzero(x - np.diag(np.diagonal(x)))

答案 1 :(得分:2)

方法1:使用NumPy strides / np.lib.stride_tricks.as_strided

我们可以利用NumPy strides为我们提供非诊断元素作为视图。因此,那里没有内存开销,几乎没有运行时间!以前在this post中已经探讨了这个想法。

因此,我们有-

# https://stackoverflow.com/a/43761941/ @Divakar
def nodiag_view(a):
    m = a.shape[0]
    p,q = a.strides
    return np.lib.stride_tricks.as_strided(a[:,1:], (m-1,m), (p+q,q))

运行示例以展示其用法-

In [175]: a
Out[175]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

In [176]: nodiag_view(a)
Out[176]: 
array([[ 1,  2,  3,  4],
       [ 6,  7,  8,  9],
       [11, 12, 13, 14]])

让我们通过在大型数组上使用它来验证免费运行时并且没有内存开销索赔-

In [182]: a = np.zeros((10000,10000), dtype=int)
     ...: np.fill_diagonal(a,np.arange(len(a)))

In [183]: %timeit nodiag_view(a)
6.42 µs ± 48.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [184]: np.shares_memory(a, nodiag_view(a))
Out[184]: True

现在,我们在这里如何使用它?只需检查所有nodiag_view元素是否都为0,表示对角矩阵!

因此,为了解决这里的问题,对于输入数组a,它应该是-

isdiag = (nodiag_view(a)==0).all()

方法2:破解方式

出于完整性考虑,一种骇人听闻的方法是临时保存diag元素,在其中分配0s,然后检查所有元素是否均为0。如果是这样,则发信号通知对角矩阵。最后分配回diag元素。

实施方式为-

def hacky_way(a):
    diag_elem = np.diag(a).copy()
    np.fill_diagonal(a,0)
    out = (a==0).all()
    np.fill_diagonal(a,diag_elem)
    return out

基准化

让我们花大量时间看一下它们在性能上的比较-

In [3]: a = np.zeros((10000,10000), dtype=int)
   ...: np.fill_diagonal(a,np.arange(len(a)))

In [4]: %timeit (nodiag_view(a)==0).all()
52.3 ms ± 393 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [5]: %timeit hacky_way(a)
51.8 ms ± 250 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@Daniel F's post中的其他方法捕获了其他方法-

# @donkopotamus solution improved by @Daniel F
def b(M):
    return np.all(M == np.diag(np.diagonal(M)))

# @Daniel F's soln without assert check
def isDiag2(M):
    i, j = M.shape
    test = M.reshape(-1)[:-1].reshape(i-1, j+1)
    return ~np.any(test[:, 1:])

# @Make42's soln
def Make42(m):
    b = np.zeros(m.shape)
    np.fill_diagonal(b, m.diagonal())
    return np.all(m == b)

与以前的设置相同的计时-

In [6]: %timeit b(a)
   ...: %timeit Make42(a)
   ...: %timeit isDiag2(a)
218 ms ± 1.68 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
302 ms ± 1.25 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
67.1 ms ± 1.35 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

答案 2 :(得分:1)

不确定这与其他人相比有多快,但是:

def isDiag(M):
    i, j = np.nonzero(M)
    return np.all(i == j)

编辑让时间:

M = np.random.randint(0, 10, 1000) * np.eye(1000)

def a(M):  #donkopotamus solution
    return np.count_nonzero(M - np.diag(np.diagonal(M)))

%timeit a(M) 
100 loops, best of 3: 11.5 ms per loop

%timeit is_diagonal(M)
100 loops, best of 3: 10.4 ms per loop

%timeit isDiag(M)
100 loops, best of 3: 12.5 ms per loop

嗯,这可能比较慢,可能来自构建ij

让我们尝试通过删除减法步骤来改进@donkopotamus解决方案:

def b(M):
    return np.all(M == np.diag(np.diagonal(M)))

%timeit b(M)
100 loops, best of 3: 4.48 ms per loop

那好一点。

答案 3 :(得分:1)

我们实际上可以做得比Daniel F建议的要好得多

import numpy as np
import time

a = np.diag(np.random.random(19999))

t1 = time.time()
np.all(a == np.diag(np.diagonal(a)))
print(time.time()-t1)

t1 = time.time()
b = np.zeros(a.shape)
np.fill_diagonal(b, a.diagonal())
np.all(a == b)
print(time.time()-t1)

产生

2.5737204551696777
0.6501829624176025

一个技巧是np.diagonal(a)实际上使用a.diagonal(),因此我们直接使用那个。但是,b的快速构建以及对b的就地操作的成功使这一切锦上添花。

答案 4 :(得分:0)

我担心这是否是最有效的方法,但我的想法是屏蔽对角元素并检查所有其他元素是否为零。我想这足以将矩阵标记为对角矩阵。

因此我们创建一个与输入矩阵大小相同的虚拟数组,用1初始化。然后用零替换对角元素。现在我们执行输入矩阵和虚拟矩阵的元素乘法。所以这里我们用零替换输入矩阵的对角元素,并保留其他元素。

现在我们最后检查是否有非零元素。

def is_diagonal(matrix):
    #create a dummy matrix
    dummy_matrix = np.ones(matrix.shape, dtype=np.uint8)
    # Fill the diagonal of dummy matrix with 0.
    np.fill_diagonal(dummy_matrix, 0)

    return np.count_nonzero(np.multiply(dummy_matrix, matrix)) == 0

diagonal_matrix = np.array([[3, 0, 0],
                            [0, 7, 0],
                            [0, 0, 4]])
print is_diagonal(diagonal_matrix)
>>> True

random_matrix = np.array([[3, 8, 0],
                          [1, 7, 8],
                          [5, 0, 4]])
print is_diagonal(random_matrix)
>>> False

答案 5 :(得分:0)

我相信这是最简洁的方法:

np.allclose(np.diag(np.diag(a)), a)

答案 6 :(得分:0)

  

快速而肮脏的方式来获取真相。在合理的时间内工作

for i in range(0, len(matrix[0])): 
    for j in range(0, len(matrix[0])): 
        if ((i != j) and
         (matrix[i][j] != 0)) : 
            return False

return True

答案 7 :(得分:-3)

import numpy as np

is_diagonal = (np.trace(mat) == np.sum(mat))