Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple constant to a matrix and convert them into block diagonal matrix in matlab

I have a1 a2 a3. They are constants. I have a matrix A. What I want to do is to get a1*A, a2*A, a3*A three matrices. Then I want transfer them into a diagonal block matrix. For three constants case, this is easy. I can let b1 = a1*A, b2=a2*A, b3=a3*A, then use blkdiag(b1, b2, b3) in matlab.

What if I have n constants, a1 ... an. How could I do this without any looping?I know this can be done by kronecker product but this is very time-consuming and you need do a lot of unnecessary 0 * constant.

Thank you.

like image 996
aaa Avatar asked Dec 11 '22 00:12

aaa


1 Answers

Discussion and code

This could be one approach with bsxfun(@plus that facilitates in linear indexing as coded in a function format -

function out = bsxfun_linidx(A,a)
%// Get sizes
[A_nrows,A_ncols] = size(A);
N_a = numel(a);

%// Linear indexing offsets between 2 columns in a block & between 2 blocks
off1 = A_nrows*N_a;
off2 = off1*A_ncols+A_nrows;

%// Get the matrix multiplication results
vals = bsxfun(@times,A,permute(a,[1 3 2])); %// OR vals = A(:)*a_arr;

%// Get linear indices for the first block
block1_idx = bsxfun(@plus,[1:A_nrows]',[0:A_ncols-1]*off1);  %//'

%// Initialize output array base on fast pre-allocation inspired by -
%// http://undocumentedmatlab.com/blog/preallocation-performance
out(A_nrows*N_a,A_ncols*N_a) = 0; 

%// Get linear indices for all blocks and place vals in out indexed by them
out(bsxfun(@plus,block1_idx(:),(0:N_a-1)*off2)) = vals;

return;

How to use: To use the above listed function code, let's suppose you have the a1, a2, a3, ...., an stored in a vector a, then do something like this out = bsxfun_linidx(A,a) to have the desired output in out.

Benchmarking

This section compares or benchmarks the approach listed in this answer against the other two approaches listed in the other answers for runtime performances.

Other answers were converted to function forms, like so -

function B = bsxfun_blkdiag(A,a)
B = bsxfun(@times, A, reshape(a,1,1,[])); %// step 1: compute products as a 3D array
B = mat2cell(B,size(A,1),size(A,2),ones(1,numel(a))); %// step 2: convert to cell array
B = blkdiag(B{:}); %// step 3: call blkdiag with comma-separated list from cell array

and,

function out = kron_diag(A,a_arr)
out = kron(diag(a_arr),A);

For the comparison, four combinations of sizes of A and a were tested, which are -

  • A as 500 x 500 and a as 1 x 10
  • A as 200 x 200 and a as 1 x 50
  • A as 100 x 100 and a as 1 x 100
  • A as 50 x 50 and a as 1 x 200

The benchmarking code used is listed next -

%// Datasizes
N_a = [10  50  100 200];
N_A = [500 200 100 50];

timeall = zeros(3,numel(N_a)); %// Array to store runtimes
for iter = 1:numel(N_a)
    
    %// Create random inputs
    a = randi(9,1,N_a(iter));
    A = rand(N_A(iter),N_A(iter));
    
    %// Time the approaches
    func1 = @() kron_diag(A,a);
    timeall(1,iter) = timeit(func1); clear func1
    
    func2 = @() bsxfun_blkdiag(A,a);
    timeall(2,iter) = timeit(func2); clear func2
    
    func3 = @() bsxfun_linidx(A,a);
    timeall(3,iter) = timeit(func3); clear func3
end

%// Plot runtimes against size of A
figure,hold on,grid on
plot(N_A,timeall(1,:),'-ro'),
plot(N_A,timeall(2,:),'-kx'),
plot(N_A,timeall(3,:),'-b+'),
legend('KRON + DIAG','BSXFUN + BLKDIAG','BSXFUN + LINEAR INDEXING'),
xlabel('Datasize (Size of A) ->'),ylabel('Runtimes (sec)'),title('Runtime Plot')

%// Plot runtimes against size of a
figure,hold on,grid on
plot(N_a,timeall(1,:),'-ro'),
plot(N_a,timeall(2,:),'-kx'),
plot(N_a,timeall(3,:),'-b+'),
legend('KRON + DIAG','BSXFUN + BLKDIAG','BSXFUN + LINEAR INDEXING'),
xlabel('Datasize (Size of a) ->'),ylabel('Runtimes (sec)'),title('Runtime Plot')

Runtime plots thus obtained at my end were -

enter image description here

enter image description here

Conclusions: As you can see, either one of the bsxfun based methods could be looked into, depending on what kind of datasizes you are dealing with!

like image 93
Divakar Avatar answered Dec 22 '22 01:12

Divakar