Replicate Kronecker tensor with repmat in MATLAB - matlab

I am trying to replicate the Kron product using only repmat and reshape and I believe I am pretty close but I can't manage to do the last correct reshape.
Particularly I have problem in reshaping A
To make things simple let suppose we have
A=[1 3; 2 4]
B=[5 10; 10 5]
so my kron(A,B) is going to be a 4x4 matrix.
kron=[5 10 15 30
10 5 30 15
10 20 20 40
20 10 40 20]
I am proceeding this way:
Y=repmat(B,2,2)
X=A(:);
X=repmat(X,1,2)';
X=X(:);
X=repmat(X,1,2);
which gives me the following 8x2 Matrix:
X= [1 1
1 1
2 2
2 2
3 3
3 3
4 4
4 4]
I can't just figure out how to do the correct reshape to obtain my 4x4 matrix:
X=[1 1 3 3
1 1 3 3
2 2 4 4
2 2 4 4]
Then I will be able to compute: X.*Y=kron(A,B)

Here's one approach using the powerful trio of bsxfun, permute and reshape -
M = bsxfun(#times,B,permute(A,[3 4 1 2]));
out = reshape(permute(M,[1 3 2 4]),size(A,1)*size(B,1),[]);
If you are hell bent on using repmat, perform the calculation of M with it, like so -
M = repmat(B,[1 1 size(A)]).*permute(repmat(A,[1 1 size(B)]),[3 4 1 2])
Verify output by comparing against kron for generic matrix sizes -
>> A = rand(4,5);
>> B = rand(6,7);
>> M = bsxfun(#times,B,permute(A,[3 4 1 2]));
>> out = reshape(permute(M,[1 3 2 4]),size(A,1)*size(B,1),[]);
>> out_kron = kron(A,B);
>> max(abs(out(:) - out_kron(:)))
ans =
0
Here's one using matrix-multiplication and as such must be pretty efficient -
[mA,nA] = size(A);
[mB,nB] = size(B);
out = reshape(permute(reshape(B(:)*A(:).',mB,nB,mA,nA),[1 3 2 4]),mA*mB,[])

If you don't want to use any loops or bsxfun/arrayfun-solutions, you can do as follows:
[ma,na] = size(A);
[mb,nb] = size(B);
Y = repmat(B,ma,mb);
X = reshape(repmat(reshape(repmat(A(:),1,mb)',ma*mb,na),nb,1),ma*mb,na*nb);
X.*Y

Related

permutation of separate rows of matrix

How to effectively vectorize the following MATLAB code, which performs permutation of each row of matrix R by indices in corresponding row of matrix P?
for i = 1:size(P,1)
pP(i,:) = R(i,P(i,:));
end
example:
P = [3 2 1;
3 1 2;
2 3 1;
2 1 3;
1 2 3;
1 3 2]
R = [6 5 4;
6 4 5;
5 6 4;
5 4 6;
4 5 6;
4 6 5]
produce following matrix pR:
4 5 6
5 6 4
6 4 5
4 5 6
4 5 6
4 5 6
One approach with bsxfun -
nrows = size(R,1)
pP = R(bsxfun(#plus,[1:nrows]',(P-1)*nrows))
Or with ndgrid -
[m,n] = size(R)
pP = R(sub2ind([m n],ndgrid(1:m,1:n),P))
Or replace ndgrid(1:m,1:n) with repmat: repmat([1:m]',[1 n]) or with meshgrid:meshgrid(1:m,1:n).'.
This might not be the best way to do it, but you could do something like:
IND1 = P(:,1)
Q(:,1) = diag(R(:,IND));
and repeat for P(:,2), P(:,3) in a similar fashion.
You can use arrayfun to avoid the loop but probably won't gain in performance if that it is the reason for vectorizing it:
cell2mat(arrayfun(#(k) R(k, P(k,:)), (1:size(P,1)).', 'uni', 0))

average 3rd column when 1st and 2nd column have same numbers

just lets make it simple, assume that I have a 10x3 matrix in matlab. The numbers in the first two columns in each row represent the x and y (position) and the number in 3rd columns show the corresponding value. For instance, [1 4 12] shows that the value of function in x=1 and y=4 is equal to 12. I also have same x, and y in different rows, and I want to average the values with same x,y. and replace all of them with averaged one.
For example :
A = [1 4 12
1 4 14
1 4 10
1 5 5
1 5 7];
I want to have
B = [1 4 12
1 5 6]
I really appreciate your help
Thanks
Ali
Like this?
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7];
[x,y] = consolidator(A(:,1:2),A(:,3),#mean);
B = [x,y]
B =
1 4 12
1 5 6
Consolidator is on the File Exchange.
Using built-in functions:
sparsemean = accumarray(A(:,1:2), A(:,3).', [], #mean, 0, true);
[i,j,v] = find(sparsemean);
B = [i.' j.' v.'];
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7]; %your example data
B = unique(A(:, 1:2), 'rows'); %find the unique xy pairs
C = nan(length(B), 1);
% calculate means
for ii = 1:length(B)
C(ii) = mean(A(A(:, 1) == B(ii, 1) & A(:, 2) == B(ii, 2), 3));
end
C =
12
6
The step inside the for loop uses logical indexing to find the mean of rows that match the current xy pair in the loop.
Use unique to get the unique rows and use the returned indexing array to find the ones that should be averaged and ask accumarray to do the averaging part:
[C,~,J]=unique(A(:,1:2), 'rows');
B=[C, accumarray(J,A(:,3),[],#mean)];
For your example
>> [C,~,J]=unique(A(:,1:2), 'rows')
C =
1 4
1 5
J =
1
1
1
2
2
C contains the unique rows and J shows which rows in the original matrix correspond to the rows in C then
>> accumarray(J,A(:,3),[],#mean)
ans =
12
6
returns the desired averages and
>> B=[C, accumarray(J,A(:,3),[],#mean)]
B =
1 4 12
1 5 6
is the answer.

Construct columns from submatrices in Matlab

In Matlab, I'm trying to transform a matrix A to another matrix B such that B's columns are made up of square submatrices of A. For example, if A is:
A = [1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4];
I'd like B to be:
B = [1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4]
A could be, say 16-by-16, and constructing B from 4-by-4 squares would result in B being 4-by-64.
Is there an efficient way to do this using reshape in combination with some other commands? Or some other approach? I am currently iterating in a loop, which is very slow with a large number of large source matrices.
Assume your matrix is a bit more general, and made of 3x2 blocks:
A = [1 1 2 2
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
3 3 4 4
5 5 6 6
5 5 6 6
5 5 6 6];
b = [3 2];
szA = size(A);
Transpose, reshape, permute, reshape.
nb = prod(szA./b); % Number of blocks
nelb = prod(b); % Number of elements per block
out1 = reshape(permute(reshape(A',szA(2),b(1),szA(1)/b(1)),[2,1,3]),nelb,nb)
Alternatively, slower and memory intensive but more readable:
d1 = repmat(b(1),1,szA(1)/b(1));
d2 = repmat(b(2),1,szA(2)/b(2));
out = reshape(mat2cell(A,d1,d2)',1,nelb);
out = reshape([out{:}],nelb,nb)
Now, if the blocks are square, simply set b = [2,2] or b = [3,3], etc..., or simplify the general formulation removing indexing of b and prod.

How to perform this kind of matrix division in Matlab?

This problem is probably less to do with Matlab and more to do with matrix algebra (which I mostly forget from my college courses). Say I have a m x n matrix X and a m x 1 matrix B. How would I divide the X by B such that all the elements of the ith row of X are piecewise divided by the ith row of B, resulting in another m x n matrix Y?
E.g.
X = [2 4 8; 3 9 27; 4 16 64]
B = [2; 3; 4]
X ? B = [2/2 4/2 8/2; 3/3 9/3 27/3; 4/4 16/4 64/4]
ans =
1 2 4
1 3 9
1 4 16
Better not use repmat - it is slow and allocates additional memory for the workspace. You can use bsxfun, which is an inbuilt function, so it is faster and avoids the extra workspace:
X = [2 4 8; 3 9 27; 4 16 64]
B = [2; 3; 4]
bsxfun(#rdivide, X, B)
ans =
1 2 4
1 3 9
1 4 16
Junuxx's comment pointed me in the right direction. The solution I used to get what I wanted is:
B_prime = repmat(B,1,3)
X ./ B_prime
ans =
1 2 4
1 3 9
1 4 16
I'd still like to know what this kind of operation is called (if it even has a formal name).
X is m x n and B is m x 1
size(X,2) gives the value of n i.e. number of columns
So, you need to do:
X./repmat(B,1,size(X,2))
X = [2 4 8; 3 9 27; 4 16 64]
B = [2; 3; 4]
Result= X./B(:,ones(1,3)) %is faster then repmat
Result =
1 2 4
1 3 9
1 4 16

Matlab - find(x==a) when a is a matrix

I don't know how to do it. I have some matrices with repeated values and I'd like to sort them and get a matrix of sorted indices. For example:
a = [1 4 3 10 8 2];
b = sort(a);
% This doesn't work but I wish it did - that's what I'm looking for.
% idx = find(a==b); idx = [1 6 3 2 5 4];
[v idx] = ismember(b,a);
However, when there are repeated values and NaN this produces errors. Try this:
a = [1 NaN 4 2 10 8 2];
b=sort(a);
[v, i] = ismember(b,a);
Gives [1 7 7 3 6 5 0] which is valid but I need it to be [ 1 4 7 3 6 5 0].
I can deal with this later but it's going to be much more elegant if it were to return the above result.
sort has a two output value version that gives you exactly the indices you're looking for:
a = [1 4 3 10 8 2];
[b, idx] = sort(a);
Outputs
idx =
1 6 3 2 5 4
This works correctly with NaNs as well:
a = [1 NaN 4 2 10 8 2];
[b, idx] =sort(a);
>> idx
idx =
1 4 7 3 6 5 2
>> b
b =
1 2 2 4 8 10 NaN