在numpy中将n个子矩阵编译成NxN矩阵

时间:2016-05-04 23:42:19

标签: numpy matrix anaconda

处理矩阵结构分析中的问题。我正在用Python编写程序(使用Anaconda 3)来分析桁架。每个单独的桁架构件生成一个4×4矩阵,总共n个4×4矩阵。然后,对于矩阵A,B,C,这些4x4矩阵被编译成NxN矩阵,排列如下:

enter image description here

正如您所看到的,每个连续的子矩阵在前一个子行上方放置一行,向下放置一行。此外,由于桁架的尺寸和桁架节点(节点)的数量由用户指定,因此必须动态确定NxN矩阵的大小(子矩阵总是4x4)。

我有一个NxN零矩阵;我想弄清楚如何正确编译子矩阵。

我发现了一些类似的问题,但没有一个问题动态地扩展了较大的矩阵。

感谢大家提供的任何帮助。

2 个答案:

答案 0 :(得分:2)

n可能是大的,所以结果是一个大的稀疏矩阵,非零值集中在对角线上?稀疏矩阵在设计时考虑了这种矩阵(来自FD和FE PDE问题)。我在MATLAB中做了很多,有些用scipy稀疏模块。

该模块具有可能有效的块定义模式,但我更熟悉的是coocsr路由。

coo格式中,非零元素由3个向量ijdata定义。您可以收集这些数组中AB等的所有值(对B等中的值应用适当的偏移量),而不必担心重叠。然后,当该格式转换为csr时(对于矩阵计算),重叠值将相加 - 这正是您想要的。

我认为sparse文档有一些简单的例子。从概念上讲,最简单的事情是迭代n子矩阵,并收集这3个数组中的值。但我也制定了一个更复杂的系统,它可以作为一个大型阵列操作完成,或者通过迭代更小的维度来完成。例如,每个子矩阵具有16个值。在现实情况下,16将比n小得多。

我可以使用代码来提供更具体的示例。

==========================

这是一个包含3个块的简单示例 - 功能性,但不是最有效的

定义3个街区:

In [620]: A=np.ones((4,4),int)    
In [621]: B=np.ones((4,4),int)*2
In [622]: C=np.ones((4,4),int)*3

列出要收集的值;可以是数组,但是附加或扩展列表很容易,也相对有效:

In [623]: i, j, dat = [], [], []

In [629]: def foo(A,n):
   # turn A into a sparse, and add it's points to the arrays
   # with an offset of 'n'
   ac = sparse.coo_matrix(A)
   i.extend(ac.row+n)
   j.extend(ac.col+n)
   dat.extend(ac.data)


In [630]: foo(A,0)

In [631]: i
Out[631]: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]    
In [632]: j
Out[632]: [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]

In [633]: foo(B,1)
In [634]: foo(C,2)  # do this in a loop in the real world

In [636]: M = sparse.csr_matrix((dat,(i,j)))

In [637]: M
Out[637]: 
<6x6 sparse matrix of type '<class 'numpy.int32'>'
    with 30 stored elements in Compressed Sparse Row format>

In [638]: M.A
Out[638]: 
array([[1, 1, 1, 1, 0, 0],
       [1, 3, 3, 3, 2, 0],
       [1, 3, 6, 6, 5, 3],
       [1, 3, 6, 6, 5, 3],
       [0, 2, 5, 5, 5, 3],
       [0, 0, 3, 3, 3, 3]], dtype=int32)

如果我做得对,A,B,C的重叠值相加。

更一般地说:

In [21]: def foo1(mats):
      i,j,dat = [],[],[]
      for n,mat in enumerate(mats):
          A = sparse.coo_matrix(mat)
          i.extend(A.row+n)
          j.extend(A.col+n)
          dat.extend(A.data)
      M = sparse.csr_matrix((dat,(i,j)))
      return M
   ....:   

