Mixing 3D arrays into a bigger 3D array - matlab

I wish to fill a N x M x W matrix ‘S’ with the data from matrices ‘P’ and ‘Q’. They are defined below and illustrated in the attached image. Also, we know for sure that n_1 + n_2 = N, m < M, so all the data may fit in the ‘S’ matrix.
S = zeros(M,N,W);
P = rand(m,n_1,W);
Q = rand(m,n_2,W);
I wish to combine ‘P’ and ‘Q’ in a manner specified by 3 other matrices, ‘Line_num’, ‘P_col’ and ‘Q_col’, described below and in the middle part of the attached image.
P_col = randperm(N); P_col = P_col(1:n_1); % 1 x n_1 matrix
Q_col = setxor(P_col, 1:1:N); % 1 x n_2 matrix
Line_num is a matrix composed of W vectors of the form aa:1:bb, where bb-aa = m and aa is chosen at random for each vector.
The important thing is that in this case the data along the 3rd dimension in all these matrixes represent W different test cases, with the data being different and not to be mixed between each other.
To fill ‘S’ one may proceed in two logical steps (although if it can be done in one I shall be glad)
combine Q and P into an intermediate matrix Y of shape m x N x W by
interweaving their columns. The columns specified in ‘Q_col’ are
taken from Q (using the vector index) and put in the matrix Y (using
the vector value). The same goes for P.
For each of the W vectors composing Line_num and arrays composing S,
use the values in the vector Line_num to spread out Y to the
corresponding rows in S, meanwhile maintaining their top to bottom
order.
I wish to achieve this without for-loops as I am looking to ‘vectorize’ my code and thus improve its running speed.
I have had a look at this post and this post, which are similar to what I desire. However they are simpler as the numbers to be extracted are constant. Maybe something similar would be appropriate?
Thank you for your help :)
Link to the image aforementioned
EDIT: here is an example code with a for-loop of what I want (my problem is that I want to get rid of the loop)
W = 4;
N = 10; n_1 = 4; n_2 = 6;
M = 20; m = 5;
P_col = [1,3,5,8]; % 1 x n_1 matrix
Q_col = setxor(P_col, 1:1:N); % 1 x n_2 matrix
line_num(:,:,1) = [1,5,10,15,18];
line_num(:,:,2) = [2,3,8,11,12];
line_num(:,:,3) = [4,7,8,14,19];
line_num(:,:,4) = [2,6,13,15,16];
S = zeros(M,N,W);
P = rand(m,n_1,W);
Q = rand(m,n_2,W);
for w=1:W
line_num_I = line_num(:,:,w);
S(line_num_I,Q_col,w) = Q(:,:,w);
S(line_num_I,P_col,w) = P(:,:,w);
end

Here is a vectorized solution. I'm not sure if it is more efficient than loop version specially when the size of data is large.
S ( reshape(line_num,[],1,W) ...
+ ([Q_col-1 P_col-1]) * M ...
+ (reshape(0:W-1,1,1,[]))*M*N ...
) ...
= ...
[reshape(Q,[],W);reshape(P,[],W)];
Here implicit expansion is used to convert subscripts to indices. Equivalently bsxfun can be used to compute linear indices:
S ( ...
bsxfun(#plus, ...
reshape(line_num,[],1,W), ...
bsxfun (#plus, ...
([Q_col-1 P_col-1]) * M, ...
(reshape(0:W-1,1,1,[]))*M*N ...
) ...
) ...
) ...
= ...
[reshape(Q,[],W);reshape(P,[],W)];
*Here You can find how to convert 3D index to lindex.

