Can I vectorize this matrix task in MATLAB? - matlab

I have a square matrix A which I would like to manipulate using a matrix D. The result will be the matrix B. The entry B(i, j) will be a sum of a number of elements from A(:, j), as determined by D.
Let me show what I have in codes -
A = magic(3); % initial matrix
D = [1, 2, 3; 2, 2, 3; 1, 3, 1]; % map matrix
B = zeros(3); % resultant matrix
for i = 1:3
for j = 1:3
B(D(i, j), j) = B(D(i, j), j) + A(i, j);
end
end
Now, to explain we have D as :
D = [1, 2, 3;
2, 2, 3;
1, 3, 1];
These D values basically act as the row indices to accumulate into output array B, while column indices would be iterating numbers themselves -
col = [1 2 3;
1 2 3;
1 2 3];
Thus, the summations are :
B(1,1) += A(1,1) % Indices from first row from D to select B
B(2,2) += A(1,2)
B(3,3) += A(1,3)
B(2,1) += A(2,1) % Indices from second row from D to select B
B(2,2) += A(2,2)
B(3,3) += A(2,3)
B(1,1) += A(3,1) % Indices from third row from D to select B
B(3,2) += A(3,2)
B(1,3) += A(3,3)
Thus, at B(1,1) we would accumulate two values from A : A(1,1) and A(3,1) and so on.
When the matrices are large, this process takes a long time. Is there a way to vectorize this computation?

