在 MATLAB 中构造 3D 点阵图

Construct 3D lattice graph in MATLAB

我对这个 question/answer () 的扩展很感兴趣,以将 4-connected 案例扩展到三维。

问题1:给定一个X x Y x Z维矩阵,如何构造6维邻接矩阵(或边表)?

问题 2:给定邻接矩阵(或边列表),如何将边权重计算为连接节点值的某个函数(例如,平均值)?

问题 3:如何在不 运行 内存不足的情况下解决非常大的矩阵的问题 1 和 2?稀疏矩阵或边列表似乎是可能的解决方案,但如何实施呢?

% simple case
X = 2; Y = 2; Z = 2;
mat = reshape([1:(X*Y*Z)],X,Y,Z);
>> mat

mat(:,:,1) =

     1     3
     2     4


mat(:,:,2) =

     5     7
     6     8


% large case
X = 256; Y = 256; Z = 1000;
mat = reshape([1:(X*Y*Z)],X,Y,Z);

答案 1: 而不是构建一个巨大的边和权重矩阵...直接在循环中使用边列表迭代构建图形,遍历每个维度(行、列、平面)。这个 64x64x10 的速度非常快(秒),但对于大型示例 (256x256x1000) 运行时间要长得多。如果能开发出更快的版本就好了。

X=256;Y=256;Z=1000;
% input matrix of 0-1 values 
mat = rand(X,Y,Z);
% matrix of indices for input matrix coordinates
imat = reshape([1:(X*Y*Z)],X,Y,Z);

% initialize empty graph
g = graph();

xdim = size(imat,1);
ydim = size(imat,2);
zdim = size(imat,3);

% create edge list with weights
disp('calculating start/end nodes for edges and associated weights...')

for y = 1:(ydim-1)  % loop through all "horizontal" (column) connections in each z plane
    % example:
    % 1 - 3
    % 2 - 4
    for z = 1:zdim
        nodes1 = imat(:,y,z);
        nodes2 = imat(:,y+1,z); % grab column y+1 nodes of plane z
        wts = ((1-mat(:,y,z)) + (1-mat(:,y+1,z))) ./ 2;  % average of 1-value for each node
        g = addedge(g,nodes1,nodes2,wts); % add to graph
    end
end

for x = 1:(xdim-1)  % loop through all "vertical" (row) connections within each z plane
    % example:
    % 1  3
    % |  |
    % 2  4

    for z = 1:zdim
        nodes1 = imat(x,:,z);
        nodes2 = imat(x+1,:,z); % grab row x+1 nodes of plane z
        wts = ((1-mat(x,:,z)) + (1-mat(x+1,:,z))) ./ 2;  % average of 1-value for each node
        g = addedge(g,nodes1,nodes2,wts); % add to graph
    end
end

for z = 1:(zdim-1)  % loop through all "deep" connections across z planes
    % example:
    %   5  7
    %  /  /
    % 1  3

    for x = 1:xdim
        nodes1 = imat(x,:,z);
        nodes2 = imat(x,:,z+1); % grab row x nodes of plane z+1
        wts = ((1-mat(x,:,z)) + (1-mat(x,:,z+1))) ./ 2;  % average of 1-value for each node
        g = addedge(g,nodes1,nodes2,wts); % add to graph
    end
end
disp('done.')

figure
spy(adjacency(g))

答案 2: 将链接答案的 4-connected case 扩展到三维 ():

disp("calculating adjacency matrix...")

X=3; Y=3; Z=2;
mat = reshape([1:(X*Y*Z)],X,Y,Z);
[x, y, z] = size(mat);                                     % Get the matrix size
diagVec1 = repmat(repmat([ones(y-1, 1); 0], x, 1), z, 1);  % Make the first diagonal vector 
                                                           % (for y-planar connections)
diagVec1 = diagVec1(1:end-1);                              % Remove the last value

diagVec2 = repmat([ones(y*(x-1), 1); zeros(x,1)], z, 1);   % Make the second diagonal vector 
                                                           % (for x-planar connections)
diagVec2 = diagVec2(1:end-x);                              % drop the last x values (zeros)

diagVec3 = ones(x*y*(z-1), 1);                             % make the third diagonal vector 
                                                           % (for z-planar connections)

adj = diag(diagVec1, 1)+diag(diagVec2, y)+diag(diagVec3, x*y);   % Add the diagonals to a zero matrix
adj = adj+adj.';                                           % Add the matrix to a transposed copy of
                                                           %   itself to make it symmetric

disp("done.")

figure
spy(adj)

对于大型案例,Matlab 报错:

Error using diag
Requested 65536000x65536000 (32000000.0GB) array exceeds maximum array size preference. Creation of
arrays greater than this limit may take a long time and cause MATLAB to become unresponsive.

Error in blah (line X)
adj = diag(diagVec1, 1) + diag(diagVec2, y) + diag(diagVec3, x*y);   % Add the diagonals to a zero matrix

更新

答案3:
(@beaker 回答的更新版本)

我很好奇是否可以通过使用 26 连接情况(所有 9 个线性或对角线相邻点在上平面中,9 个在下平面中,8 个在重合平面中)来获得改进的最短路径结果.

