how to vectorze this kind of loop for array - matlab

I'm newbie to Matlab. I have some questions.
How can I vectorise this loop:
epsilon = 0.45;
t = -3.033;
n = 100;
I = ones(n,n);
%// diagonal block 1
DB1 = gallery('tridiag',ones(1,n-1),ones(1,n),ones(1,n-1));
for k = 1:n
DB1(k,k) = epsilon;
end
for k = 1:n-1
DB1(k,k+1) = t*heaviside((-1)^(k+1));
end
for k = 2:n
DB1(k,k-1) = t*heaviside((-1)^k);
and this loop with LRG, R2, R1 are 3D arrays
for k = 2:N
LRG(:,:,k) = inv(R(:,:,k) - R2(:,:,k-1)*LRG(:,:,k-1)*R1(:,:,k-1));
end
Is there any way to handle the third dimension of an array (page) without writing (:,:,...) many times?

You don't need to call gallery: this will just initialize DB1 to a specific sparse tridiagonal matrix, which you manually overwrite afterwards. With n=100, I suspect that you don't actually want to work on sparse matrices. Here's the solution:
epsilon = 0.45; t = -3.033;
n = 100;
DB1 = zeros(n); %initialize to 0
%diagonal indices
inds=sub2ind([n n],1:n,1:n);
DB1(inds) = epsilon;
%superdiagonal
inds=sub2ind([n n],1:n-1,2:n);
DB1(inds) = t*heaviside((-1).^(2:n));
%subdiagonal
inds=sub2ind([n n],2:n,1:n-1);
DB1(inds) = t*heaviside((-1).^(2:n));
Here sub2ind lets you compute linear indices from your matrix indices, allowing you to access "subvectors" in your matrix. Watch out for the exponentialization in the heaviside: you need .^ instead of ^ to perform an element-wise operation using the vector 2:n.
If you insist on getting a sparse matrix, you could either convert this matrix to sparse by calling
DB1=sparse(DB1);
which is the easiest option if your matrix is small. If n was larger and you would really need sparse matrices, then you could set up the sparse matrix itself by
DB1new=sparse([1:n 1:n-1 2:n],[1:n 2:n 1:n-1],...
[epsilon*ones(1,n) t*heaviside((-1).^(2:n)) t*heaviside((-1).^(2:n))],n,n);
This call sets the elements of DB1new one-by-one according to its vector arguments: it takes the first index from the first input argument, the seconf index from the second, and the corresponding element from the third vector (see help sparse in MATLAB).
As for your second question: I'm not sure I understand correctly. Like in my example, sub2ind can be used to transform the multidimensional indices into linear ones, see help sub2ind. But personally, I think using the array notation is far more transparent and less prone to typos. And I think you can't vectorize that loop: you have to explicitly calculate the inverse matrix for each k.

Related

Vectorize function that finds an array of nearest values

