Assigning a matrix as an element of another matrix - matlab

Suppose I have the n x m matrices A,B,C. I want to make a "3 x 1" block-matrix Y, such that
Y(1,1) = A, Y(2,1) = B, Y(3,1) = C. Is that possible in MATLAB?

As an alternative to storing the three n x m matrices in a cell vector, consider storing them in a three-dimensional array of size n x m x 3. You can use the cat command to concatenate the matrices along the third dimension, for example:
>> n = 2; m = 3;
>> A = rand(n, m); B = rand(n, m); C = rand(n, m);
>> Y = cat(3, A, B, C)
Y(:,:,1) =
0.792207329559554 0.655740699156587 0.849129305868777
0.959492426392903 0.0357116785741896 0.933993247757551
Y(:,:,2) =
0.678735154857773 0.743132468124916 0.655477890177557
0.757740130578333 0.392227019534168 0.171186687811562
Y(:,:,3) =
0.706046088019609 0.27692298496089 0.0971317812358475
0.0318328463774207 0.0461713906311539 0.823457828327293
This can also be accomplished by indexing, for example:
>> Y(:,:,1) = A; Y(:,:,2) = B; Y(:,:,3) = C;
>> Y
Y(:,:,1) =
0.792207329559554 0.655740699156587 0.849129305868777
0.959492426392903 0.0357116785741896 0.933993247757551
Y(:,:,2) =
0.678735154857773 0.743132468124916 0.655477890177557
0.757740130578333 0.392227019534168 0.171186687811562
Y(:,:,3) =
0.706046088019609 0.27692298496089 0.0971317812358475
0.0318328463774207 0.0461713906311539 0.823457828327293

Yes, this is available using cell arrays.
if you have as stated three matrices A,B,C of size n x m.
to assign them to Y your code should be.
Y{1,1} = A;
Y{2,1} = B;
Y{3,1} = C;
As you see cell arrays use {} instead of ().
For more information about cell arrays visit Matlab help

Related

MATLAB: efficient generating of block matrices using a block vector

