How to vectorize code with 3 loops that generates a matrix - matlab

Could you help me to vectorise this Matlav code constructing a matrix A of dimension MNx(2+N-1)xR in order to speed it up? At the moment it takes approx. 8 sec
INITIALISING
R=200;
M=400;
N=20;
B=[kron((1:1:M)', ones(N,1)) repmat((1:1:N)', M,1)]; %(MN)x(2)
B looks like
B=[1 1;
1 2;
...;
1 20;
2 1;
2 2;
...
2 20;
...
400 20]
CODE
A=[ repmat(B,1,1,R) zeros(M*N,N-1,R)]; %Allocate the space
%(MN)x(2+N-1)x(R)
% I want A(:,1:2,r)=B for r=1,...,R
%Fill the remaining columns of A in the following way
for r=1:R
utemp1=randn(M*N, N-1); %Generate a matrix of random
%numbers of dimension (M*N)x(N-1)
utemp2=randn(M, N); %Generate a matrix of random
%numbers of dimension (M)x(N)
utemp3=zeros(M*N,N-1); %Generate a matrix of random
%numbers of dimension(M)x(N-1)
for m=1:M
for j=1:N
utemp3((m-1)*N+j,:)= utemp2(m,j)+[utemp2(m,1:j-1) utemp2(m,j+1:N)]; %(1)x(N-1)
%e.g. if m=2, j=3, I want to fill the 23th row of utemp3
%with utemp2(2,3)+[utemp2(2,1:2) utemp2(m,4:20)];
%e.g. if m=4, j=1, I want to fill the 61st row of utemp3
%with utemp2(4,1)+[utemp2(4,2:20)];
end
end
A(:,3:end,r)=utemp1+utemp3; %sum utemp1
end
SOME EXPLANATIONS
for r=1,...,R
A is such that
for m=1,...,M and for j=1,...,N
the row in A(:,:,r) starting with [m j] in the first two columns is filled in the remaining (N-1) columns with uj+uh+ujh for each h~=j, where uj, uh, ujh are i.i.d standard Gaussian numbers that can be found in utemp1 and utemp2.

