Faster way to transpose each row of a matrix and multiply the resulting vector by some other matrix? - matlab

I have an input matrix X with dimensions N_rows x N_cols. I also have a sparse, tridiagonal matrix M which is square of size N_rows x N_rows. These are created as follows:
N_rows = 3;
N_cols = 6;
X = rand(N_rows,N_cols);
mm = 10*ones(N_cols,1); % Subdiagonal elements
dd = 20*ones(N_cols,1); % Main diagonal elements
pp = 30*ones(N_cols,1); % Superdiagonal elements
M = spdiags([mm dd pp],-1:1,N_cols,N_cols);
and look like the following:
>> X
X =
0.4018 0.1233 0.4173 0.9448 0.3377 0.1112
0.0760 0.1839 0.0497 0.4909 0.9001 0.7803
0.2399 0.2400 0.9027 0.4893 0.3692 0.3897
full(M)
ans =
2 3 0 0 0 0
1 2 3 0 0 0
0 1 2 3 0 0
0 0 1 2 3 0
0 0 0 1 2 3
0 0 0 0 1 2
I would like to take each row of X, and do a matrix multiplication with M, and piece the obtained rows back together to obtain an output Y. At the moment, I achieve this successfully with the following:
Y = (M*X.').';
The example above is for a 3x6 matrix for X, but in reality I need to do this for a matrix with dimensions 500 x 500, about 10000 times, and the profiler says that this operation in the bottleneck in my larger code. Is there a faster way to do this row-by-row matrix multiplication multiplication?
On my system, the following takes around 20 seconds to do this 10000 times:
N_rows = 500;
N_cols = 500;
X = rand(N_rows,N_cols);
mm = 10*ones(N_cols,1); % Subdiagonal elements
dd = 20*ones(N_cols,1); % Main diagonal elements
pp = 30*ones(N_cols,1); % Superdiagonal elements
M = spdiags([mm dd pp],-1:1,N_cols,N_cols);
tic
for k = 1:10000
Y = (M*X.').';
end
toc
Elapsed time is 18.632922 seconds.

You can use X*M.' instead of (M*X.').';. This saves around 35% of time on my computer.
This can be explained because transposing (or permuting dimensions) implies rearranging the elements in the internal (linear-order) representation of the matrix, which takes time.

Another option is using conv2:
Y = conv2(X, [30 20 10], 'same');
Explanation:
There is a tridiagonal matrix that all elements on each diagonal are identical to each other:
M =
2 3 0 0 0 0
1 2 3 0 0 0
0 1 2 3 0 0
0 0 1 2 3 0
0 0 0 1 2 3
0 0 0 0 1 2
Suppose you want to multiply the matrix by a vector:
V = [11 ;12 ;13 ;14 ;15 ;16];
R = M * V;
Each element of the vector R is computed by sum of products of each row of M by V:
R(1):
2 3 0 0 0 0
11 12 13 14 15 16
R(2):
1 2 3 0 0 0
11 12 13 14 15 16
R(3):
0 1 2 3 0 0
11 12 13 14 15 16
R(4):
0 0 1 2 3 0
11 12 13 14 15 16
R(5):
0 0 0 1 2 3
11 12 13 14 15 16
R(6):
0 0 0 0 1 2
11 12 13 14 15 16
It is the same as multiplying a sliding window of [1 2 3] by each row of M. Basically convolution applies a sliding window but first it reverses the direction of window so we need to provide the sliding window in the reversed order to get the correct result. Because of that I used Y = conv2(X, [30 20 10], 'same'); instead of Y = conv2(X, [10 20 30], 'same');.

Related

Find last true element of columns

I'd like to extract one value per column of a matrix using a condition. Multiple values on each column match that condition, but only the last one should be selected. It is safe to assume that each row contains at least one such value.
So given an NxM matrix and an equally-sized boolean, extract M values for which the boolean is true and it is the last true value in a column. For example:
m = magic(4);
i = (m > 10);
% m =
% 16 2 3 13
% 5 11 10 8
% 9 7 6 12
% 4 14 15 1
% i =
% 1 0 0 1
% 0 1 0 0
% 0 0 0 1
% 0 1 1 0
And the expected output:
% i_ =
% 1 0 0 0
% 0 0 0 0
% 0 0 0 1
% 0 1 1 0
% x = [16, 14, 15, 12]
I know this could be easily achieved by looping through the columns and using find, but in my experience there often are better ways of formulating these problems.
This would do it
m(max(i.*reshape([1:numel(m)],size(m))))
Explanation
So we are generating an array of indices
reshape([1:numel(m)],size(m))
ans =
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
That represents the indices for each value. The we multiply that with I to get the values we are interested in
i.*reshape([1:numel(m)],size(m))
ans =
1 0 0 13
0 6 0 0
0 0 0 15
0 8 12 0
Then we do a max on that since max works on columns. This will give us the last index in each column.
max(i.*reshape([1:numel(m)],size(m)))
ans =
1 8 12 15
Then apply those indices on m to get the values
m(max(i.*reshape([1:numel(m)],size(m))))
ans =
16 14 15 12
You can use the second output of max to find the last true element of each column. Before that the logical matrx should be multiplied by an increasing column vector.
[~, idx] = max((1:size(i, 1)).' .* i, [], 1, 'linear') ;
x = m(idx) ;
Here's another way, using accumarray:
[~, col] = find(i); % column indices
lin = find(i); % linear indices
x = accumarray(col, m(lin), [], #(x) x(end));

Diagonal matrix in matlab

I am having trouble creating this matrix in matlab, basically I need to create a matrix that has -1 going across the center diagonal followed be 4s on the diagonal outside of that (example below). All the other values can be zero.
A5 = [-1 4 0 0 0;
4 -1 4 0 0;
0 4 -1 4 0;
0 0 4 -1 4;
0 0 0 4 -1];
I have tried using a command v = [4]; D = diag(v)
but that only works for the center diagonal.
This can also be done using a toeplitz matrix:
function out = tridiag(a,b,c,N)
% TRIDIAG generates a tri-diagonal matrix of size NxN.
% lower diagonal is a
% main diagonal is b
% upper diagonal is c
out = toeplitz([b,a,zeros(1,N-2)],[b,c,zeros(1,N-2)]);
>> tridiag(4,-1,4,5)
ans =
-1 4 0 0 0
4 -1 4 0 0
0 4 -1 4 0
0 0 4 -1 4
0 0 0 4 -1
Note #1: When your desired output is symmetric, you can omit the 2nd input to toeplitz.
Note #2: As the size of the matrix increases, there comes a point where it makes more sense to store it as sparse, as this saves memory and improves performance (assuming your matrix is indeed sparse, i.e. comprised mostly of zeros, as it happens with a tridiagonal matrix). Some useful functions are spdiags, sptoeplitzFEX and blktridiagFEX.
A little hackish, but here it goes:
N = 7; % matrix size
v = [11 22 33]; % row vector containing the diagonal values
w = [0 v(end:-1:1)];
result = w(max(numel(v)+1-abs(bsxfun(#minus, 1:N, (1:N).')),1))
This gives
result =
11 22 33 0 0 0 0
22 11 22 33 0 0 0
33 22 11 22 33 0 0
0 33 22 11 22 33 0
0 0 33 22 11 22 33
0 0 0 33 22 11 22
0 0 0 0 33 22 11
To understand how it works, see some intermediate steps:
>> abs(bsxfun(#minus, 1:N, (1:N).'))
ans =
0 1 2 3 4 5 6
1 0 1 2 3 4 5
2 1 0 1 2 3 4
3 2 1 0 1 2 3
4 3 2 1 0 1 2
5 4 3 2 1 0 1
6 5 4 3 2 1 0
>> max(numel(v)+1-abs(bsxfun(#minus, 1:N, (1:N).')),1)
ans =
4 3 2 1 1 1 1
3 4 3 2 1 1 1
2 3 4 3 2 1 1
1 2 3 4 3 2 1
1 1 2 3 4 3 2
1 1 1 2 3 4 3
1 1 1 1 2 3 4
Use D = diag(u,k) to shift u in k levels above the main diagonal, and D = diag(u,-k) for the opposite direction. Keep in mind that you need u to be in the right length of the k diagonal you want, so if the final matrix is n*n, the k's diagonal will have only n-abs(k) elements.
For you case:
n = 5; % the size of the matrix
v = ones(n,1)-2; % make the vector for the main diagonal
u = ones(n-1,1)*4; % make the vector for +1 and -1 diagonal
A5 = diag(v)+diag(u,1)+diag(u,-1) % combine everything together
Which gives:
A5 =
-1 4 0 0 0
4 -1 4 0 0
0 4 -1 4 0
0 0 4 -1 4
0 0 0 4 -1

how to programme in Matlab to obtain transition probability 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

Multiplying a small matrix by a big matrix

I'm trying to multiply every element in a small matrix (let's say 2x2) with every position in a big matrix (let's say 4x4), element by element.
So I want:
1 2 3 4 1 0 3 0
1 0 1 2 3 4 0 0 0 0
0 0 'x' 1 2 3 4 = 1 0 3 0
1 2 3 4 0 0 0 0
The small matrix is applied as many times as it fits, and the multiplication is element by element. I've tried a bunch of loops, but that doesn't feel right in MATLAB, there must be prettier ways of doing it?
One possibility is to use repmat to repeat the small matrix as many times as necessary:
C = repmat(A,size(B,1)/size(A,1),size(B,2)/size(A,2)).*B
Another possibility, which avoids repmat: cut up the large matrix, arrange the pieces in the third and fourth dimensions, and use bsxfun to do the multiplication:
[m n] = size(A);
[M N] = size(B);
T = permute(reshape(B,M,n,[]), [2 1 3]);
T = permute(reshape(T,n,m,[],size(T,3)),[2 1 3 4]);
C = cell2mat(squeeze(mat2cell(bsxfun(#times,T,A),m,n,ones(1,M/m),ones(1,N/n))));
(The two lines T = ... do the cutting, and are due to A. Donda.)
The advantage of this approach is that, if memory is an issue, you can overwrite B instead of defining T, thus saving memory:
[m n] = size(A);
[M N] = size(B);
B = permute(reshape(B,M,n,[]),[2 1 3]);
B = permute(reshape(B,n,m,[],size(B,3)),[2 1 3 4]);
C = cell2mat(squeeze(mat2cell(bsxfun(#times,B,A),m,n,ones(1,M/m),ones(1,N/n))));
If you have the image processing toolbox, you can try blkproc:
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> B = [1 0; 0 0];
>> C = blkproc(A,size(B),#(x) x.*B)
C =
16 0 3 0
0 0 0 0
9 0 6 0
0 0 0 0

Multiplication of Vectors with diagonal of a matrices from t to t+1 in Matlab

Still very new to programming...
I have 9x1 Vectors at time t, t+1, t+2 etc.
[10 10 10 10 10 10 10 10 10]'
and matrices. Each matrix is 9x9 and also at time 1, t+1, t+2 etc. =
1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 1
They are 3d matrices and I want to make them 4d in the future.
I want to multiply vector(:,:,t) with the diagonal of matrix at time t and output vector(:,:,t+1).
So in short...
vector t multiplied by diag matrix t = vector t+1
vector t+1 multiplied by diag matrix t+1 = vector t+2
vector t+2 multiplied by diag matrix t+2 = vector t+3 ... and so on.
the diagonal numbers change in each time step but for simplicity sake, let's keep them all at 1 for the moment.
I've tried using diag but it states I have to use a 2D input so only works when I ignore t.
Cheers for your help guys - it's helping me learn a lot. Any hints or solutions will be much appreciated. I know you guys know the simplest and most efficient solutions.
Since the result of each step depends on the previous iteration, it cannot be vectorized. So I would go with #JohnColby's solution.
For what it's worth, here is an example how you would extract the diagonals of a 3D matrix in a vectorized way:
M = reshape(1:3*4*3,[3 4 3]);
[r,c,p] = size(M);
ind = bsxfun(#plus, (1:r+1:r*c)', (0:p-1).*r*c);
M(ind)
Each column of the result corresponds to the diagonal elements from each slice (doesn't have to be square matrix):
>> M
M(:,:,1) =
1 4 7 10
2 5 8 11
3 6 9 12
M(:,:,2) =
13 16 19 22
14 17 20 23
15 18 21 24
M(:,:,3) =
25 28 31 34
26 29 32 35
27 30 33 36
>> M(ind)
ans =
1 13 25
5 17 29
9 21 33
Here you go:
n = 10;
% Make sample data
t = zeros(9,1,n);
t(:,1,1) = 1;
T = repmat(diag(ones(9,1)), [1 1 n]);
% Loop though to fill in t based on previous t and T
for i = 2:n
t(:,1,i) = t(:,1,i-1) .* diag(T(:,:,i-1));
end
Now all of t should be 1.