Extended block diagonal matrix in Matlab - matlab

I know that to generate a block-diagonal matrix in Matlab the command blkdiag generates such a matrix:
Now I am faced with generating the same block-diagonal matrix, but with also matrix elements B_1, B_2,..., B_{n-1} on the upper diagonal, zeros elsewhere:
I guess this can be hardcoded with loops, but I would like to find a more elegant solution. Any ideas on how to implement such a thing?
P.S. I diag command, that using diag(A,k) returns the kth diagonal. I need something for writing in the matrix, for k>0, and for block matrices, not only elements.

There is a submission on the File Exchange that can do this:
(Block) tri-diagonal matrices.
You provide the function with three 3D-arrays, each layer of the 3D array represents a block of the main, sub- or superdiagonal. (Which means that the blocks will have to be of the same size.) The result will be a sparse matrix, so it should be rather efficient in terms of memory.
An example usage would be:
As = bsxfun(#times,ones(3),permute(1:3,[3,1,2]));
Bs = bsxfun(#times,ones(3),permute(10:11,[3,1,2]));
M = blktridiag(As, zeros(size(Bs)), Bs);
where full(M) gives you:
1 1 1 10 10 10 0 0 0
1 1 1 10 10 10 0 0 0
1 1 1 10 10 10 0 0 0
0 0 0 2 2 2 11 11 11
0 0 0 2 2 2 11 11 11
0 0 0 2 2 2 11 11 11
0 0 0 0 0 0 3 3 3
0 0 0 0 0 0 3 3 3
0 0 0 0 0 0 3 3 3

This could be one approach based on kron, tril & triu -
%// Take all A1, A2, A3, etc in a cell array for easy access and same for B
A = {A1,A2,A3,A4}
B = {B1,B2,B3}
%// Setup output array with the A blocks at main diagonal
out = blkdiag(A{:})
%// logical array with 1s at places where kth diagonal elements are to be put
idx = kron(triu(true(numel(A)),k) & tril(true(numel(A)),k),ones(size(A{1})))>0
%// Put kth diagonal blocks using the logical mask
out(idx) = [B{1:numel(A)-k}]
Sample run with k = 1 for 2 x 2 sizes matrices -
>> A{:}
ans =
0.3467 0.7966
0.6228 0.7459
ans =
0.1255 0.0252
0.8224 0.4144
ans =
0.7314 0.3673
0.7814 0.7449
ans =
0.8923 0.1296
0.2426 0.2251
>> B{:}
ans =
0.3500 0.9275
0.2871 0.0513
ans =
0.5927 0.8384
0.1629 0.1676
ans =
0.5022 0.3554
0.9993 0.0471
>> out
out =
0.3467 0.7966 0.3500 0.9275 0 0 0 0
0.6228 0.7459 0.2871 0.0513 0 0 0 0
0 0 0.1255 0.0252 0.5927 0.8384 0 0
0 0 0.8224 0.4144 0.1629 0.1676 0 0
0 0 0 0 0.7314 0.3673 0.5022 0.3554
0 0 0 0 0.7814 0.7449 0.9993 0.0471
0 0 0 0 0 0 0.8923 0.1296
0 0 0 0 0 0 0.2426 0.2251

Related

Matlab Matrix indexing with both column and row

Suppose I have a 2 by 5 matrix:
d1= 3 3 1 1 2
4 4 2 3 4
and there is a 4 by 5 zero matrix :
z1= 0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
Each column of d1 shows the positions of 1's in the corresponding column in z1. Specifically, I want to get a result like:
r1= 0 0 1 1 0
0 0 1 0 1
1 1 0 1 0
1 1 0 0 1
I am looking for an efficient way to obtain r1 from d1 and z1.
Convert d1 to linear indices, and use them to index z1:
% Prior to R2016b:
I = bsxfun(#plus, d1, (0:size(d1,2)-1) * size(z1,1));
% On or after R2016b:
I = d1 + (0:size(d1,2)-1) * size(z1,1));
% Index using the linearized indices
z1(I) = 1

Loopless submatrix assignment in Matlab

I have a matrix F of size D-by-N and a vector A of length N of random integers in the range [1,a]. I want to create a matrix M of size D * a such that each colum M(:,i) has the vector F(:,i) starting from the index (A(i)-1)*D+1 to (A(i)-1)*D+D.
Example:
F = [1 2 3 10
4 5 6 22]
A = [3 2 1 2]
a = 4
M = [0 0 3 0
0 0 6 0
0 2 0 10
0 5 0 22
1 0 0 0
4 0 0 0
0 0 0 0
0 0 0 0]
I can do it with a simple loop
for i = 1 : N
M((A(i)-1)*D+1:(A(i)-1)*D+D,i) = F(:,i);
end
but for large N this might take a while. I am looking for a way to do it without loop.
You can use bsxfun for a linear-indexing based approach -
[D,N] = size(F); %// Get size of F
start_idx = (A-1)*D+1 + [0:N-1]*D*a; %// column start linear indices
all_idx = bsxfun(#plus,start_idx,[0:D-1]'); %//'# all linear indices
out = zeros(D*a,N); %// Initialize output array with zeros
out(all_idx) = F; %// Insert values from F into output array
Sample run -
F =
1 2 3 10
4 5 6 22
A =
3 2 1 2
a =
4
out =
0 0 3 0
0 0 6 0
0 2 0 10
0 5 0 22
1 0 0 0
4 0 0 0
0 0 0 0
0 0 0 0

Matlab: How to insert zeros in specific places of vector

Can someone help me with the following problem in Matlab? I have a first vector containing the elements values. For example,
[2 8 4 9 3].
And a second one with the desired places in a second vector. For example,
[0 0 1 0 0 0 0 1 1 0 0 1 0 0 1].
Now I want to put the values from the first vector on the positions of the second one to end up with
[0 0 2 0 0 0 0 8 4 0 0 9 0 0 3].
What is the most efficient way of doing this when the size of the vector can be very large. (then thousands of elements)?
You can consider the y values as logical indicators, then use logical indexing to set those values to the values in x.
x = [2 8 4 9 3];
y = [0 0 1 0 0 0 0 1 1 0 0 1 0 0 1];
y(logical(y)) = x;
Alternatively, you could use
y(y==1) = x;
Use self-indexing:
% Your values:
V = [2 8 4 9 3];
% The desired locations of these values:
inds = [0 0 1 0 0 0 0 1 1 0 0 1 0 0 1];
% index the indices and assign
inds(inds>0) = V

How to vectorize matrix subsetting such that find() returns a matrix?

Given a matrix A, I need to find the indices corresponding to the values 1 and 2. I could do this sequentially as follows:
>> B
B =
1 2 3
4 1 6
7 8 9
4 5 1
>> find(B==1)
ans =
1
6
12
>> find(B==2)
ans =
5
But if I do this kind of operation in a loop, Matlab will only use one core of my processor. How can I vectorise it, so that I obtain a matrix from find? I want this result:
>> my_find( B, [1 2] )
ans =
1 5
6 0
12 0
(or some other padding)
How can I obtain this?
Just don't use find
B==1
ans =
1 0 0
0 1 0
0 0 0
0 0 1
B==2
ans =
0 1 0
0 0 0
0 0 0
0 0 0
And then add or logical OR those together.
i.e.
(B==1) + (B==2)
ans =
1 1 0
0 1 0
0 0 0
0 0 1
or
(B==1) | (B==2)
ans =
1 1 0
0 1 0
0 0 0
0 0 1
[i, j] = ind2sub(size(B), find(logical(sum(bsxfun(#eq, B(:), [1 2]), 2))))

Computing linear indices and counting non-zero entries of large sparse matrices in row-first order

Say I have a sparse non-rectangular matrix A:
>> A = round(rand(4,5))
A =
0 1 0 1 1
0 1 0 0 1
0 0 0 0 1
0 1 1 0 0
I would like to obtain the matrix B where the non-zero entries of A are replaced by their linear index in row-first order:
B =
0 2 0 4 5
0 7 0 0 10
0 0 0 0 15
0 17 18 0 0
and the matrix C that where the non-zero entries of A are replaced by the order in which they are found in a row-first search:
C =
0 1 0 2 3
0 4 0 0 5
0 0 0 0 6
0 7 8 0 0
I am looking for vectorized solutions for this problem that scale to large sparse matrices.
If I understand what you are asking, a couple of tranpositions should do the trick. The key is that find(A.') will do "row-first" indexing on A, where .' is the short hand for the transpose of a 2D matrix. So:
>> A = round(rand(4,5))
A =
0 1 0 1 1
0 1 0 0 1
0 0 0 0 1
0 1 1 0 0
then
B=A.';
B(find(B)) = find(B);
B=B.';
gives
B =
0 2 0 4 5
0 7 0 0 10
0 0 0 0 15
0 17 18 0 0
Here's a solution that doesn't require any transposing back and forth:
>> B = A; %# Initialize B
>> C = A; %# Initialize C
>> mask = logical(A); %# Create a logical mask using A
>> [r,c] = find(A); %# Find the row and column indices of non-zero values
>> index = c + (r - 1).*size(A,2); %# Compute the row-first linear index
>> [~,order] = sort(index); %# Compute the row-first order with
>> [~,order] = sort(order); %# two sorts
>> B(mask) = index %# Fill non-zero elements of B
B =
0 2 0 4 5
0 7 0 0 10
0 0 0 0 15
0 17 18 0 0
>> C(mask) = order %# Fill non-zero elements of C
C =
0 1 0 2 3
0 4 0 0 5
0 0 0 0 6
0 7 8 0 0
An outline (Matlab isn't on this machine, so verification is delayed):
You can use find() to get the coordinate list. Let T = A'; [r,c] = find(T)
From the coordinate list, you can create both B and C. Let valB = sub2ind([r,c],T) and valC = 1:length(r)
Use the sparse command to create B and C, e.g. B = sparse(r,c,valB), and then transpose, e.g. B = B' (or could do sparse(c,r,valB)).
Or, as #IanHincks suggests, let B = A'; B(find(B)) = find(B). (I'm not sure why .' is recommended, but, again, I don't have Matlab in front of to check.) For C, simply use C(find(C)) = 1:nnz(A). And transpose back, as he suggests.
Personally, I work with coordinate lists all the time, having migrated away from the sparse matrix representation, just to cut out the costs of index lookups.