Suppose
x = [x1;x2; ...; xn]
where each xi is a column vector with length l(i). We can set L = sum(l), the total length of x. I would like to generate 2 matrices based on x:
Let's call them A and B. For example, when x only as 2 blocks x1 and x2 then:
A = [x1*x1' zeros(l(1),l(2)); zeros(l(2),l(1)), x2*x2'];
B = [x1 zeros(l(1),1);
zeros(l(2),1), x2];
In the notation of the problem, A is always L by L and B is L by n. I can generate A and B given x using loops but it is tedious. Is there a clever (loop-free) way to generate A and B. I am using MATLAB 2018b but you can assume earlier version of MATLAB if necessary.
I think it is both short and fast:
B = x .* (repelem((1:numel(l)).',l)==(1:numel(l)));
A = B * B.';
If you have large data It is better to use sparse matrix:
B = sparse(1:numel(x), repelem(1:numel(l), l), x);
A = B * B.';
The following should work. In this case I do an inefficient conversion to cell arrays so there may be a more efficient implementation possible.
cuml = [0; cumsum(l(:))];
get_x = #(idx) x((1:l(idx))+cuml(idx));
x_cell = arrayfun(get_x, 1:numel(l), 'UniformOutput', false);
B = blkdiag(x_cell{:});
A = B*B';
Edit
After running some benchmarks I found a direct loop based implementation to be about twice as fast as the cell based approach above.
A = zeros(sum(l));
B = zeros(sum(l), numel(l));
prev = 0;
for idx = 1:numel(l)
xidx = (1:l(idx))+prev;
A(xidx, xidx) = x(xidx,1) * x(xidx,1)';
B(xidx, idx) = x(idx,1);
prev = prev + l(idx);
end
Here's an alternative approach:
s = repelem(1:numel(l), l).';
t = accumarray(s, x, [], #(x){x*x'});
A = blkdiag(t{:});
t = accumarray(s, x, [], #(x){x});
B = blkdiag(t{:});

Tensor multiplication w/o looping in Matlab

I have a 3d array A, e.g. A=rand(N,N,K).
I need an array B s.t.
B(n,m) = norm(A(:,:,n)*A(:,:,m)' - A(:,:,m)*A(:,:,n)','fro')^2 for all indices n,m in 1:K.
Here's the looping code:
B = zeros(K,K);
for n=1:K
for m=1:K
B(n,m) = norm(A(:,:,n)*A(:,:,m)' - A(:,:,m)*A(:,:,n)','fro')^2;
end
end
I don't want to loop through 1:K.
I can create an array An_x_mt of size NK x NK s.t.
An_x_mt equals A(:,:,n)*A(:,:,m)' for all n,m in 1:K by
An_x_mt = Ar*Ac_t;
with
Ac_t=reshape(permute(A,[2 1 3]),size(A,1),[]);
Ar=Ac_t';
How do I create an array Am_x_nt also of size NK x NK s.t.
Am_x_nt equals A(:,:,m)*A(:,:,n)' for all n,m in 1:K
so that I could do
B = An_x_mt - Am_x_nt
B = reshape(B,N,N,[]);
B = reshape(squeeze(sum(sum(B.^2,1),2)),K,K);
Thx
For those who can't/won't use mmx and want to stick to pure Matlab code, here's how you could do it. mat2cell and cell2mat functions are your friends:
[N,~,nmat]=size(A);
Atc = reshape(permute(A,[2 1 3]),N,[]); % A', N x N*nmat
Ar = Atc'; % A, N*nmat x N
Anmt_2d = Ar*Atc; % An*Am'
Anmt_2d_cell = mat2cell(Anmt_2d,N*ones(nmat,1),N*ones(nmat,1));
Amnt_2d_cell = Anmt_2d_cell'; % ONLY products transposed, NOT their factors
Amnt_2d = cell2mat(Amnt_2d_cell); % Am*An'
Anm = Anmt_2d - Amnt_2d;
Anm = Anm.^2;
Anm_cell = mat2cell(Anm,N*ones(nmat,1),N*ones(nmat,1));
d = cellfun(#(c) sum(c(:)), Anm_cell); % squared Frobenius norm of each product; nmat x nmat
Alternatively, after computing Anmt_2d_cell and Amnt_2d_cell, you could convert them to 3d with the 3rd dimension encoding the (n,m) and (m,n) indices and then do the rest of the computations in 3d. You would need the permn() utility from here https://www.mathworks.com/matlabcentral/fileexchange/7147-permn-v-n-k
Anmt_3d = cat(3,Anmt_2d_cell);
Amnt_3d = cat(3,Amnt_2d_cell);
Anm_3d = Anmt_3d - Amnt_3d;
Anm_3d = Anm_3d.^2;
Anm = squeeze(sum(sum(Anm_3d,1),2));
d = zeros(nmat,nmat);
nm=permn(1:nmat, 2); % all permutations (n,m) with repeat, by-row order
d(sub2ind([nmat,nmat],nm(:,1),nm(:,2))) = Anm;
For some reason, the 2nd option (3D arrays) is twice faster.
Hopes this helps.

Inserting matrix into another matrix with Matlab

I want to put a small matrix (p-by-q) called B into a bigger matrix (m-by-n) called A. How can I do it? Matrix B should be put on the right corner of matrix A:
You can do this with basic array indexing, for example:
m = 3;
n = 4;
A = rand(m,n)
p = 2;
q = 3;
B = rand(p,q)
A(end-p+1:end,end-q+1:end) = B
... assuming that p <= m and q <= n of course.

MATLAB - vectorize iteration over two matrices used in function

I have two matrices X and Y, both of order mxn. I want to create a new matrix Z of order mx1 such that each i th entry in this new matrix is computed by applying a function to ith and ith row of X and Y respectively. In my case m = 100000 and n = 2. I tried using a loop but it takes forever.
for i = 1:m
Z = function(X(1,:),Y(1,:), constant_parameters)
end
Is there an efficient way to vectorize it?
EDIT 1
This is the function
function [peso] = fxPesoTexturaCN(a,b, img, r, L)
ac = num2cell(a);
bc = num2cell(b);
imgint1 = img(sub2ind(size(img),ac{:}));
imgint2 = img(sub2ind(size(img),bc{:}));
peso = (sum((a - b) .^ 2) + (r/L) * (imgint2 - imgint1)) / (2*r^2);
Where img, r, L are constats. a is X(1,:) and b is Y(1,:)
And the call of this function is
peso = bsxfun(#(a,b) fxPesoTexturaCN(a,b,img,r,L), a, b);

Replace the diagonal of matrix

I am interesting to replace the diagonal of matrix D to 1,2,3,4.
This is matrix D:
A=[1,2,3,4,2,3,4,5; 3,4,5,6,4,5,6,7];
D=[A;A];
D=[D D]; % size of matrix [4x16] %
To set the main diagonal to integers starting a 1 and incrementing by 1:
D(eye(4)==1) = 1:4
Or to generalize it:
n = min(size(D));
D(eye(n)==1) = 1:n;
note here that the ==1 is to convert the output of eye(n), the identity matrix, to type logical.
EDIT:
This is just a guess at what you mean by all the diagonals but here goes:
n = size(D,1);
m = size(D,2);
I = repmat(eye(min([n,m])), ceil(n/m), ceil(m/n));
I = I(1:n, 1:m)==1
d = repmat(1:min([n,m]), 1, max([ceil(n/m), ceil(m/n)]));
d = d(1:max(m,n));
D(I) = d