Diagonal matrix in matlab - 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

Related

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

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');.

Reorder the elements in each row of a square diagonal matrix in Matlab

I have a square symmetric matrix A of dimension n in Matlab and I want to reorder the elements in each row in ascending order but preserving the symmetry and without touching the diagonal elements.
E.g.
n=4;
A=[10 9 8 7; 9 6 5 4; 8 5 3 2; 7 4 2 1];
%reorder A in order to obtain
B=[10 7 8 9; 7 6 4 5; 8 4 3 2; 9 5 2 1];
Could you provide some help?
you can use triu to sort only the upper triangle and then add the transpose to keep the symmetry. finally just set the diagonal as in the original matrix:
n=4;
A=[10 9 8 7; 9 6 5 4; 8 5 3 2; 7 4 2 1];
% upper triangle indexes
UI = triu(true(size(A)),1);
% upper triangle of A
B = triu(A,1);
% assign inf to diagonal and below - important if there are any negative values in A
B(~UI) = -inf;
% sort rows descending order
B = sort(B,2,'ascend');
% set infs to 0
B(isinf(B)) = 0;
% add lower triangle matrix
B = B + B.';
% set diagonal as original A
B(1:n+1:n^2) = diag(A)
A = [10 9 8 7;
9 6 5 4;
8 5 3 2;
7 4 2 1];
B = sort(triu(A,1),2) + diag(diag(A)) + sort(triu(A,1),2)';
Explain
triu(A,1) gets the upper triangular matrix above the main diagonal, that is
ans0 =
0 9 8 7
0 0 5 4
0 0 0 2
0 0 0 0
sort(triu(A,1),2) sorts the elements in each row of the above result in an ascending order, that is
ans1 =
0 7 8 9
0 0 4 5
0 0 0 2
0 0 0 0
diag(diag(A)) gets the diagonal of A, that is
ans2 =
10 0 0 0
0 6 0 0
0 0 3 0
0 0 0 1
sort(triu(A,1),2)' gets the transpose of the sorted upper triangular matrix, that is
ans =
0 0 0 0
7 0 0 0
8 4 0 0
9 5 2 0
Add ans1, ans2 and ans3 up, you will get B.

Matlab: create band matrix with repeating and alternating values