I am still wrapping my head around vectorization and I'm having a difficult time trying to resolve the following function I made...
for i = 1:size(X, 1)
min_n = inf;
for j=1:K
val = X(i,:)' - centroids(j,:)';
diff = val'*val;
if (diff < min_n)
idx(i) = j;
min_n = diff;
end
end
end
X is an array of (x,y) coordinates...
2 5
5 6
...
...
centroids in this example is limited to 3 rows. It is also in (x,y) format as shown above.
For every pair in X I am computing the closest pair of centroids. I then store the index of the centroid in idx.
So idx(i) = j means that I am storing the index j of the centroid at index i, where i corresponds to the index of X. This means the closest centroid to pair X(i, :) is at idx(i).
Can I possibly simplify this via vectorization? I struggle with just vectorizing the inner loop.
Here are three options. But please note that the disadvantage of vectorization, as compared to your double loops, is that it stores all the difference operation results at once, which means that if your matrices have many rows, you might run out of memory. On the other hand, the vectorized approach is probably much faster.
Option 1
If you have access to Statistics and Machine Learning Toolbox, you can use the function pdist2 to get all the pairwise distances between rows of two matrices. Then, the min function gives you the minimum of each column of the result. Its first returned value are the minimal values, and its second are the indices, which is what you need for idx:
diff = pdist2(centroids,X);
[~,idx] = min(diff);
Option 2
If you don't have access to the toolbox, you can use bsxfun. This will let you compute the difference operation between the two matrices even if their dimensions don't agree. All you need to do is to use shiftdim to reshape X' to have size [1,size(X,2),size(X,1)], and then reshapedX and and centroids are compatible with their dimensions (see documentation of bsxfun). This lets you take the difference between their values. The result is a three dimensional array, which you need to sum along the second dimension to get the norm of the differences between rows. At this point you can proceed as in option 1.
reshapedX = shiftdim(X',-1);
diff = bsxfun(#minus,centroids,reshapedX);
diff = squeeze(sum(diff.^2,2));
[~,idx] = min(diff);
Note: Starting in the Matlab version 2016b, the bsxfun is used implicitly and you do not need to call it anymore. So the line with bsxfun can be replaced with the simpler line diff = centroids-reshapedX.
Option 3
Use the function dsearchn, which performs exactly what you need:
idx = dsearchn(centroids,X);
it could be done using pdist2 - pairwise distances between rows of two matrices:
% random data
X = rand(500,2);
centroids = rand(3,2);
% pairwise distances
D = pdist2(X,centroids);
% closest centroid index for each X coordinates
[~,idx] = min(D,[],2)
% plot
scatter(centroids(:,1),centroids(:,2),300,(1:size(centroids,1))','filled');
hold on;
scatter(X(:,1),X(:,2),30,idx);
legend('Centroids','data');

Extracting block diagonal from matrix

I have an njxnj matrix made up of nxn matrices. I want to extract the diagonal j blocks of nxn matrices. i.e. I want to extract the diagonal (for n = 2, j = 4):
What would be the most efficient way of doing this?
To index the elements you can use blkdiag to create a corresponding mask.
%your parameters
n=2
j=4
%some example matrix
M=magic(n*j);
%create the input for blkdiag, j matrices of size n
h=repmat({true(n)},j,1)
%use blkdiag to select the elements
M(logical(blkdiag(h{:})))
For large j, this answer of #Daniel becomes slow. I would instead recommend using linear indices of block diagonal.
n=2;
j=4;
%some example matrix
M=magic(n*j);
linIndices = (0:n*((n*j)+1):n*((n*j)+1)*(j-1))+reshape((1:n)'+n*j*(0:n-1),[],1);
newM = reshape(M(linIndices),n,n,[]);

Matlab code for generating a particular class of matrices

I need to generate all square matrices of order n with given properties.
Matrices are symmetric.
Entries are 0 and 1.
Diagonal elements are zeros.
I am using Matlab2012b. Can you help me with the code?
I was trying to write it down. It needs a long sequences of for loops. Any simpler technique?
Try this:
N = 4; %// matrix size
M = (N^2-N)/2; %// number of values to fill in each matrix
P = 2^M; %// number of matrices
x = dec2bin(0:P-1)-'0'; %// each row contains the values of a matrix, "packed" in a vector
result = NaN(N,N,P); %// preallocate
for k = 1:P
result(:,:,k) = squareform(x(k,:)); %// unpack values
end
The matrices are result(:,:,1), result(:,:,2) etc.

MATLAB accessing multiple elements in sparse matrix using row and column index vectors

I feel there should be an easy solution but I can't find it:
I have the sparse matrices A B with the same dimension n*n. I want to create matrix C which copies values in A where B is non-zero.
This is my approach:
[r,c,v] = find(B);
% now I'd like to create an array of values using indices r and c,
% but this doesn't work (wrong syntax)
v2 = A(r,c);
% This won't work either
idx = find(B); % linear indexing, too high-dimensional
v2 = A(idx);
% and create C
C = sparse(r,c,v2,n,n);
Here are some more details:
My matrices are very large, so the solution needs to be efficient. C(B~=0) = B(B~=0); won't do it, unfortunately.
Linear indexing won't work either as the matrices are too large (Matrix is too large to return linear indices.).
Is there really no way to use 2-dimensional indices?
Thanks for your help!
I think C = A .* (B~=0); should work. Only non-zeros will be accessed in the entrywise multiplication of two sparse matrices so it will be fast.

Extracting a banded matrix from a dense matrix in MATLAB

I have a large dense matrix, say matrix A of size 10000 by 10000 and I need to extract a banded matrix of bandwidth say 10 from it, i.e.,
B(i,j) = A(i,j) if |i-j| <=10
B(i,j) = 0 otherwise
What is the most efficient way to go about doing this in MATLAB?
I don't know that this is the most efficient way, but here is a way to create a matrix banded about the main diagonal via masking using the toeplitz() function:
r = zeros(1,size(A,2));
r(1 : ceil(bandwidth/2)) = 1;
bandedMask = toeplitz(r); %Create a banded toeplitz matrix of 1s and 0s
bandedMat = bandedMask.*A;
Note: This method assumes your bandwidth is odd.
As it is a huge matrix it might be a useful option not to copy it a second time to the memory. In that case
N = 10;
M = ...
for lin = 1:size(M,1)
M(lin, lin+N:end) = 0;
M(lin, 1:lin-N) = 0;
end
could be useful (depends whether you need the original matrix or not afterwards).
In the case you have to keep the original matrix you could think about representing your matrix diagonal by diagonal or as sparse matrix. In the case you have to copy the matrix you shouldn't touch all the elements that you don't need.
You should evaluate the different ways and tell us your results :-)
Suppose you have a matrix B and bandwidth n:
B = rand(16,7);
n = 4;
% Index main diagonal
szB = size(B);
idx = abs(bsxfun(#minus, (1:szB(1))',1:szB(2))) <= n;
% Build sparse
[r,c] = find(idx);
sparse(r,c,B(idx))