So I ended up finding the answer. For those of you that it may interest, the above for-loop may be replaced by
% 1. Combine columns
mixed_col = zeros(m,N,W);
mixed_col(:,Q_col,:) = Q(:,:,:);
mixed_col(:,P_col,:) = P(:,:,:);
mixed_col = permute(mixed_col,[2,1,3]); % turn 3D matrix into 2D [1]
mixed_col = reshape(mixed_col,N,[],1)';
% 2. Combine lines
S = reshape(S,M*w,N,1); % turn 3D matrix into 2D [2]
line_num_v = permute(line_num + reshape((0:1:(W-1)).*M,1,1,W),[2,1,3]); % turn 3D matrix into 2D [3]
line_num_v = reshape(line_num_v,[],1,1);
S(line_num_v,:) = mixed_col(:,:); % combine using three 2D matrices
S = permute(reshape(S',N,M,W),[2,1,3]);
This involves lots of reshaping but I don't have a simpler answer.
Thanks again for your help.

Related

Merge matrixes that are generated by function [duplicate]

In MatLab, I have a matrix SimC which has dimension 22 x 4. I re-generate this matrix 10 times using a for loop.
I want to end up with a matrix U that contains SimC(1) in rows 1 to 22, SimC(2) in rows 23 to 45 and so on. Hence U should have dimension 220 x 4 in the end.
Thank you!!
Edit:
nTrials = 10;
n = 22;
U = zeros(nTrials * n , 4) %Dimension of the final output matrix
for i = 1 : nTrials
SimC = SomeSimulation() %This generates an nx4 matrix
U = vertcat(SimC)
end
Unfortunately the above doesn't work as U = vertcat(SimC) only gives back SimC instead of concatenating.
vertcat is a good choice, but it will result in a growing matrix. This is not good practice on larger programs because it can really slow down. In your problem, though, you aren't looping through too many times, so vertcat is fine.
To use vertcat, you would NOT pre-allocate the full final size of the U matrix...just create an empty U. Then, when invoking vertcat, you need to give it both matrices that you want to concatenate:
nTrials = 10;
n = 22;
U = [] %create an empty output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
U = vertcat(U,SimC); %concatenate the two matrices
end
The better way to do this, since you already know the final size, is to pre-allocate your full U (as you did) and then put your values into U via computing the correct indices. Something like this:
nTrials = 10;
n = 22;
U = U = zeros(nTrials * n , 4); %create a full output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
indices = (i-1)*n+[1:n]; %here are the rows where you want to put the latest output
U(indices,:)=SimC; %copies SimC into the correct rows of U
end

Select an element of a Sub matrix

I have a Matrix of 100 sub matrix . Each of this sub matrix have 6 elements (1*6),
I need to compute the mean of the first element of each sub matrix then the
second, etc
Example:
B=[4,**3**,2,1,1,2]
C=[4,**3**,5,1,1,2]
D=[6,**3**,2,1,1,2]
A={B,C,D}
...etc
So I want the mean of the surlined numbers, then the next etc
How can I do that ???
Thanks by advance,
i think what you need here is the command cell2mat. here a small script of how to compute means automatically without knowing the size of the data. let me know if that was what you were looking for.
% Problem
vec1 = [4,3,2,1,1,2];
vec2 = [4,3,5,1,1,2];
vec3 = [6,3,2,1,1,2];
A = {vec1,vec2,vec3};
% get dimensions
cols = numel(cell2mat(A(1)));
rows = numel(A);
% convert list of vectors to matrix
M = cell2mat(A);
M = reshape(M,[cols,rows]);
M = M';
means = mean(M)

Vectorization of double for loop including sine of two variables

I need to numerically evaluate some integrals which are all of the form shown in this image:
These integrals are the matrix elements of a N x N matrix, so I need to evaluate them for all possible combinations of n and m in the range of 1 to N. The integrals are symmetric in n and m which I have implemented in my current nested for loop approach:
function [V] = coulomb3(N, l, R, R0, c, x)
r1 = 0.01:x:R;
r2 = R:x:R0;
r = [r1 r2];
rl1 = r1.^(2*l);
rl2 = r2.^(2*l);
sines = zeros(N, length(r));
V = zeros(N, N);
for i = 1:N;
sines(i, :) = sin(i*pi*r/R0);
end
x1 = length(r1);
x2 = length(r);
for nn = 1:N
for mm = 1:nn
f1 = (1/6)*rl1.*r1.^2.*sines(nn, 1:x1).*sines(mm, 1:x1);
f2 = ((R^2/2)*rl2 - (R^3/3)*rl2.*r2.^(-1)).*sines(nn, x1+1:x2).*sines(mm, x1+1:x2);
value = 4*pi*c*x*trapz([f1 f2]);
V(nn, mm) = value;
V(mm, nn) = value;
end
end
I figured that calling sin(x) in the loop was a bad idea, so I calculate all the needed values and store them. To evaluate the integrals I used trapz, but as the first and the second/third integrals have different ranges the function values need to be calculated separately and then combined.
I've tried a couple different ways of vectorization but the only one that gives the correct results takes much longer than the above loop (used gmultiply but the arrays created are enourmous). I've also made an analytical solution (which is possible assuming m and n are integers and R0 > R > 0) but these solutions involve a cosine integral (cosint in MATLAB) function which is extremely slow for large N.
I'm not sure the entire thing can be vectorized without creating very large arrays, but the inner loop at least should be possible. Any ideas would be be greatly appreciated!
The inputs I use currently are:
R0 = 1000;
R = 8.4691;
c = 0.393*10^(-2);
x = 0.01;
l = 0 # Can reasonably be 0-6;
N = 20; # Increasing the value will give the same results,
# but I would like to be able to do at least N = 600;
Using these values
V(1, 1:3) = 873,379900963549 -5,80688363271849 -3,38139152472590
Although the diagonal values never converge with increasing R0 so they are less interesting.
You will lose the gain from the symmetricity of the problem with my approach, but this means a factor of 2 loss. Odds are that you'll still benefit in the end.
The idea is to use multidimensional arrays, making use of trapz supporting these inputs. I'll demonstrate the first term in your figure, as the two others should be done similarly, and the point is the technique:
r1 = 0.01:x:R;
r2 = R:x:R0;
r = [r1 r2].';
rl1 = r1.'.^(2*l);
rl2 = r2.'.^(2*l);
sines = zeros(length(r),N); %// CHANGED!!
%// V = zeros(N, N); not needed now, see later
%// you can define sines in a vectorized way as well:
sines = sin(r*(1:N)*pi/R0); %//' now size [Nr, N] !
%// note that implicitly r is of size [Nr, 1, 1]
%// and sines is of size [Nr, N, 1]
sines2mat = permute(sines,[1, 3, 2]); %// size [Nr, 1, N]
%// the first term in V: perform integral along first dimension
%//V1 = 1/6*squeeze(trapz(bsxfun(#times,bsxfun(#times,r.^(2*l+2),sines),sines2mat),1))*x; %// 4*pi*c prefactor might be physics, not math
V1 = 1/6*permute(trapz(bsxfun(#times,bsxfun(#times,r.^(2*l+2),sines),sines2mat),1),[2,3,1])*x; %// 4*pi*c prefactor might be physics, not math
The key point is that bsxfun(#times,r.^(2*l+2),sines) is a matrix of size [Nr,N,1], which is again multiplied by sines2mat using bsxfun, the result is of size [Nr,N,N] and an element (k1,k2,k3) corresponds to an integrand at radial point k1, n=k2 and m=k3. Using trapz() with explicitly the first dimension (which would be default) reduces this to an array of size [1,N,N], which is just what you need after a good squeeze(). Update: as per #Dev-iL's comment you should use permute instead of squeeze to get rid of the leading singleton dimension, as that might be more efficent.
The two other terms can be handled the same way, and of course it might still help if you restructure the integrals based on overlapping and non-overlapping parts.

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.
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 -
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!
Here's another approach:
Compute the products as a 3D array using bsxfun;
Convert into a cell array with one product (matrix) in each cell;
Call blkdiag with a comma-separated list generated from the cell array.
Let A denote your matrix, and a denote a vector with your constants. Then the desired result B is obtained as
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
Here's a method using kron which seems to be faster and more memory efficient than Divakar's bsxfun based solution. I'm not sure if this is different to your method, but the timing seems pretty good. It might be worth doing some testing between the different methods to work out which is more efficient for you problem.
A=magic(4);
a1=1;
a2=2;
a3=3;
kron(diag([a1 a2 a3]),A)

How can I build a Scilab / MATLAB program that averages a 3D matrix?

I need to make a scilab / MATLAB program that averages the values of a 3D matrix in cubes of a given size(N x N x N).I am eternally grateful to anyone who can help me.
Thanks in advance
In MATLAB, mat2cell and cellfun make a great team for working on N-dimensional non-overlapping blocks, as I think is the case in the question. An example scenario:
[IN]: A = [30x30x30] array
[IN]: bd = [5 5 5], size of cube
[OUT]: B = [6x6x6] array of block means
To accomplish the above, the solution is:
dims = [30 30 30]; bd = [5 5 5];
A = rand(dims);
f = floor(dims./bd);
remDims = mod(dims,bd); % handle dims that are not a multiple of block size
Ac = mat2cell(A,...
[bd(1)*ones(f(1),1); remDims(1)*ones(remDims(1)>0)], ....
[bd(2)*ones(f(2),1); remDims(2)*ones(remDims(2)>0)], ....
[bd(3)*ones(f(3),1); remDims(3)*ones(remDims(3)>0)] );
B = cellfun(#(x) mean(x(:)),Ac);
If you need a full size output with the mean values replicated, there is a straightforward solution involving the 'UniformOutput' option of cellfun followed by cell2mat.
If you want overlapping cubes and the same size output as input, you can simply do convn(A,ones(blockDims)/prod(blockDims),'same').
EDIT: Simplifications, clarity, generality and fixes.
N = 10; %Same as OP's parameter
M = 10*N;%The input matrix's size in each dimensiona, assumes M is an integer multiple of N
Mat = rand(M,M,M); % A random input matrix
avgs = zeros((M/N)^3,1); %Initializing output vector
l=1; %indexing
for i=1:M/N %indexing 1st coord
for j=1:M/N %indexing 2nd coord
for k=1:M/N % indexing third coord
temp = Mat((i-1)*N+1:i*N,(j-1)*N+1:j*N,(k-1)*N+1:k*N); %temporary copy
avg(l) = mean(temp(:)); %averaging operation on the N*N*N copy
l = l+1; %increment indexing
end
end
end
The for loops and copying can be eliminated once you get the gist of indexing.