Let us say that I have 7 values: a, b, c, d, e, f and g
I would like to construct an m by n matrix in this way:
[ a b c d 0 0 0 0 . . . .
[ b e f g 0 0 0 0 . . . .
[ c f a b c d 0 0 . . . .
[ d g b e f g 0 0 . . . .
[ 0 0 c f a b c d 0 0 . .
[ 0 0 d g b e f g 0 0 . .
[ . . 0 0 c f a b c d . .
[ . . 0 0 d g b e f g . .
And so forth...
Therefore, the desired matrix is symmetrical. Values a and e alternate on the main diagonal; values b and f alternate on the 1st upper diagonal; values c and g alternate on the 2nd upper diagonal; values d and 0 alternate on the 3rd upper diagonal. I would like to be able to specify the matrix size with m by n parameters.
I used to do this easily with the SparseArray and Band functions in Mathematica, but I cannot find equivalent functions in Matlab. Would there be an efficient way to construct this kind of matrix in Matlab?
You can use spdiags to specify the upper diagonals in a sparse matrix and then add the transpose of the strictly upper triangular part for exact symmetry:
>> n = 6;
>> a = 1;b = 2;c = 3;d = 4;e = 5;f = 6;g = 7;
>> n = 6;
>> A = spdiags(repmat([[a;e] , [f;b] , [c;g] , [0;d]],n/2,1),0:3,n,n);
>> A = A + triu(A,1).';
>> issymmetric(A)
ans =
1
>> full(A)
ans =
1 2 3 4 0 0
2 5 6 7 0 0
3 6 1 2 3 4
4 7 2 5 6 7
0 0 3 6 1 2
0 0 4 7 2 5
You might notice I flipped b/f and 0/d to adjust for the filling behavior; there may be a better way to do this.
For non-square matrices with potentially odd dimension number, I would build the minimum-sized square sparse matrix that has the actual one as a sub-matrix and mask out the unneeded portion at the end:
>> m = 13;
>> n = 6;
>> p = max([m,n]);
>> p = p + mod(p,2); % added to make p even.
>> A = spdiags(repmat([[a;e] , [f;b] , [c;g] , [0;d]],p/2,1),0:3,p,p);
>> A = A + triu(A,1).';
>> A = A(1:m,1:n);
>> full(A)
ans =
1 2 3 4 0 0
2 5 6 7 0 0
3 6 1 2 3 4
4 7 2 5 6 7
0 0 3 6 1 2
0 0 4 7 2 5
0 0 0 0 3 6
0 0 0 0 4 7
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
It's a tad inefficient to build, but easy and straight-forward.
You can use the spdiags function
Using numbers instead of letters (sparse does not support symbols) like this:
m = 10;
n = 10;
B = repmat([1:4;[5:7 0]],n/2,1);
B=[B(:,end:-1:2) B];
d=-3:3;
A = spdiags(B,d,m,n);
full(A)
which outputs:
1 6 3 0 0 0 0 0 0 0
2 5 2 7 4 0 0 0 0 0
3 6 1 6 3 0 0 0 0 0
4 7 2 5 2 7 4 0 0 0
0 0 3 6 1 6 3 0 0 0
0 0 4 7 2 5 2 7 4 0
0 0 0 0 3 6 1 6 3 0
...
Explanation:
The B matrix contains the values needed on the diagonals in its columns. It is first constructed using B = repmat([1:4;[5:7 0]],n/2,1); which makes the first two rows repeat n/2 times. At that point it looks like:
1 2 3 4
5 6 7 0
1 2 3 4
5 6 7 0
1 2 3 4
5 6 7 0
1 2 3 4
...
Then it is mirrored to give it the diagonals that are in the lower diagonal part using B=[B(:,end:-1:2) B]; which makes it look like:
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
0 7 6 5 6 7 0
4 3 2 1 2 3 4
...
These are the diagonals passed to the spdiags function.
The end is a MATLAB keyword which refers to the last column or row. That indexing line says, in words: "Take all the rows of B, and the columns starting from the end, going back 1 step at a time until the second column, and place that submatrix before B." That is how the mirroring is accomplished.

How to normalize matrix setting 0 for minimum values and 1 for maximum values?

I need to transform a neural network output matrix with size 2 X N in zeros and ones, where 0 will represent the minimum value of the column and 1 contrariwise. This will be necessary in order to calculate the confusion matrix.
For example, consider this matrix 2 X 8:
2 33 4 5 6 7 8 9
1 44 5 4 7 5 2 1
I need to get this result:
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
How can I do this in MATLAB without for loops? Thanks in advance.
>> d = [ 2 33 4 5 6 7 8 9;
1 44 5 4 7 5 2 1];
>> bsxfun(#rdivide, bsxfun(#minus, d, min(d)), max(d) - min(d))
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
The bsxfun function is necessary to broadcast the minus and division operations to matrices of different dimensions (min and max have only 1 row each).
Other solution is the following (works only for 2 rows):
>> [d(1,:) > d(2,:); d(1,:) < d(2,:)]
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
If it's just 2xN, then this will work:
floor(A./[max(A); max(A)])
In general:
floor(A./repmat(max(A),size(A,1),1))

Reshape / Transform an upper triangular matrix in MATLAB

I have an upper triangular matrix (without the diagonal) given by:
M = [0 3 2 2 0 0; 0 0 8 6 3 2; 0 0 0 3 2 1; 0 0 0 0 2 1; 0 0 0 0 0 0]
The resulting matrix should look like this:
R = [0 0 0 0 0 0; 0 2 0 0 0 0; 2 3 1 0 0 0; 2 6 2 1 0 0; 3 8 3 2 0 0]
Since I couldn't find a simple explanation which describes my goal I tried to visualize it with an image:
I already tried lots of different combinations of rot90, transpose, flipud etc., but I could't find the right transformation that gives me the matrix R
EDIT:
The rows of the matrix M are not always sorted as in the example above. For another matrix M_2:
M_2 = [0 2 3 1 0 0; 0 0 3 6 3 9; 0 0 0 1 2 4; 0 0 0 0 2 6; 0 0 0 0 0 0]
the resulting matrix R_2 need to be the following:
R_2 = [0 0 0 0 0 0; 0 9 0 0 0 0; 1 3 4 0 0 0; 3 6 2 6 0 0; 2 3 1 2 0 0]
Again the visualization below:
EDIT:
Inspired by the tip from #Dan's comment, it can be further simplified to
R = reshape(rot90(M), size(M));
Original Answer:
This should be a simple way to do this
F = rot90(M);
R = F(reshape(1:numel(M), size(M)))
which returns
R =
0 0 0 0 0 0
0 2 0 0 0 0
2 3 1 0 0 0
2 6 2 1 0 0
3 8 3 2 0 0
The idea is that when you rotate the matrix you get
>> F = rot90(M)
F =
0 2 1 1 0
0 3 2 2 0
2 6 3 0 0
2 8 0 0 0
3 0 0 0 0
0 0 0 0 0
which is a 6 by 5 matrix. If you consider the linear indexing over F the corresponding indices are
>> reshape(1:30, size(F))
1 7 13 19 25
2 8 14 20 26
3 9 15 21 27
4 10 16 22 28
5 11 17 23 29
6 12 18 24 30
where elements 6, 11, 12, 16, 17, 18 , and ... are zero now if you reshape this to a 5 by 6 matrix you get
>> reshape(1:30, size(M))
1 6 11 16 21 26
2 7 12 17 22 27
3 8 13 18 23 28
4 9 14 19 24 29
5 10 15 20 25 30
Now those elements corresponding to zero values are on top, exactly what we wanted. So by passing this indexing array to F we get the desired R.
Without relying on order (just rotating the colored strips and pushing them to the bottom).
First solution: note that it doesn't work if there are zeros between the "data" values (for example, if M(1,3) is 0 in the example given). If there may be zeros please see second solution below:
[nRows nCols]= size(M);
R = [flipud(M(:,2:nCols).') zeros(nRows,1)];
[~, rowSubIndex] = sort(~~R);
index = sub2ind([nRows nCols],rowSubIndex,repmat(1:nCols,nRows,1));
R = R(index);
Second solution: works even if there are zeros within the data:
[nRows nCols]= size(M);
S = [flipud(M(:,2:nCols).') zeros(nRows,1)];
mask = 1 + fliplr(tril(NaN*ones(nRows, nCols)));
S = S .* mask;
[~, rowSubIndex] = sort(~isnan(S));
index = sub2ind([nRows nCols],rowSubIndex,repmat(1:nCols,nRows,1));
R = S(index);
R(isnan(R)) = 0;
Alternate option, using loops:
[nRows nCols]= size(M);
R = zeros(nRows,nCols);
for n = 1:nRows
R((n+1):nCols,n)=fliplr(M(n,(n+1):nCols));
end