You can precompute the indices and use them in 500 iterations:
idx = repmat(reshape(1:M*N,M,N).',1,N);
idx = reshape(idx(logical(kron(~eye(N),ones(1,M)))),N-1,[]).';
for k=1:500
for r=1:R
utemp2=randn(M*N,1);
A(:,3:end,r)=randn(M*N, N-1)+bsxfun(#plus,utemp2,utemp2(idx) );
end
end
However allocating large matrices ,specially of repeated elements, and vectorizing operations on it is not always the most efficient way. There are built-in functions that directly operate on the original array avoiding repeating elements of the array.

Related

Assigning values to a matrix through vector addition from an adjacency matrix

Very new to Matlab, I usually use STATA.
I want to use the nchoosek fuction to get the sum of vectors in one matrix.
I have a 21x21 adjacency matrix, with either 0 or 1 as the inputs. I want to create a new matrix, that will give me a sum of inputs between all possible triads from the adjacency matrix.
The new matrix should look have four variables, indexes (i, j, k) - corresponding to each combination from the 21x21. And a final variable which is a sum of the inputs.
The code I have so far is:
C = nchoosek(21,3)
B = zeros(nchoosek(21,3), 4)
for i=1:C
for j=i+1:C
for k=j+1:C
B(?)=B(i, j, k, A(i)+A(j)+A(k)) #A is the 21x21 adj mat
end
end
end
I know my assignment statement is incorrect as I don't completed understand the indexing role of the ":" operator. Any help will be appreciated.
Thanks!
This might be what you want:
clear all
close all
clc
A = rand(21,21); % Replace this with actual A
rowNum = 0;
for i=1:21
for j=i+1:21
for k=j+1:21
rowNum = rowNum+1;
B(rowNum,:) = [i, j, k, sum(A(:,i)+A(:,j)+A(:,k))];
end
end
end
There are some points:
You loop for different combinations. the total number of combination is nchoosek(21,3) which you can check after 3 nested loop. Your code with for i=1:C was the first error since you're actually looping for different values of i and different values of j and k. So these just 21 values not more.
To avoid repeated combinations, it's enough to start new index after the previous one, which you've realized in your code.
There are other possible approaches such as vectorized format, but to stick to your approach, I used a counter: rowNum which is the loop counter and updated along the loop.
B(rowNum,:) means all element of rowNum'th row of the matrix B.
Below is an algorithm to find the triads in an adjacency matrix. It checks all possible triads and sums the values.
%basic adjacency matrix with two triads (1-2-5) (2-3-5)
A=[];
A(1,:) = [0 1 0 0 1];
A(2,:) = [1 0 1 0 1];
A(3,:) = [0 1 0 0 1];
A(4,:) = [0 0 0 0 1];
A(5,:) = [1 1 1 1 0];
A=A==1; %logical matrix
triads=nchoosek(1:5,3);
S=nan(size(triads,1),4);
for ct = 1:size(triads,1)
S(ct,1:3)=[A(triads(ct,1),triads(ct,2)),A(triads(ct,1),triads(ct,3)),A(triads(ct,2),triads(ct,3))];
S(ct,4)=sum(S(ct,1:3));
end
triads(find(S(:,4)==3),:)
ans =
1 2 5
2 3 5

How to concatenate smaller matrices to form a larger matrix?

I would like to concatenate 14641 number of 3X3 matrices into a matrix of size 363X363 and the large matrix must contain 121 submatrices in each row(121*3=363 columns) and 121 such rows of submatrices(121*3=363rows).I have gone through the similar questions but I didn't get the correct logic to concatenate large number of matrices.
Awaiting your suggestions. Thanks in advance.
PS: I got those 3X3 matrices from a 363X363 matrix. The following code is for splitting the single matrix into submatrices.
I=imread('photo.jpg');
B = randi([0 255],363,726,3);
B(1:numel(I)) = I;
L=B(1:363,1:363);
[al,bl]=size(L);
ImageSize=al*bl;
BlockD=3; % i assume 3x3 block
BlockSize=BlockD*BlockD;
TotalBlocks=ImageSize/BlockSize;
subL=zeros(BlockD,BlockD,TotalBlocks); %arrays of blocks.
LL=double(L);
k=1;
for i=1:BlockD:al
for j=1:BlockD:bl
subL(:,:,k)=LL(i:i+BlockD-1,j:j+BlockD-1);
k=k+1;
end
end
Now I want to concatenate all these 'subL' submatrices to form 'LL' again
Using blocproc instead of above code
I tried using blockproc function instead of the above code. I did this piece of code and is working pretty well.Thank you
I=imread('photo.jpg');
B = randi([0 255],363,726,3);
B(1:numel(I)) = I;
L=B(1:363,1:363);
q=[1 2 3 4];
fun=#(block_struct)quaternionrotate(q,block_struct.data);
LL = blockproc(L,[3 3],fun);
and the function quaternionrotate is
function [ Lrot1 ] = quaternionrotate(q,A)
qinv=quatinv(q);
B=zeros(3,1);
A1=[B A];
Lrot=quatmultiply(q,quatmultiply(A1,qinv));
Lrot(:,1)=[];
Lrot1=Lrot;
end
Finally figured this out!
% I used this to make 14641 3x3 sample matrices, you should use your own
matrix = ones(3,3);
for i = 2:14641
matrix = [matrix, i*ones(3,3)];
end
c_size = 121;
output = cell(c_size, c_size) % Make 121x121 cell matrix
% Populate the cell matrix
for i = 1:1:c_size
for j = 1:1:c_size
output(i,j) = {matrix(1:3, (i-1)*c_size*3+(j-1)*3+1 : (i-1)*c_size*3+(j-1)*3+3)};
end
end
This breaks up the 43923x3 size matrix of 363 3x3 matrices into a 121x121 cell matrix of 3x3 matrices. Now that's a tongue twister...
Either way the code does what you need! :)

Regarding Random duplication of data elements

Please consider my sample code:
data=[-1 0 1 2]; % data
N=[4,8,16]; % No. of desired output columns
Now Create a output matrix such that:
out=1xN % having each element of data randomly repeated exactly N/4 times
For N as a scalar it's simple:
data=[-1 0 1 2];
N= 4
R = repmat(data', 1, N); %// You actually you don't really need the '
R(randperm(numel(R)))

How to make a block of matrices from large matrix in matlab?

I have a 256x256 matrix from that I want to create block of 8x8 matrices. I have a below code which shows just one block i want all the blocks then subtract a number from each element of the matrix. When i do the subtract 128 from each element it doesn't show negative values rather showing only 0.
[x,y] = size(M);
for i=1:8
for j=1:8
k(i,j) = M(i,j);
end
end
disp(k);
for a=1:size(k)
for b=1:size(k)
A(a,b) = k(a,b) - 128;
end
end
disp(A);
well, if you have to subtract a fixed value it's better to do it as
M = M - 128;
It's faster.
You said you get 0 values instead of negative ones; it is likely due to the type of the matrix which is unsigned (i.e. the guys are just positive). A cast to general integer is necessary. Try:
M = int16(M) - 128;
To obtain the partition I propose the following, there can be more efficient ways, btw:
r = rand(256,256); %// test case, substitute with your matrix M
[i j] = meshgrid(1:8:256); %// lattice of indices
idx = num2cell([ i(:) , j(:) ],2); %// cell version
matr = cellfun( #(i) r(i(1):i(1)+7,i(2):i(2)+7), idx, 'UniformOutput',false); %// blocks
matr will contain all the sub-matrices in lexicographical order. e.g.
matr{2}
ans =
0.4026 0.3141 0.4164 0.5005 0.6952 0.1955 0.9803 0.5097
0.8186 0.9280 0.1737 0.6133 0.8562 0.7405 0.8766 0.0975
0.2704 0.8333 0.1892 0.7661 0.5168 0.3856 0.1432 0.9958
0.9973 0.8488 0.6937 0.2630 0.1004 0.5842 0.1844 0.5206
0.4052 0.0629 0.6982 0.1530 0.9234 0.1271 0.7317 0.3541
0.2984 0.3633 0.1510 0.0297 0.0225 0.7945 0.2925 0.0396
0.5097 0.0802 0.8744 0.1032 0.8523 0.6150 0.4845 0.5703
0.8635 0.0194 0.1879 0.5017 0.5297 0.6319 0.2406 0.5125
Explanation: In matlab you can efficiently operate on matrices and slices of them. For instance, you can easily copy submatrices, e.g. the first 8x8 matrix is
sub = M(1:8,1:8);
You want all the submatrices, thus you need kind of a lattice of indices to get
sub_ij = M(1+8*(i-1) : 7 * 8*(i-1) , 1+8*(j-1) : 7 * 8*(j-1))
i.e. you need the lattice
(1+8*(i-1) : 7 * 8*(i-1) , 1+8*(j-1) : 7 * 8*(j-1)) % // for all i,j
you can use meshgrid for that.
Finally you have to cut off the pieces, that is what the last two instruction do. particularly the first one generates the indices (try idx{1},...), and the second one generates the submatrices (try matr{1},...).
Hope this code helps you. It has 16x16 image to divide into 4x4 blocks. You may change the value and get the required output as you like.

Indexing of unknown dimensional matrix

I have a non-fixed dimensional matrix M, from which I want to access a single element.
The element's indices are contained in a vector J.
So for example:
M = rand(6,4,8,2);
J = [5 2 7 1];
output = M(5,2,7,1)
This time M has 4 dimensions, but this is not known in advance. This is dependent on the setup of the algorithm I'm writing. It could likewise be that
M = rand(6,4);
J = [3 1];
output = M(3,1)
so I can't simply use
output=M(J(1),J(2))
I was thinking of using sub2ind, but this also needs its variables comma separated..
#gnovice
this works, but I intend to use this kind of element extraction from the matrix M quite a lot. So if I have to create a temporary variable cellJ every time I access M, wouldn't this tremendously slow down the computation??
I could also write a separate function
function x= getM(M,J)
x=M(J(1),J(2));
% M doesn't change in this function, so no mem copy needed = passed by reference
end
and adapt this for different configurations of the algorithm. This is of course a speed vs flexibility consideration which I hadn't included in my question..
BUT: this is only available for getting the element, for setting there is no other way than actually using the indices (and preferably the linear index). I still think sub2ind is an option. The final result I had intended was something like:
function idx = getLinearIdx(J, size_M)
idx = ...
end
RESULTS:
function lin_idx = Lidx_ml( J, M )%#eml
%LIDX_ML converts an array of indices J for a multidimensional array M to
%linear indices, directly useable on M
%
% INPUT
% J NxP matrix containing P sets of N indices
% M A example matrix, with same size as on which the indices in J
% will be applicable.
%
% OUTPUT
% lin_idx Px1 array of linear indices
%
% method 1
%lin_idx = zeros(size(J,2),1);
%for ii = 1:size(J,2)
% cellJ = num2cell(J(:,ii));
% lin_idx(ii) = sub2ind(size(M),cellJ{:});
%end
% method 2
sizeM = size(M);
J(2:end,:) = J(2:end,:)-1;
lin_idx = cumprod([1 sizeM(1:end-1)])*J;
end
method 2 is 20 (small number of index sets (=P) to convert) to 80 (large number of index sets (=P)) times faster than method 1. easy choice
For the general case where J can be any length (which I assume always matches the number of dimensions in M), there are a couple options you have:
You can place each entry of J in a cell of a cell array using the num2cell function, then create a comma-separated list from this cell array using the colon operator:
cellJ = num2cell(J);
output = M(cellJ{:});
You can sidestep the sub2ind function and compute the linear index yourself with a little bit of math:
sizeM = size(M);
index = cumprod([1 sizeM(1:end-1)]) * (J(:) - [0; ones(numel(J)-1, 1)]);
output = M(index);
Here is a version of gnovices option 2) which allows to process a whole matrix of subscripts, where each row contains one subscript. E.g for 3 subscripts:
J = [5 2 7 1
1 5 2 7
4 3 9 2];
sizeM = size(M);
idx = cumprod([1 sizeX(1:end-1)])*(J - [zeros(size(J,1),1) ones(size(J,1),size(J,2)-1)]).';