G. Loutie
G. Loutie

Reputation: 23

How can I do vectorization for this matlab "for loop"?

I have some matlab code as follow, constructing KNN similarity weight matrix.

[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
D = D < threshold;
W = zeros(n, n);
for i=1:size(I,2)
    W(I(:,i), i) = D(:,i);
    W(i, I(:,i)) = D(:,i)';
end

I want to vectorize the for loop. I have tried

W(I) = D;

but failed to get the correct value.

I add test case here:

n = 5;

D = [
 1     1     1     1     1
 0     1     1     1     1
 0     0     0     0     0
];

I = [
 1     2     3     4     5
 5     4     5     2     3
 3     1     1     1     1
];

Upvotes: 1

Views: 70

Answers (3)

rahnema1
rahnema1

Reputation: 15837

A possible solution:

idx1 = reshape(1:n*n,n,n).';
idx2 = bsxfun(@plus,I,0:n:n*size(I,2)-1);

W=zeros(n,n);
W(idx2) = D;
W(idx1(idx2)) = D;

Here assumed that you repeatedly want to compute D and I so compute idx only one time and use it repeatedly.

n = 5;
idx1 = reshape(1:n*n,n,n).';
%for k = 1 : 1000
    %[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
    %D = D < threshold;
    idx2 = bsxfun(@plus,I,0:n:n*size(I,2)-1);   
    W=zeros(n,n);
    W(idx2) = D;
    W(idx1(idx2)) = D;
%end

But if n isn't constant and it varies in each iteration it is better to change the way idx1 is computed:

n = 5;
%for k = 1 : 1000
    %n = randi([2 10]);%n isn't constant
    %[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
    %D = D < threshold;
    idx1 = bsxfun(@plus,(0:n:n^2-1).',1:size(I,2));
    idx2 = bsxfun(@plus,I,0:n:n*size(I,2)-1);   
    W=zeros(n,n);
    W(idx2) = D;
    W(idx1(idx2)) = D;
%end

Upvotes: 1

percusse
percusse

Reputation: 3106

You can cut some corners with linear indices but if your matrices are big then you should only take the nonzero components of D. Following copies all values of D

W = zeros(n);
W(reshape(sub2ind([n,n],I,[1;1;1]*[1:n]),1,[])) = reshape(D,1,[]);

Upvotes: 1

bidi
bidi

Reputation: 66

There are some undefined variables that makes it hard to check what it is doing, but this should do the same as your for loop:

D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
D = D < threshold;
W = zeros(n);

% set the diagonal values
W(sub2ind(size(X), I(1, :), I(1, :))) = D(1,:);
% set the other values
W(sub2ind(size(W), I(2, :), 1:size(I, 2))) = D(2, :);
W(sub2ind(size(W), 1:size(I, 2), I(2, :))) = D(2, :).';

I splited the directions, it works now with your test case.

Upvotes: 2

Related Questions