在 Matlab 中优化 Voronoi 图的唯一边的计算

Optimising the calculation of unique edges of a Voronoi diagram in Matlab

我正在执行一些计算,我需要评估以某些节点为中心的 Voronoi 多边形之间的通量。为此,我需要找到多边形对之间的公共边,例如。 V1&V2如下图所示。每条边应仅评估一次。

为此,我获取节点的 x 和 y 坐标并执行 Delaunay triangulation 以查找相邻节点。然后我 运行 一个循环来找出哪些节点具有公共顶点。然后我计算 Voronoi 多边形并创建一个数组 (istr),索引为 'edge' 数字,值是中央 voronoi 多边形。 'neigh' 数组然后将索引作为 'edge' 数字和所有相邻的多边形值。在检查以确保我不会对每条边重复此评估之后,我然后计算边(即每个多边形之间共享的顶点)。

我可以使用下面的代码计算边缘,但是在计算 nodneigh 的 for... 循环中存在巨大瓶颈,因为元胞数组的组件需要迭代访问。占用更多时间的是 edge 的计算,使用单元函数访问 Delaunay triangulation/Voronoi 多边形的输出。

我的问题是如何加速这两个瓶颈?虽然我很欣赏 Matlab 中元胞数组的灵活性,但我觉得当我不需要它时它真的会减慢一切。我尝试用 NaN 填充元胞数组,将其转换为矩阵并执行逐行相交,但这并不是那么成功:arrayfun 需要很多很多时间,而且我似乎无法使用与GPU计算相交。

% Create dummy data
nstr    = 1000;         % number of particles
x       = rand(nstr,1);   % particle x coordinates
y       = rand(nstr,1);   % particle y coordinates

% Delaunay triangulation
DT      = delaunayTriangulation(x,y);

% Determine node neighbors of the original nodes
nodneigh    = cell(nstr,1);
numtotneigh = 0;                        % initialise total # of neighbors
bla         = DT.vertexAttachments;     % Get the particle/triangle IDs

% BOTTLENECK 1: Find out which particles/triangles are neighbours
for istr = 1:nstr
    nodneigh{istr} = setdiff(unique(DT.ConnectivityList(bla{istr},:)),istr);
    numtotneigh = numtotneigh+length(nodneigh{istr});
end

% Construct Thiessen polygons by Voronoi tessalation
[voro_V,voro_R] = DT.voronoiDiagram;

% Bookkeeping - create an index of edges with associated voronoi regions
cellsz = cellfun(@size,nodneigh,'uni',false);
cellsz = cell2mat(cellsz);
cellsz = cellsz(:,1);
temp = [1:nstr];
idx([cumsum([1 cellsz(cellsz>0)'])]) = 1;

istr    = temp(cumsum(idx(1:find(idx,1,'last')-1)))'; % Region number
neigh   = vertcat(nodneigh{:});                       % Region neighbours 
neigh_m = mod(neigh,nstr);   

% Make sure neighbourship has not already been evaluated
idx             = neigh_m == 0;
neigh_m(idx,:)  = nstr;

neigh    = vertcat(nodneigh{:});  

% BOTTLENECK 2:
% Determine which edges are common to both central and neighbour regions
edge     = cellfun(@intersect,voro_R(istr),voro_R(neigh),...
                'UniformOutput',false);
edge     = cell2mat(edge);

您可以遍历边缘并计算从边缘中点到所有站点的距离。然后按升序对距离进行排序,对于内部 voronoi 多边形,选择第一个和第二个。对于外部多边形,选择第一个。基本上是一条边 separate/divide 2 个多边形。

它应该更快(瓶颈 #2)。您不需要为 voronoi 图计算(所有)三角剖分的邻居。当您遍历所有三角形边并检查重影(双边)(瓶颈 #1)时,它会起作用。

好的,终于得到了令我满意的东西。利用matlab中DelaunayTriangulation class I could resolve the first bottleneck. I think there is a slight difference in the calculations now, but I am guessing they are now a bit more rigorous. I have managed to speed up the second bottleneck by moving the cell array to a matrix and then looping through this. Instead of intersect I am using ismember, partly because I found this helpful post about how to improve performance with intersect and setdiffedges方法。

 % Create dummy data
nstr    = 1000;         % number of particles
x       = rand(nstr,1);   % particle x coordinates
y       = rand(nstr,1);   % particle y coordinates

% Delaunay triangulation
DT      = delaunayTriangulation(x,y);

 % construct Thiessen polygons by Voronoi tessalation
[voro_V,voro_R] = DT.voronoiDiagram;
dt_ed = DT.edges;
istr = dt_ed(dt_ed(:,1)<=nstr,1);
neigh = dt_ed(dt_ed(:,1)<=nstr,2);

% Determine cross-sectional area and Dtrans of all nodes                  
neigh_m = mod(neigh,nstr);                    

% if neigh_m == 0, neigh_m = nstr; end
% if the index of the neighboring streamline is smaller than
% that of the current streamline, the relationship has already
% been evaluated
idx             = neigh_m == 0;
neigh_m(idx,:)  = nstr;

temp_is = nan(numel(istr),40);
temp_ne = nan(numel(istr),40);
edge = nan(numel(istr),2);
for index = 1:numel(istr)
   temp_len1     = length(voro_R{istr(index)});
   temp_len2     = length(voro_R{neigh(index)});
   temp_is(index,1:temp_len1) = voro_R{istr(index)};
   temp_ne(index,1:temp_len2) = voro_R{neigh(index)};
   edge(index,:) = temp_is(index,ismember(temp_is(index,:),temp_ne(index,:)));
end

这些更改使我的代码 运行 比原来的代码快 3-4 倍。如果有人有任何更好的想法(也许基于 GPU 的东西会有所帮助?)我愿意接受更好的答案。