Here's one approach using accumarray and bsxfun -
[m,n] = size(A);
% Generate all linear indices corresponding to D
idx = bsxfun(#plus,D,m*(0:n-1))
% Use accumarray to accumulate values for each of those indices.
% We need some reshaping to get a final 2D output after feeding in column
% vectors for using accumarray
Bout = reshape(accumarray(idx(:),A(:),[m*n,1]),[m,n])
Sample run -
>> % Setup inputs and run loopy version
A = randi(9,4,6);
D = randi(size(A,1),4,6);
B = zeros(size(A));
for i = 1:size(A,1)
for j = 1:size(A,2)
B(D(i, j), j) = B(D(i, j), j) + A(i, j);
end
end
% Proposed vectorized method
>> [m,n] = size(A);
>> idx = bsxfun(#plus,D,m*(0:n-1));
>> Bout = reshape(accumarray(idx(:),A(:),[m*n,1]),[m,n]);
% Verify results
>> max(abs(Bout(:)-B(:)))
ans =
0

Related

How can I find the value of a matrix that minimize a least square cost function in MATLAB?

Given the value of S1 which is a vector of size (1,K), I want to find the value of matrix B of size (N,M) that can minimize the following least square cost function:
sum(S2 - S1).^2
Subject to:
S2(i)>=S1(i) \forall i \in {1, .., K}
Where S2 is a vector of size (1,K) and is a function of matrix B.
S2 can be calculated after optimizing matrix B using the following system parameters and equations:
clc;
clear;
% Given system parameters:
N = 2;
K = 4;
M = 2;
C_l = 4;
H = [0.1185 0.2811; 0.3550 0.8224; 0.3260 0.9644; 0.5333 0.6083]; % 4*2 matrix
A = [-2 1; -1 1]; % 2*2 matrix
C = [7 -3; 7 -3; -2 1; -2 1]; % 4*2 matrix
P = [25000000 0; 0 25000000]; % 4*4 matrix
S1 = [3.1683 3.1686 1.8716 1.8898]; % 1*4 vector
S2 = zeros(1,K); % intial value
B = zeros(N,M); % intial value
% How can we optimize the value of the B matrix to achieve our goal?
%calculate S2 from B and the other given inputs
for j=1:1:N
d(j) = (B(j,:)*P*B(j,:)')/((2^(2*C_l))-(norm(A(:,j))^2));
end
D_d = diag(d);
for i=1:1:K
V_d(i)=C(i,:)*P*B'*H(i,:)'*inv(1+H(i,:)*(A'*D_d*A+B*P*B')*H(i,:)');
sigma_d(i)=norm((V_d(i)*H(i,:)*B-C(i,:))*(P^(1/2)))^2+(V_d(i)^2)*(1+H(i,:)*A'*D_d*A*H(i,:)');
S2(i)=0.5*log2((P(1,1))/sigma_d(:,i));
end

Multiply n vectors of length p by n matrices of size pxp

I have n complex vectors of length p that I want to multiply by n complex matrices of size p-by-p. I am looking for the most efficient way to do this in MATLAB. If it matters, I am imagining that n is large and p is small.
An example using a loop (which I would like to avoid) is shown below.
N = 1e4;
p = 5;
A = randn(p, N); % N vectors of length p
B = randn(p, p, N); % N matrices of size pxp
C = zeros(p, N);
for k = 1:N
C(:, k) = B(:, :, k) * A(:, k);
end
It's been suggested that I might be able to achieve this efficiently using tensor functions, but I haven't been able to figure that out.
Here's a way using implicit expansion:
C = permute(sum(B.*permute(A, [3 1 2]), 2), [1 3 2]);
For old Matlab versions (before R2016b) you need to rewrite it with bsxfun:
C = permute(sum(bsxfun(#times, B, permute(A, [3 1 2])), 2), [1 3 2]);
You can accomplish that in various ways:
A = rand(3, 3, 1e6);
B = rand(3, 1);
tic, C = zeros(3, size(A, 3));
for i = 1:size(A, 3)
C(:,i) = A(:,:,i)*B ;
end, toc
tic; C = reshape(reshape(permute(A,[2,1,3]),3,[]).'*B,3,[]); toc
tic; C = squeeze(sum(bsxfun(#times, A, reshape(B, 1, 3)), 2)); toc
In my system:
Elapsed time is 2.067629 seconds. % Loop
Elapsed time is 0.064164 seconds. % permute
Elapsed time is 0.145738 seconds % sum(times())

how to get an incremental power matrix in matlab

I wanted to compute the following matrix in Matlab:
g=[I
A
.
.
.
A^N]
I used the following program in Matlab:
A=[2 3;4 1];
s=A;
for n=1:1:50
s(n)=A.^n;
end
g=[eye(1,1),s];
I am getting the following error:
In an assignment A(I) = B, the number of elements in B and I must be the same.
Error in s_x_calcu_v1 (line 5)
s(n)=A.^n;
The problem is that you are trying to assign a matrix to a single element. In matlab calling s(n) mean you get the nth element of s, regardless of the dimensions of s. You can use a three dimensional matrix
N = 50;
A=[2 3;4 1];
[nx,ny] = size(A);
s(nx,ny,N) = 0; %makes s a nx x ny x N matrix
for n=1:1:N
s(:,:,n)=A.^n; %Colon to select all elements of that dimension
end
g=cat(3, eye(size(A)) ,s); %Add the I matrix of same size as A
Or a vectorized version
s = bsxfun(#power, A(:), 1:N);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
And a third solution using cumprod
s = repmat(A(:), [1 N]);
s = cumprod(s,2);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
Your s array is a 2-by-2 array, you cannot index it to store the result of your compuation at each step of your loop.
For this, the simpler is probably to define s as a cell:
% --- Definitions
A = [2 3;4 1];
N = 50;
% --- Preparation
s = cell(N,1);
% --- Computation
for n=1:N
s{n} = A.^n;
end
Best,
When you loop from 1 to N computing each time A.^n you are doing LOTS of redundant computations! Note that
A.^n = (A.^(n-1)).*A; %//element-wise power
A^n = (A^n) * A; %// matrix power
Therefore,
A = [2 3;4 1];
N = 50;
s = cell(N+1,1);
s{1} = eye(size(A,1));
for ii=1:N
s{ii+1} = s{ii}.*A; %// no powers, just product!
end
g = vertcat( s{:} );
BTW, the same holds if you want to compute matrix power (instead of element-wise powers), all you need is changing to s{ii+1} = s{ii}*A;

binary matrix of all combination with equal probability of 1 and 0 in matlab

I want to generate a binary matrix, let say (8,1). with equal probability means four 1's and four 0s in a matrix. with different permutation of these elements total 70 combination are possible (e.g 8C4). I want these all possible combination one by one.
please help.
The straighforward answer is:
unique(perms([true(1, N / 2), false(1, N / 2)]), 'rows')
or in a fancier form:
unique(perms(sparse(1, 1:N / 2, true, 1, N)), 'rows')
where N is the length of your vector (N = 8 in your example). However, expect this solution to be very slow for large arrays.
Surprisingly, in this case a faster method is to generate all possible permutations (see here) and eliminate those that do not satisfy the desired criterion:
C = cell(N, 1); %// Preallocate memory
[C{:}] = ndgrid([true, false]); %// Generate N grids of binary values
p = cellfun(#(x){x(:)}, C); %// Convert grids to column vectors
p = [p{:}]; %// Obtain all combinations
p = p(sum(p, 2) == N / 2, :); %// Keep only desired combinations
Benchmark
N = 8;
%// Method #1 (one-liner)
tic
for k = 1:1e3
p = unique(perms(sparse(1, 1:N / 2, true, 1, N)), 'rows');
end
toc
%// Method #2
tic
for k = 1:1e3
C = cell(N, 1);
[C{:}] = ndgrid([true, false]);
p = cellfun(#(x){x(:)}, C);
p = [p{:}];
p = p(sum(p, 2) == N / 2, :);
end
toc
The results I got were:
Elapsed time is 0.858539 seconds. %// Method #1
Elapsed time is 0.803826 seconds. %// Method #2
... and for N = 10:
Elapsed time is 55.3068 seconds. %// Method #1
Elapsed time is 1.03664 seconds. %// Method #2
Not only that nchoosek fails for large values of N, it's also slower.
Here is an even faster way to do it, using the fact that you are looking for a subset of binary number representations:
b = dec2bin(1:2^N-1);
x = b-'0';
x = x(sum(x,2)==N/2,:);
The performance comparison:
N = 8;
% Dennis solution
tic
b = dec2bin(1:2^N-1);
x = b-'0';
x=x(sum(x,2)==N/2,:);
toc
% Eitan Method 2
tic
for k = 1:1e3
C = cell(N, 1);
[C{:}] = ndgrid([true, false]);
p = cellfun(#(x){x(:)}, C);
p = [p{:}];
p = p(sum(p, 2) == N / 2, :);
end
toc
Gives these timings:
Elapsed time is 0.002200 seconds.
Elapsed time is 0.594309 seconds.
Note that the resulting lines will be in different orders for both solutions.

Convert vector into logical matrix?

I have a vector y of length n. y(i) is an integer in 1..m. Is there a simpler way to convert y into an n x m logical matrix yy, where yy(i, j) = 1 if y(i) = j, but 0 otherwise? Here's how I've been doing it:
% If m is known (m = 3 here), you could write it out all at once
yy = [y == 1; y== 2; y == 3];
yy = reshape(yy, n, 3);
or
% if m is not known ahead of time
yy = [ y == 1 ];
for i = 2:m;
yy = [ yy; y == i ];
end
yy = reshape(yy, n, m);
You can use bsxfun for this
yy = bsxfun(#eq,y(:),[1,2,3])
y is transformed (if necessary) to a column-vector, while the other vector is a row vector. bsxfun implicitly expands the m-by-1 and 1-by-n arrays so that the result becomes m-by-n.
If n*m is sufficiently large (and m is, by itself, sufficiently large), it is a good idea to create yy as a sparse matrix. Your y vector is really a special type of sparse matrix format, but we can translate it into the built-in sparse matrix format by doing the following.
yy = sparse(1:length(y), y, 1);
This will keep your storage to O(n). It is not going to be doing you a lot of favors if you are using yy for a lot of indexing. If that is the case you are better off using your original sparse structure (i.e., y).
A slight modification to your method:
% A n-dimensional vector y, with values in some range 1..m
m = 4;
n = 7;
y = randi([1 m], n, 1);
% Preallocating a n by m matrix of zeros
nXm = zeros(n, m);
% In each pass of this loop a single column of nXm is updated, where
% for each column index j in nXm, if y(i) = j then nXm(i,j) = 1
for j = 1:m;
nXm(:,j) = (y == j);
end
From Machine Learning on Coursera:
yy = eye(m)(y, :)
This requires that the list be a range 1:m (as OP stated). For an irregular list, like [2 3 5], do this
yy = eye(m)(:, [2 3 5])(y, :)
Note: not tested on MATLAB.
In octave you can write:
yy = y' == (1:m); % or y == (1:m)' for transposed
[1 2 1 3 2] == [1 2 3]' % = [1 0 1 0 0; 0 1 0 0 1; 0 0 0 1 0]