Extracting block diagonal from matrix - matlab

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,[]);

Related

how to vectorze this kind of loop for array

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.

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.

Random permutation matrix

Is there an easy way to simulate a random permutation matrix (say of size 1000 by 1000) in Matlab? I would like to study the eigenvalue distribution of independent sum of such matrices.
Thanks in advance!
You can generate a random permutation matrix like so:
Create a unity matrix:
A = eye( N ); %// N is the size of your matrix
For large values of N it is better to use sparse matrices:
A = speye( N ); % create sparse identity matrix
Generate a random permutation:
idx = randperm(1:N);
Use vector indexing to rearrange the rows accordingly
A = A(idx, :);
Voila!
In Matlab (used R2012a) idx = randperm(1:N) gives a warning that input should be scalar. So: idx = randperm(N); .

Multiply each column of a matrix by another matrix

I have a M x N matrix. I want to multiply each of the N columns by a M x M matrix. The following does this in a loop, but I have no idea how to vectorize it.
u=repmat(sin(2*pi*f*t),[n 1]);
W = rand(n);
answer = size(u);
for i=1:size(u,2)
answer(:,i) = W*u(:,i);
end
You simply need to multiply the two matrices:
answer = W*u;
Think about it: in every iteration of your loop you multiply a matrix by a vector. The result of that operation is a vector, which you save into your answer in column i. Matrix multiplication is a similar thing: you can understand it as multiplication of a matrix (W) by a set of vectors, which form your matrix u.
So your code is good, just remove the loop :)

How can I assign a value to the diagonals of a 4-D matrix using linear indexing in MATLAB?

I have a 4-D matrix A of size NxNxPxQ. How can I easily change the diagonal values to 1 for each NxN 2-D submatrix in a vectorized way?
Incorporating gnovice's suggestion, an easy way to index the elements is:
[N,~,P,Q]=size(A);%# get dimensions of your matrix
diagIndex=repmat(logical(eye(N)),[1 1 P Q]);%# get logical indices of the diagonals
A(diagIndex)=1;%# now index your matrix and set the diagonals to 1.
You can actually do this very simply by directly computing the linear indices for every diagonal element, then setting them to 1:
[N,N,P,Q] = size(A);
diagIndex = cumsum([1:(N+1):N^2; N^2.*ones(P*Q-1,N)]);
A(diagIndex) = 1;
The above example finds the N diagonal indices for the first N-by-N matrix (1:(N+1):N^2). Each subsequent N-by-N matrix (P*Q-1 of them) is offset by N^2 elements from the last, so a matrix of size PQ-1-by-N containing only the value N^2 is appended to the linear indices for the diagonal of the first matrix. When a cumulative sum is performed over each column using the function CUMSUM, the resulting matrix contains the linear indices for all diagonal elements of the 4-D matrix.
You can use direct indexing, and some faffing about with repmat, to add the indexes for a single 50x50 diagonal to the offsets within the larger matrix of each 50x50 block:
Here's an example for a smaller problem:
A = NaN(10,10,5,3);
inner = repmat(sub2ind([10 10], [1:10],[1:10]), 5*3, 10); % diagonals
outer = repmat([10*10 * [0:5*3-1]]', 1, 10*10); % offsets to blocks
diags = inner + outer;
A(diags(:)) = 1;