% 3x3x3 'cube'
X=3;Y=3;Z=3;

% input matrix of 0-1 values 
M_wts = rand(X,Y,Z);
M_wts = 1-M_wts;

% matrix of indices for input matrix coordinates
M = reshape([1:(X*Y*Z)],X,Y,Z);

% initialize empty graph
g = graph();

%% Given a 3d matrix M, generate a graph representing
% the 6-connected neighbors.
% Node numbers are equal to node indices in M.
% Edge weights are given by mean of adjacent node values.
[x, y, z] = size(M);

% Initialize Source and Target node vectors and corresponding Weight
S = []; 
T = [];

%% 6-connected case: 
% List neighboring nodes where S(i) < T(i)
% Neighbors along dimension 1 (x/lateral)
[X1, X2, X3] = ndgrid(1:x-1, 1:y, 1:z);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 1:y, 1:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 2 (y/vertical)
[X1, X2, X3] = ndgrid(1:x, 1:y-1, 1:z);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x, 2:y, 1:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (z/planar)
[X1, X2, X3] = ndgrid(1:x, 1:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x, 1:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

%% 
% Calculate the weight for each edge 
W = mean(M_wts([S, T]), 2);

g = graph(S, T, W);
gplot = plot(g,'EdgeLabel',g.Edges.Weight);
layout(gplot,'force3')
view(3)

%% diagonal connections
% Neighbors within dimension 3 (diagonal xy1)
[X1, X2, X3] = ndgrid(1:x-1, 1:y-1, 1:z);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 2:y, 1:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors within dimension 3 (diagonal xy2)
[X1, X2, X3] = ndgrid(1:x-1, 2:y, 1:z);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 1:y-1, 1:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];


%% across-plane diagonal connections
% Neighbors along dimension 3 (across-plane diagonal xz1)
[X1, X2, X3] = ndgrid(1:x, 1:y-1, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x, 2:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal xz2)
[X1, X2, X3] = ndgrid(1:x, 2:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x, 1:y-1, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal yz1)
[X1, X2, X3] = ndgrid(1:x-1, 1:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 1:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal yz2)
[X1, X2, X3] = ndgrid(2:x, 1:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x-1, 1:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal xyz1)
[X1, X2, X3] = ndgrid(1:x-1, 1:y-1, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 2:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal xyz2)
[X1, X2, X3] = ndgrid(2:x, 1:y-1, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x-1, 2:y, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal xyz3)
[X1, X2, X3] = ndgrid(1:x-1, 2:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:x, 1:y-1, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3 (across-plane diagonal xyz4)
[X1, X2, X3] = ndgrid(2:x, 2:y, 1:z-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:x-1, 1:y-1, 2:z);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];


%% 3D plot it
% Calculate the weight for each edge 
W = mean(M_wts([S, T]), 2);

g = graph(S, T, W);
gplot = plot(g,'EdgeLabel',g.Edges.Weight);
layout(gplot,'force3')
view(3)

此方法使用 ndgrid 在一个方向上查找每个节点的邻居。例如,在维度 2 中,我们找到每个节点右侧的(有效)邻居。

这里,蓝色节点是边的源,右边的红色节点是目标节点。因此源节点和目标节点的网格彼此偏移 1。

% Given a 3d matrix M, generate a graph representing
% the 6-connected neighbors.
% Node numbers are equal to node indices in M.
% Edge weights are given by mean of adjacent node values.
[m, n, p] = size(M);

% Initialize Source and Target node vectors and corresponding Weight
S = T = W = [];

% List neighboring nodes where S(i) < T(i)
% Neighbors along dimension 1
[X1, X2, X3] = ndgrid(1:m-1, 1:n, 1:p);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(2:m, 1:n, 1:p);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 2
[X1, X2, X3] = ndgrid(1:m, 1:n-1, 1:p);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:m, 2:n, 1:p);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Neighbors along dimension 3
[X1, X2, X3] = ndgrid(1:m, 1:n, 1:p-1);
S = [S; sub2ind(size(M), X1(:), X2(:), X3(:))];

[X1, X2, X3] = ndgrid(1:m, 1:n, 2:p);
T = [T; sub2ind(size(M), X1(:), X2(:), X3(:))];

% Calculate the weight for each edge 
W = mean(M([S, T]), 2);

%% Adjacency matrix A is for an undirected graph
%%    therefore only edges where S(i) < T(i) are listed.
%%    If the backward edges are also necessary:
% tempS = S;
% S = [S;T];
% T = [T;tempS];
% W = [W;W];

% [S T W] gives the graph's edge list.
% If the sparse adjacency matrix is required:
A = sparse(S, T, W);

运行 在 Octave 中使用您的大型测试用例:

X = 256; Y = 256; Z = 1000;
M = reshape([1:(X*Y*Z)],X,Y,Z);

(无向)边列表大约需要 45 秒,另外还需要 20 秒左右来生成稀疏邻接矩阵。由于我使用的是 Octave,因此我无法测试哪个生成图表的速度更快。