这是 FEM/FVM 方程系统的典型用例,因此可能具有更广泛的意义。从三角网格
我想创建一个scipy.sparse.csr_matrix。矩阵的行/列表示网格节点的值。矩阵在主对角线上以及两个节点通过边连接的地方都有条目。
scipy.sparse.csr_matrix
这是一个 MWE,它首先构建节点->边->单元关系,然后构建矩阵:
import numpy import meshzoo from scipy import sparse nx = 1600 ny = 1000 verts, cells = meshzoo.rectangle(0.0, 1.61, 0.0, 1.0, nx, ny) n = len(verts) nds = cells.T nodes_edge_cells = numpy.stack([nds[[1, 2]], nds[[2, 0]],nds[[0, 1]]], axis=1) # assign values to each edge (per cell) alpha = numpy.random.rand(3, len(cells)) vals = numpy.array([ [alpha**2, -alpha], [-alpha, alpha**2], ]) # Build I, J, V entries for COO matrix I = [] J = [] V = [] # V.append(vals[0][0]) V.append(vals[0][1]) V.append(vals[1][0]) V.append(vals[1][1]) # I.append(nodes_edge_cells[0]) I.append(nodes_edge_cells[0]) I.append(nodes_edge_cells[1]) I.append(nodes_edge_cells[1]) # J.append(nodes_edge_cells[0]) J.append(nodes_edge_cells[1]) J.append(nodes_edge_cells[0]) J.append(nodes_edge_cells[1]) # Create suitable data for coo_matrix I = numpy.concatenate(I).flat J = numpy.concatenate(J).flat V = numpy.concatenate(V).flat matrix = sparse.coo_matrix((V, (I, J)), shape=(n, n)) matrix = matrix.tocsr()
和
python -m cProfile -o profile.prof main.py snakeviz profile.prof
可以创建并查看上述内容的个人资料:
该方法tocsr()在这里占用了大部分的运行时间,但在构建更复杂时也是如此alpha。因此,我正在寻找加快速度的方法。
tocsr()
alpha
我已经发现了:
py V.append(vals[0, 0, 0] + vals[1, 1, 2]) I.append(nodes_edge_cells[0, 0]) # == nodes_edge_cells[1, 2] J.append(nodes_edge_cells[0, 0]) # == nodes_edge_cells[1, 2]
这使得I、J更V短,从而加快tocsr。
I
J
V
tocsr
numpy.unique
I``J``V
我还有一个想法,如果存在一个类似 的数据结构,其中主对角线被单独保存,那么我可以用一个简单的 来替换对角线,。V我知道这在其他一些软件包中存在,但在 scipy 中找不到它。对吗?I``J``numpy.add.at``csr_matrix
I``J``numpy.add.at``csr_matrix
也许有一种合理的方法来直接构建 CSR?
因此,最终这被证明是 COO 和 CSR 之间的区别sum_duplicates(正如 @hpaulj 所怀疑的那样)。感谢所有参与人员(尤其是 @paul-panzer)的努力,PR正在进行中,以tocsr大大加快速度。
sum_duplicates
SciPytocsr确实做到了lexsort,(I, J)因此它有助于以某种方式组织索引,从而(I, J)使结果公平排序。
lexsort
(I, J)
对于nx=4,ny=2在上面的例子中,I和J是
nx=4
ny=2
[1 6 3 5 2 7 5 5 7 4 5 6 0 2 2 0 1 2 1 6 3 5 2 7 5 5 7 4 5 6 0 2 2 0 1 2 5 5 7 4 5 6 0 2 2 0 1 2 1 6 3 5 2 7 5 5 7 4 5 6 0 2 2 0 1 2 1 6 3 5 2 7] [1 6 3 5 2 7 5 5 7 4 5 6 0 2 2 0 1 2 5 5 7 4 5 6 0 2 2 0 1 2 1 6 3 5 2 7 1 6 3 5 2 7 5 5 7 4 5 6 0 2 2 0 1 2 5 5 7 4 5 6 0 2 2 0 1 2 1 6 3 5 2 7]
首先对每一行进行排序cells,然后按第一列对行进行排序,例如
cells
cells = numpy.sort(cells, axis=1) cells = cells[cells[:, 0].argsort()]
生产
[1 4 2 5 3 6 5 5 5 6 7 7 0 0 1 2 2 2 1 4 2 5 3 6 5 5 5 6 7 7 0 0 1 2 2 2 5 5 5 6 7 7 0 0 1 2 2 2 1 4 2 5 3 6 5 5 5 6 7 7 0 0 1 2 2 2 1 4 2 5 3 6] [1 4 2 5 3 6 5 5 5 6 7 7 0 0 1 2 2 2 5 5 5 6 7 7 0 0 1 2 2 2 1 4 2 5 3 6 1 4 2 5 3 6 5 5 5 6 7 7 0 0 1 2 2 2 5 5 5 6 7 7 0 0 1 2 2 2 1 4 2 5 3 6]
对于原始帖子中的数字,排序将运行时间从大约 40 秒缩短到 8 秒。
如果一开始就对节点进行更适当的编号,也许可以实现更好的排序。