This is the following part of the below:
2) Additional question:
After getting the average of the non-zero neighbors, I also want to test if the neighbor elements are equal, lesser, or greater than the average of the nonzeros. If it is greater or equal then '1' or else '0'.
Note: if the neighbors are with in the radius of the two or more centres, take the smallest centre average to test.
0 12 9
4 **9** 15
11 19 0
The '9' in the middle is within the radius of 12, 15, and 19 centres, so take the minimum average of those min[9.000, 9.000, 8.000]=8.000
For example, when radius = 1 m or 1 element away.
new_x =
0 0 0 0 0
0 0 **9.0000** 9.0000 0
0 4.0000 9.0000 **9.0000** 0
0 **8.3333** **8.0000** 0 0
0 2.0000 4.0000 8.0000 0
0 4.0000 5.0000 8.0000 0
0 0 0 0 0
Test_x =
0 0 0 0 0
0 0 **9.0000** 1 0
0 0 1 **9.0000** 0
0 **8.3333** **8.0000** 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
=================================================================================
1) Say if I have a matrix, shown as below,
X =
0 0 0 0 0
0 0 12 9 0
0 4 9 15 0
0 11 19 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
and I want to find the average of the surrounding non-zero elements that is greater than 10. The rest of the elements still remain the same i.e. elements < 10.
So I want my solution to look something like,
new_x =
0 0 0 0 0
0 0 9.0000 9.0000 0
0 4.0000 9.0000 9.0000 0
0 8.3333 8.0000 0 0
0 2.0000 4.0000 8.0000 0
0 4.0000 5.0000 8.0000 0
0 0 0 0 0
Not: that I am NOT only looking at the neighbors of the element thats greather than some value (i.e. 10 in this case).
Lets say any elements thats are greater than 10 are the 'centre' and we want to find the avearge of the non-zeros with the radius of say 1 m. where 1 metre = 1 element away from the centre.
Note: It might not always be 1 meter away in radius i.e. can be 2 or more. In this case it wont be just top, bottom, left and right of the centre.
****Also Be aware of the matrix boundary. For example, when radius = 2 or more, some of the average of nonzero neighbors are out side the boundary.**
For example,
For radius =1 m = 1 element away,
new_x = average of [(i+1,j) , (i-1,j) , (i,j+1) and (i,j-1)] - top, bottom, right, and left of the centre.
For radius =2 m = 2 elements away,
new_x = average of [(i+1,j), (i+2,j) , (i-1,j) , (i-2,j), (i,j+1), (i,j+2), (i,j-1), (i,j-2), (i+1,j+1), (i+1,j-1), (i-1,j-1), and (i-1,j+1)].
==================================================================
I have tried a few things before, however I am not familiar with the functions.
So please help me to solve the problem.
Thank you in advance.
EDIT:
Note this requires functions from the Image Processing Toolbox, namely: COLFILT and STREL
r = 1; %# radius
t = 10; %# threshold value
mid = round((2*r+1)^2/2); %# mid point
nhood = getnhood(strel('diamond', r));
nhood(mid) = false;
fcn = #(M)sum(M(nhood(:),:),1)./(sum(M(nhood(:),:)~=0)+all(M(nhood(:),:)==0)).*(M(mid,:)>=t)+M(mid,:).*(M(mid,:)<t);
new_x = colfilt(x, 2*[r r]+1, 'sliding',fcn)
For r=1:
new_x =
0 0 0 0 0
0 0 9 9 0
0 4 9 9 0
0 8.3333 8 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
For r=2:
new_x =
0 0 0 0 0
0 0 11.2 9 0
0 4 9 10.167 0
0 7 7.7778 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
In fact, it should work for any radius >= 1
Notice how the diamond shape structuring element represents the neighborhood:
nhood =
0 1 0
1 0 1
0 1 0
nhood =
0 0 1 0 0
0 1 1 1 0
1 1 0 1 1
0 1 1 1 0
0 0 1 0 0
and so on..
Explanation:
We use the COLFILT function which traverse the matrix using a sliding neighborhood of NxN, and places each block as a column in a temporary matrix.
We process each column of this temp matrix (blocks) using the function fcn, and the result will be placed in the correct location once finished (COLFILT uses IM2COL and COL2IM underneath).
We check for two cases depending of the value of the center of the block:
If its less than 10, it returns that value unchanged: M(mid,:)
if its >=10, we compute the mean of the non-zero elements of its neighborhood
sum(M(nhood(:),:),1) ./ (sum(M(nhood(:),:)~=0) + all(M(nhood(:),:)==0)).
The last term in there is necessary to avoid dividing by zero
Notice how the result of 1 & 2 above are combined using R1.*(M(mid,:)<t) + R2.*(M(mid,:)>=t) to emulate an if/else choice.
Here is the algorithm I think you are describing in your question. For each pixel:
If the pixel value is less than 10, do nothing.
If the pixel value is greater than or equal to 10, replace the pixel value by the average of the non-zero 4-connected nearest neighbors.
If this is correct (as it appears to be from the sample matrices you gave), then you could use the function NLFILTER from the Image Processing Toolbox (if you have access to it) to perform this operation:
fcn = #(x) [x(5) sum(x(2:2:8))/max(sum(x(2:2:8) > 0),1)]*[x(5) < 10; x(5) >= 10];
new_x = nlfilter(X,[3 3],fcn);
EDIT: If you don't have access to the Image Processing Toolbox, you can also do this using the built-in CONV2 function, like so:
kernel = [0 1 0; ... %# Convolution kernel
1 0 1; ...
0 1 0];
sumX = conv2(X,kernel,'same'); %# Compute the sum of neighbors
%# for each pixel
nX = conv2(double(X > 0),kernel,'same'); %# Compute the number of non-zero
%# neighbors for each pixel
index = (X >= 10); %# Find logical index of pixels >= 10
new_x = X; %# Initialize new_x
new_x(index) = sumX(index)./max(nX(index),1); %# Replace the pixels in index
%# with the average of their
%# non-zero neighbors
The above handles your radius = 1 case. To address your radius = 2 case, you just have to change the convolution kernel to the following and rerun the above code:
kernel = [0 0 1 0 0; ...
0 1 1 1 0; ...
1 1 0 1 1; ...
0 1 1 1 0; ...
0 0 1 0 0];
You could do something like this: (tested in Octave, should work in matlab)
octave-3.2.3:17> toohigh = (x>=10)
toohigh =
0 0 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
octave-3.2.3:18> nbr_avg = filter2(ones(3,3)/9,x)
nbr_avg =
0.00000 1.33333 2.33333 2.33333 1.00000
0.44444 2.77778 5.44444 5.00000 2.66667
1.66667 6.11111 8.77778 7.11111 2.66667
1.88889 5.44444 8.00000 6.11111 2.55556
1.88889 5.00000 6.77778 4.88889 1.77778
0.66667 1.66667 3.44444 2.77778 1.77778
0.44444 1.00000 1.88889 1.44444 0.88889
octave-3.2.3:19> y=x; y(toohigh) = nbr_avg(toohigh)
y =
0.00000 0.00000 0.00000 0.00000 0.00000
0.00000 0.00000 5.44444 9.00000 0.00000
0.00000 4.00000 9.00000 7.11111 0.00000
0.00000 5.44444 8.00000 0.00000 0.00000
0.00000 2.00000 4.00000 8.00000 0.00000
0.00000 4.00000 5.00000 8.00000 0.00000
0.00000 0.00000 0.00000 0.00000 0.00000
The filter2 function allows you to filter on neighbors (not sure what function you want...), and if you use a boolean index matrix (toohigh in this case) to select those members of the original matrix that are too high, you can replace them with the ones you want.
More specifically, filter2 allows you to convolve with an arbitrary matrix. The matrix of all ones does a spatial low pass filter.
note: my math doesn't match yours. I'm not quite sure why you want to average only the nonzero neighbors (that gives higher weight to nonzero neighbors when there are zeros), but if you wanted to do that, you could do filter2(ones(3,3),x) ./ M where M = filter2(ones(3,3),(x ~= 0)) is the count of nonzero neighbors.
EDIT: Here is a solution that does not require the Image Processing Toolbox. It does, however, use conv2nan.m which is part of the free NaN toolbox.
This approach relies on doing two different filtering/convolution operations: one that gets the sum of surrounders for each element, and one that gets the count of nonzero surrounders. Then, you are ready to combine them to get the average of nonzero surrounders only. Like this:
% set up starting point matrix with some zeros
X = magic(4);
X(X < 5) = 0;
X(X == 0) = NaN; % convert zeros to NaNs to work with conv2nan
countmat = double(X > 0);
cmat = [0 1 0;
1 0 1;
0 1 0]; % consider surrounding elements only
[m1,c] = conv2nan(X,cmat); % sum of surrounding elements
[m2,c] = conv2nan(countmat,cmat); % number of surrounding elements > 0
x_new = m1./m2; % compute average we want
x_new = x_new(2:end-1,2:end-1); % trim edges created by conv2
x_new(~countmat) = 0; % restore zero elements
x_new(X < 10) = X(X < 10) % restore < 10 elements
It does some extra work in that the convolutions are done for all elements and not just those that are >= 10. But it's more general than the manual looping approach.
Related
I need to create all possible permutation matrices for a matrix where every permutation matrix contains only one 1 in each column and each row, and 0 in all other places.
For example, below example in (1) is all possible permutation matrices for 2x2 matrix and in (2) is a all possible permutation matrices for 3x3 matrix and so on
So how can I get these matrices of a matrix NxN in MATLAB and store them into one three-dimensional matrix?
Here's my solution, using implicit expansion (tested with Octave 5.2.0 and MATLAB Online):
n = 3;
% Get all permutations of length n
p = perms(1:n);
% Number of permutations
n_p = size(p, 1);
% Set up indices, where to set elements to 1
p = p + (0:n:n^2-1) + (0:n^2:n^2*n_p-1).';
% Set up indices, where to set elements to 1 (for MATLAB R2016a and before)
%p = bsxfun(#plus, bsxfun(#plus, p, (0:n:n^2-1)), (0:n^2:n^2*n_p-1).');
% Initialize 3-dimensional matrix
a = zeros(n, n, n_p);
% Set proper elements to 1
a(p) = 1
The output for n = 3:
a =
ans(:,:,1) =
0 0 1
0 1 0
1 0 0
ans(:,:,2) =
0 1 0
0 0 1
1 0 0
ans(:,:,3) =
0 0 1
1 0 0
0 1 0
ans(:,:,4) =
0 1 0
1 0 0
0 0 1
ans(:,:,5) =
1 0 0
0 0 1
0 1 0
ans(:,:,6) =
1 0 0
0 1 0
0 0 1
Using repelem, perms and reshape:
n = 3; % matrix size
f = factorial(n); % number of permutation
rep = repelem(eye(n),1,1,f) % repeat n! time the diagonal matrix
res = reshape(rep(:,perms(1:n).'),n,n,f) % indexing and reshaping
Where res is:
res =
ans(:,:,1) =
0 0 1
0 1 0
1 0 0
ans(:,:,2) =
0 1 0
0 0 1
1 0 0
ans(:,:,3) =
0 0 1
1 0 0
0 1 0
ans(:,:,4) =
0 1 0
1 0 0
0 0 1
ans(:,:,5) =
1 0 0
0 0 1
0 1 0
ans(:,:,6) =
1 0 0
0 1 0
0 0 1
And according to your comment:
What I need to do is to multiply a matrix i.e Z with all possible
permutation matrices and choose that permutation matrix which
resulting a tr(Y) minimum; where Y is the results of multiplication of
Z with the permutation matrix. I Think I don't need to generate all
permutation matrices and store them in such variable, I can generate
them one by one and get the result of multiplication. Is that possible
?
You're trying to solve the assignment problem, you can use the well known hungarian algorithm to solve this task in polynomial time. No needs to generate a googleplex of permutation matrix.
I have a sequence x= [12,14,6,15,15,15,15,6,8,8,18,18,14,14] so I want to make transition probability matrix. Transition probability matrix calculated by equation i.e. probability=(number of pairs x(t) followed by x(t+1))/(number of pairs x(t) followed by any state). Matrix should be like below
6 8 12 14 15 18
6 0 1/2 0 0 1/2 0
8 0 1/2 0 0 0 1/2
12 0 0 0 1 0 0
14 1/2 0 0 1/2 0 0
15 1/4 0 0 0 3/4 0
18 0 0 0 0 1/2 1/2
by following code I can do
m = max(x);
n = numel(x);
y = zeros(m,1);
p = zeros(m,m);
for k=1:n-1
y(x(k)) = y(x(k)) + 1;
p(x(k),x(k+1)) = p(x(k),x(k+1)) + 1;
end
p = bsxfun(#rdivide,p,y); p(isnan(p)) = 0;
but with this code matrix forms of order maximum state present in sequence i.e. matrix becomes of 18*18, and much more places zero occurs. I want matrix like above posted by me how to do it.
Step 1 - organize data and generate empty transition table
x= [12,14,6,15,15,15,15,6,8,8,18,18,14,14]
xind = zeros(1,length(x));
u = unique(x) % find unique elements and sort
for ii = 1:length(u)
xmask = x==u(ii); % locate all elements of a single value
xind = xind+ii*xmask; % number them in the order listed in u
end
Output is labeled Markov chain (elements are labels instead of meaningful values)
>> u
u =
6 8 12 14 15 18
>> xind
xind =
3 4 1 5 5 5 5 1 2 2 6 6 4 4
Step 2 - build "from-to" table for each hop
>> T = [xind(1:end-1);xind(2:end)]
T =
3 4 1 5 5 5 5 1 2 2 6 6 4
4 1 5 5 5 5 1 2 2 6 6 4 4
Each column is a transition. First row is "from" label, second row is "to" label.
Step 3 - count frequencies and create transition table
p = zeros(length(u));
for ii = 1:size(T,2)
px = T(1,ii); % from label
py = T(2,ii); % to label
p(px,py) = p(px,py)+1;
end
Output is aggregated frequency table. Each element is counts of a hop. Row number is "from" and column number is "to".
>> p
p =
0 1 0 0 1 0
0 1 0 0 0 1
0 0 0 2 0 0
2 0 0 1 0 0
1 0 0 0 3 0
0 0 0 1 0 1
For example the 3 means 3 transitions from 5th label to 5th label (actual value is 15 to 15)
Step 4 - normalize row vectors to get probability table
>> p./repmat(sum(p,2),1,length(u))
ans =
0 0.5000 0 0 0.5000 0
0 0.5000 0 0 0 0.5000
0 0 0 1.0000 0 0
0.5000 0 0 0.5000 0 0
0.2500 0 0 0 0.7500 0
0 0 0 0.5000 0 0.5000
alternative loop version
for ii = 1:size(p,1)
count = sum(p(ii,:));
p(ii,:) = p(ii,:)/count;
end
x=[12,14,6,15,15,15,15,6,8,8,18,18,14,14]; %discretized driving cycle
n=numel(x);%total no of data points in driving cycle
j=0;
z=unique(x); % unique data points in the driving cycle and also in arranged form
m=numel(z); % total number of unique data points
N=zeros(m); % square matrix for counting all unique data points
for k=1:1:m; % using loop cycle for unique data points all combinations; for this k is used
for l=1:1:m;
for i=1:1:n-1;
j=i+1;
if x(i)== z(k) & x(j)==z(l);
N(k,l) = N(k,l)+1;
end
i=i+1;
end
l=l+1;
end
k=k+1;
end
N
s=sum(N,2);
Tpm= N./s %transition probability matrix
%%Sample matrix
p=magic(8)
%%Fill rows and cols 3,5 with 0's
p([3 5],:)=0
p(:,[3 5])=0
%%The code
lb=[]
for k = [length(p):-1:1]
if any(p(k,:)) | any(p(:,k))
lb=[ [k],lb ]
else
p(k,:)=[]
p(:,k)=[]
end
end
lb keeps your original index
I know that to generate a block-diagonal matrix in Matlab the command blkdiag generates such a matrix:
Now I am faced with generating the same block-diagonal matrix, but with also matrix elements B_1, B_2,..., B_{n-1} on the upper diagonal, zeros elsewhere:
I guess this can be hardcoded with loops, but I would like to find a more elegant solution. Any ideas on how to implement such a thing?
P.S. I diag command, that using diag(A,k) returns the kth diagonal. I need something for writing in the matrix, for k>0, and for block matrices, not only elements.
There is a submission on the File Exchange that can do this:
(Block) tri-diagonal matrices.
You provide the function with three 3D-arrays, each layer of the 3D array represents a block of the main, sub- or superdiagonal. (Which means that the blocks will have to be of the same size.) The result will be a sparse matrix, so it should be rather efficient in terms of memory.
An example usage would be:
As = bsxfun(#times,ones(3),permute(1:3,[3,1,2]));
Bs = bsxfun(#times,ones(3),permute(10:11,[3,1,2]));
M = blktridiag(As, zeros(size(Bs)), Bs);
where full(M) gives you:
1 1 1 10 10 10 0 0 0
1 1 1 10 10 10 0 0 0
1 1 1 10 10 10 0 0 0
0 0 0 2 2 2 11 11 11
0 0 0 2 2 2 11 11 11
0 0 0 2 2 2 11 11 11
0 0 0 0 0 0 3 3 3
0 0 0 0 0 0 3 3 3
0 0 0 0 0 0 3 3 3
This could be one approach based on kron, tril & triu -
%// Take all A1, A2, A3, etc in a cell array for easy access and same for B
A = {A1,A2,A3,A4}
B = {B1,B2,B3}
%// Setup output array with the A blocks at main diagonal
out = blkdiag(A{:})
%// logical array with 1s at places where kth diagonal elements are to be put
idx = kron(triu(true(numel(A)),k) & tril(true(numel(A)),k),ones(size(A{1})))>0
%// Put kth diagonal blocks using the logical mask
out(idx) = [B{1:numel(A)-k}]
Sample run with k = 1 for 2 x 2 sizes matrices -
>> A{:}
ans =
0.3467 0.7966
0.6228 0.7459
ans =
0.1255 0.0252
0.8224 0.4144
ans =
0.7314 0.3673
0.7814 0.7449
ans =
0.8923 0.1296
0.2426 0.2251
>> B{:}
ans =
0.3500 0.9275
0.2871 0.0513
ans =
0.5927 0.8384
0.1629 0.1676
ans =
0.5022 0.3554
0.9993 0.0471
>> out
out =
0.3467 0.7966 0.3500 0.9275 0 0 0 0
0.6228 0.7459 0.2871 0.0513 0 0 0 0
0 0 0.1255 0.0252 0.5927 0.8384 0 0
0 0 0.8224 0.4144 0.1629 0.1676 0 0
0 0 0 0 0.7314 0.3673 0.5022 0.3554
0 0 0 0 0.7814 0.7449 0.9993 0.0471
0 0 0 0 0 0 0.8923 0.1296
0 0 0 0 0 0 0.2426 0.2251
I have a A which is 640x1 cell. where the value of each cell A(i,1) varies from row to row, for example A(1,1) =[], while A(2,1)=[1] and A(3,1)=[1,2,3].
There is another matrix B of size 480x640, where the row_index (i) of vector A corresponds to the col_index of matrix B. While the cell value of each row in vector A corresponds to the row_index in matrix B. For example, A(2,1)=[1] this means col_2 row_1 in matrix B, while A(3,1)=[1,2,3] means col_3 rows 1,2&3 in matrix B.
What I'm trying to do is to for each non-zero value in matrix B that are referenced from vector A, I want to check whether there are at least 4 other neighbors that are also referenced from vector A. The number neighbors of each value are determined by a value N.
For example, this is a part of matrix B where all the zeros"just to clarify, as in fact they may be non-zeros" are the neighbors of pixel X when N=3:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 X 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
As shown, because N=3, all these zeros are pixel X's neighbors. So if more than 4 neighbor pixels are found in vector A then do something e.g G=1 if not then G=0;
So if anyone could please advise. And please let me know if any more clarification is needed.
The first thing I would do is to convert your cell of indices A to a logic matrix Amat. This makes it easier to check how many neighbours are included in A.
Here is a solution that uses this conversion. I hope the comments are enough to make it understandable.
clear all
clc
nCols = 7;
nRows = 6;
N = 3; %// Number of neighbours
M = 4; %// Minimum number of wanted connections
%// Create cell of indices A
A = cell(nCols,1);
A{1} = [];
A{2} = 1;
A{3} = [1 2 3];
A{4} = [2 5];
A{5} = 3;
A{6} = [3 5];
A{7} = [1 4 6];
%// Generate radom data B
%// (There is a 50% probability for each element of B to be zero)
Bmax = 17;
B = (randi(2,nRows,nCols)-1).*(randi(Bmax,nRows,nCols));
%// Convert the cell A to a logic matrix Amat
Amat = zeros(size(B));
for ii = 1:nCols
Amat(A{ii},ii) = 1;
end
A
B
Amat
for ii = 1:nCols
for jj = A{ii}
if B(jj,ii)>0
%// Calculate neighbour indices with a lower bound of 1
%// and an upper bound of nCols or nRows
col_lim_low = max(1,ii-N);
col_lim_high = min(nCols,ii+N);
row_lim_low = max(1,jj-N);
row_lim_high = min(nRows,jj+N);
%// Get the corresponding neighbouring-matrix from Amat
A_neighbours = ...
Amat(row_lim_low:row_lim_high,col_lim_low:col_lim_high);
%// Check the number of neighbours against the wanted number M
if sum(A_neighbours(:)) > 1 + M
%# do something
fprintf('We should do something here at (%d,%d)\n',jj,ii)
end
end
end
end
The following is a printout from one run of the code.
A =
[]
[ 1]
[1x3 double]
[1x2 double]
[ 3]
[1x2 double]
[1x3 double]
B =
1 5 0 0 11 0 16
0 13 13 0 0 0 9
0 0 0 5 0 0 0
3 8 16 16 0 2 12
0 0 5 0 9 9 0
12 13 0 6 0 15 0
Amat =
0 1 1 0 0 0 1
0 0 1 1 0 0 0
0 0 1 0 1 1 0
0 0 0 0 0 0 1
0 0 0 1 0 1 0
0 0 0 0 0 0 1
We should do something here at (1,2)
We should do something here at (2,3)
We should do something here at (5,6)
We should do something here at (4,7)
Since you have a one-to-one correspondence between A and B, there is no need to work on A. B is a logical matrix (0 if not referenced in A, 1 if referenced). You can therefore apply a simple filter2 function counting the number of active neighbors within the 8 closest elements.
Here is the code
B = rand(10,10); %generate binary matrix
h = [1 1 1;1 0 1;1 1 1]; %filter to be applied
filter2(h,B,'same')>=4 & B>0 %apply filter on B, count minimum of 4 neighbors, if only B>1
EDIT
To transform a cell array B into binary presence (0=empty, 1=not empty), use of cellfunis straightforward
B = ~cellfun(#isempty,B);
And see Armo's response to your previous question for how to create B based on A.
I am trying to multiply the (2x2) sub-matrices of a large (2x2m) matrix together, in a "vectorized" fashion in order to eliminate for loops and increase speed. Currently, I reshape to a (2x2xm) then use a for loop to do this:
for n = 1:1e5
m = 1e4;
A = rand([2,2*m]); % A is a function of n
A = reshape(A,2,2,[]);
B = eye(2);
for i = 1:m
B = A(:,:,i)*B; % multiply the long chain of 2x2's
end
end
The function goal is similar to #prod, but with matrix multiplication instead of element-wise scalar multiplication. #multiprod seems close, but takes two different nD matrices as arguments. I imagine a solution using multiple submatrices of a very large 2D array, or a single 2x2m{xn} array to eliminate one or both for loops.
Thanks in advance, Joe
I think you have to reshape your matrix in different way to do the vectorized multiplication, like in the code below. This code also uses loop, but I think should be faster
MM = magic(2);
M0 = MM;
M1 = rot90(MM,1);
M2 = rot90(MM,2);
M3 = rot90(MM,3);
MBig1 = cat(2,M0,M1,M2,M3);
fprintf('Original matrix\n')
disp(MBig1)
MBig2 = zeros(size(MBig1,2));
MBig2(1:2,:) = MBig1;
for k=0:3
c1 = k *2+1;
c2 = (k+1)*2+0;
MBig2(:,c1:c2) = circshift(MBig2(:,c1:c2),[2*k 0]);
end
fprintf('Reshaped original matrix\n')
disp(MBig2)
fprintf('Checking [ M0*M0 M0*M1 M0*M2 M0*M3 ] in direct way\n')
disp([ M0*M0 M0*M1 M0*M2 M0*M3 ])
fprintf('Checking [ M0*M0 M0*M1 M0*M2 M0*M3 ] in vectorized way\n')
disp( kron(eye(4),M0)*MBig2 )
fprintf('Checking [ M0*M1*M2*M3 ] in direct way\n')
disp([ M0*M1*M2*M3 ])
fprintf('Checking [ M0*M1*M2*M3 ] in vectorized way\n')
R2 = MBig2;
for k=1:3
R2 = R2 * circshift(MBig2,-[2 2]*k);
end
disp(R2)
The output is
Original matrix
1 3 3 2 2 4 4 1
4 2 1 4 3 1 2 3
Reshaped original matrix
1 3 0 0 0 0 0 0
4 2 0 0 0 0 0 0
0 0 3 2 0 0 0 0
0 0 1 4 0 0 0 0
0 0 0 0 2 4 0 0
0 0 0 0 3 1 0 0
0 0 0 0 0 0 4 1
0 0 0 0 0 0 2 3
Checking [ M0*M0 M0*M1 M0*M2 M0*M3 ] in direct way
13 9 6 14 11 7 10 10
12 16 14 16 14 18 20 10
Checking [ M0*M0 M0*M1 M0*M2 M0*M3 ] in vectorized way
13 9 0 0 0 0 0 0
12 16 0 0 0 0 0 0
0 0 6 14 0 0 0 0
0 0 14 16 0 0 0 0
0 0 0 0 11 7 0 0
0 0 0 0 14 18 0 0
0 0 0 0 0 0 10 10
0 0 0 0 0 0 20 10
Checking [ M0*M1*M2*M3 ] in direct way
292 168
448 292
Checking [ M0*M1*M2*M3 ] in vectorized way
292 168 0 0 0 0 0 0
448 292 0 0 0 0 0 0
0 0 292 336 0 0 0 0
0 0 224 292 0 0 0 0
0 0 0 0 292 448 0 0
0 0 0 0 168 292 0 0
0 0 0 0 0 0 292 224
0 0 0 0 0 0 336 292
The function below may solve part of my probelm. It is named "mprod" vs. prod, similar to times vs. mtimes. With some reshaping, it uses multiprod recursively. In general, a recursive function call is slower than a loop. Multiprod claims to be >100x faster, so it should more than compensate.
function sqMat = mprod(M)
% Multiply *many* square matrices together, stored
% as 3D array M. Speed gain through recursive use
% of function 'multiprod' (Leva, 2010).
% check if M consists of multiple matrices
if size(M,3) > 1
% check for odd number of matrices
if mod(size(M,3),2)
siz = size(M,1);
M = cat(3,M,eye(siz));
end
% create two smaller 3D arrays
X = M(:,:,1:2:end); % odd pages
Y = M(:,:,2:2:end); % even pages
% recursive call
sqMat = mprod(multiprod(X,Y));
else
% create final 2D matrix and break recursion
sqMat = M(:,:,1);
end
end
I have not tested this function for speed or accuracy. I believe this is much faster than a loop. It does not 'vectorize' the operation since it cannot be used with higher dimensions; any repeated use of this function must be done within a loop.
EDIT Below is new code that seems to work fast enough. Recursive calls to functions are slow and eat up stack memory. Still contains a loop, but reduces the number of loops by log(n)/log(2). Also, added support for more dimensions.
function sqMats = mprod(M)
% Multiply *many* square matrices together, stored along 3rd axis.
% Extra dimensions are conserved; use 'permute' to change axes of "M".
% Speed gained by recursive use of 'multiprod' (Leva, 2010).
% save extra dimensions, then reshape
dims = size(M);
M = reshape(M,dims(1),dims(2),dims(3),[]);
extraDim = size(M,4);
% Check if M consists of multiple matrices...
% split into two sets and multiply using multiprod, recursively
siz = size(M,3);
while siz > 1
% check for odd number of matrices
if mod(siz,2)
addOn = repmat(eye(size(M,1)),[1,1,1,extraDim]);
M = cat(3,M,addOn);
end
% create two smaller 3D arrays
X = M(:,:,1:2:end,:); % odd pages
Y = M(:,:,2:2:end,:); % even pages
% recursive call and actual matrix multiplication
M = multiprod(X,Y);
siz = size(M,3);
end
% reshape to original dimensions, minus the third axis.
dims(3) = [];
sqMats = reshape(M,dims);
end