In [22]: foo1((A,B,C,B,A)).A
Out[22]: 
array([[1, 1, 1, 1, 0, 0, 0, 0],
       [1, 3, 3, 3, 2, 0, 0, 0],
       [1, 3, 6, 6, 5, 3, 0, 0],
       [1, 3, 6, 8, 7, 5, 2, 0],
       [0, 2, 5, 7, 8, 6, 3, 1],
       [0, 0, 3, 5, 6, 6, 3, 1],
       [0, 0, 0, 2, 3, 3, 3, 1],
       [0, 0, 0, 0, 1, 1, 1, 1]], dtype=int32)

提出一种更有效地执行此操作的方法可能取决于如何生成各个子矩阵。如果它们是迭代创建的,那么你也可以迭代地收集i,j数据值。

==========================

由于子矩阵很密集,我们可以直接获得适当的i,j,data值,而无需通过coo中介。如果将A,B,C收集到一个更大的数组中,则不进行循环。

如果我修改foo1以返回coo矩阵,我会看到给定的i,j,data列表(作为数组),而不是重复项的总和。在具有5个矩阵的示例中,我得到80个元素数组,可以将其重新整形为

In [110]: f.col.reshape(-1,16)
Out[110]: 
array([[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
       [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4],
       [2, 3, 4, 5, 2, 3, 4, 5, 2, 3, 4, 5, 2, 3, 4, 5],
       [3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 5, 6],
       [4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7]], dtype=int32)

In [111]: f.row.reshape(-1,16)
Out[111]: 
array([[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3],
       [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4],
       [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5],
       [3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6],
       [4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7]], dtype=int32)

In [112]: f.data.reshape(-1,16)
Out[112]: 
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
       [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
       [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
       [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])

我应该能够生成没有循环的那些,尤其是rowcol

In [143]: mats=[A,B,C,B,A]

数组元素的坐标

In [144]: I,J=[i.ravel() for i in np.mgrid[range(A.shape[0]),range(A.shape[1])]] 

通过广播

复制它们
In [145]: x=np.arange(len(mats))[:,None]
In [146]: I=I+x    
In [147]: J=J+x

将数据收集到一个大型数组中:

In [148]: D=np.concatenate(mats,axis=0)

In [149]: f=sparse.csr_matrix((D.ravel(),(I.ravel(),J.ravel())))

或作为紧凑的功能

def foo3(mats):
    A = mats[0]
    n,m = A.shape
    I,J = np.mgrid[range(n), range(m)]
    x = np.arange(len(mats))[:,None]
    I = I.ravel()+x
    J = J.ravel()+x
    D=np.concatenate(mats,axis=0)
    f=sparse.csr_matrix((D.ravel(),(I.ravel(),J.ravel())))
    return f

在这个温和的例子中,第二个版本快了2倍;第一个与列表的长度成线性比例;第二个几乎与它的长度无关。

In [158]: timeit foo1(mats)
1000 loops, best of 3: 1.3 ms per loop

In [160]: timeit foo3(mats)
1000 loops, best of 3: 653 µs per loop

答案 1 :(得分:0)

简单的for - 循环方式是将每个4x4矩阵添加到大零矩阵的适当切片中:

for i, small_m in enumerate(small_matrices):
    big_m[i:i+4, i:i+4] += small_m

通过创建零矩阵的跨步视图并使用np.add.at进行无缓冲添加,您也可以在没有Python循环的情况下执行此操作。如果你的4x4矩阵被打包成一个4乘4乘的阵列,这应该特别有效:

import numpy as np
from numpy.lib.stride_tricks import as_strided

# Create a view of big_m with the shape of small_matrices.
# strided_view[i] is a view of big_m[i:i+4, i:i+4]
strides = (sum(big_m.strides),) + big_m.strides
strided_view = as_strided(big_m, shape=small_matrices.shape, strides=strides)

np.add.at(strided_view, np.arange(small_matrices.shape[0]), small_matrices)