MATLAB Answers

How to change a column vector into a square a matrix?

12 views (last 30 days)
I am trying to change a column vector
p=[1;3;5]
into a square matrix P=[1,3;3,5]
Howevery I only manage to change the column vector p into a square matrix with element [1,0;3,5]
with the following code
p=[1;3;5]
n=2
P = zeros(n);
P(triu(ones(n)) == 1) = p;
P

  8 Comments

Show 5 older comments
John D'Errico
John D'Errico on 16 May 2020
The example for a 3x3 matrix is not a vector of length 10. You only gave 6 elements there.
It LOOKS like you want to make a square matrix from a vector that has only the lower triangle, IF you are willing to assume the matrix is a SYMMETRIC matrix. Is THAT your goal?
In that case, your vector must ALWAYS contain a "triangle number" of elements. A triangle number is a number of the form n*(n+1)/2, where n would be the size of the matrix in equation.
Is this what you are asking? To create a symmetric matrix from only the lower triangle of that matrix, when it is stored ina vector? Must you write code for any size matrix?
And, finally, is this your homework assignment?
Muhammad Prasetyo
Muhammad Prasetyo on 16 May 2020
My mistake for 3x3 matrix it should be from a vector with 6 elements. Yes my goal is to make a symmetric matrix P from the elements of vector p. My goal is to make a code for a 4x4 matrix which means the vector needs to have 10 elements.
This is something related with my thesis, related to designing a controller in control engineering.

Sign in to comment.

Accepted Answer

John D'Errico
John D'Errico on 16 May 2020
Edited: John D'Errico on 16 May 2020
This is something that I've sometimes been surprised that is not already a function in MATLAB, thus, to create a symmetric matrix from only the list of the unique triangle of elements. And there will be various ways to solve the problem. Even a carefully constructed double loop would suffice, though that would not appeal to my purist nature. (You can also do it using a single loop, with careful code.)
For example, one could do this as:
function M = buildSymmetric(V,n)
% uses nested loops to create an nxn symmetric matrix from the lower triangle elements as a list
if length(V) ~= (n*(n+1)/2))
error('V is not of a length consistent with n')
end
M = zeros(n,n); % Note the importance of preallocating M
ind = 0;
for cind = 1:n
for rind = cind:n
ind = ind + 1;
M(rind,cind) = V(ind);
if rind > cind
M(cind,rind) = V(ind);
end
end
end
end
Did it work? Of course.
V = 1:10; % this is just as an example of a 10 element vector for a test case
P = buildSymmetric(V,4)
P =
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 10
It is reasonably efficient, since the MATLAB parser does make loops reasonably efficient these days. Personally, I'd prefer a non-looped solution. The next, classic solution is to create the LOWER trianglular matrix, as you already know how to do (though your code created the upper triangle. The difference is irrelevant.) In fact, I might not be totally surprised if you may be effectively using code I wrote in some answer long, long ago, from a galaxy far, far away.
function M = buildSymmetric(V,n)
% create an nxn symmetric matrix from the lower triangle elements as a list
if numel(V) ~= n*(n+1)/2
error('V is not of a length consistent with n')
end
M = zeros(n,n);
% stuff the lower triangle elements
M(tril(ones(n)) == 1) = V;
% symmetrize the matrix, by adding it to its transpose, then reduce the diagonal
M = M + M.' - diag(diag(M));
end
That final line of code just adds the triangular matrix to its NON-conjugate transpose, which essentially doubles the diagonal elements. Then it subtracts off those diagonal elements to return them to what they should have been.
buildSymmetric(1:6,3)
ans =
1 2 3
2 4 5
3 5 6
buildSymmetric([1 3 5],2)
ans =
1 3
3 5
Honestly, I don't terribly love that solution. Why not? Party because it forces MATLAB to perform a spurious set of n^2 additions, most of which are an add between a number and zero. Those adds don't bother me too much, as they are just CPU cycles, while superfluous, they are not a true bug. However, as well, it essentially multiplies the diagonal elements by 2, and then subtracts off the diagonal element. I could also have divided those elements by 2.
Why is that a bug?
If one of those diagonal elements was really large, doubling the number could cause an overflow, resulting in an inf. I would see that as a bug. It would be a bug that was extremely rare to trip over, but still a significant bug. And I don't like code that will one day fail, even if that is only a rare event.
realmax
ans =
1.7977e+308
buildSymmetric([realmax,1,3],2)
ans =
Inf 1
1 3
So, while I imagine that answer might be acceptable, it is not something that makes me feel good in a MATLAB sense, at least as I think of it now. I can live with the adds to zeros, but a bug just bugs the heck out of me.
function M = buildSymmetric(V,n)
% create an nxn symmetric matrix from the lower triangle elements as a list
if numel(V) ~= n*(n+1)/2
error('V is not of a length consistent with n')
end
% extract the purely diagonal elements from the list
ind = 1 + [0,cumsum(n:-1:2)];
D = V(ind);
V(ind) = [];
M = zeros(n);
if n > 1
% stuff the strictly lower triangle elements
M(logical(tril(ones(n),-1))) = V;
% adds to get the upper triangle
M = M + M.';
end
% stuff the main diagonal without doubling those elements.
% This also just inserts the diagonal elements where they belong.
M(1 + (n+1)*(0:n-1)) = D;
end
This last version also works as we can see. It still used adds with zeros to get the upper triangle correct. It no longer doubles the diagonal elements though, so it is corrrect for all sizes of matrices, even if one of the diagonals would have otherwise caused an overflow.
>> buildSymmetric(2,1)
ans =
2
buildSymmetric([realmax,1,3],2)
ans =
1.7977e+308 1
1 3
>> buildSymmetric(1:6,3)
ans =
1 2 3
2 4 5
3 5 6
>> buildSymmetric(1:10,4)
ans =
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 10
So as you see, that version always works, creating a symmetric matrix from the list of triangular elements as a vector, even if some of those numbers would have cause an overflow otherwise.
Even more professionally written code yet would not need the number n as an argument, but would be able to extract that from the length of V. And one of the most important feature of professionally written code would be good help built into the help block. The help I've written here is just my personal style to write help, but it should be sufficient.
function M = buildSymmetric(V)
% buildSymmetric: nxn symmetric matrix from the list of lower triangle elements
%
% arguments: (input)
% V - a vector of elements from a triangle of a matrix,
% used to then create the corresponding symmetric matrix.
%
% numel(V) MUST be a triangle number, thus a number that can
% be written as n*(n+1)/2
%
% arguments: (output)
% M - a symmetric matrix of size nxn, containing the elements
% of V in proper order.
%
% Examples:
% buildSymmetric([11 12 22])
% ans =
% 11 12
% 12 22
%
% buildSymmetric([11 12 13 14 22 23 24 33 34 44])
% ans =
% 11 12 13 14
% 12 22 23 24
% 13 23 33 34
% 14 24 34 44
%
% Author: John D'Errico
% Date: 5/16/2020
nV = numel(V);
n = round(sqrt(2*nV + 1/4) - 1/2);
if numel(V) ~= n*(n+1)/2
error('numel(V) is not a triangle number, so cannot create a symmetric matrix')
end
% extract the purely diagonal elements from the list
ind = 1 + [0,cumsum(n:-1:2)];
D = V(ind);
V(ind) = [];
% Preallocate M
M = zeros(n);
% When n > 1, we have a matrix, as opposed to a scalar result,
% so we need to build the lower/upper triangles
if n > 1
% stuff the strictly lower triangle elements
M(logical(tril(ones(n),-1))) = V;
% adds to get the upper triangle (sigh - those superfluous adds, I know)
% I could probably write this next line differently with some thought invested.
M = M + M.';
end
% stuff the main diagonal without doubling those elements.
% This also just inserts the diagonal elements where they belong.
M(1 + (n+1)*(0:n-1)) = D;
end
Testing this final version, we see now all test cases work including a case where an overflow could have happened, and n does not need to provided, since that was easily extractable information. When the length of V was not the consistent in size with a triangular number, the code fails with an error of course, explaining why it failed.
>> buildSymmetric(1:5)
Error using buildSymmetric (line 7)
numel(V) is not a triangle number, so cannot create a symmetric matrix
>> buildSymmetric(5)
ans =
5
>> buildSymmetric([realmax,1,3])
ans =
1.7977e+308 1
1 3
>> buildSymmetric(1:6)
ans =
1 2 3
2 4 5
3 5 6
>> buildSymmetric([11 12 13 14 22 23 24 33 34 44])
ans =
11 12 13 14
12 22 23 24
13 23 33 34
14 24 34 44
This final code is longer. It would probably fail miserably in a Cody contest, because the code is the longest code written. But a good Cody score is not always a true measure of good code. :(
Perhaps most important is to understand why the code was written as it was in the end, and how I used MATLAB to accomplish the end goal in a reasonable way that lacks bugs, even when they might be really, really rare bugs.

  6 Comments

Show 3 older comments
John D'Errico
John D'Errico on 18 May 2020
Well, the shorter versions of the code I wrote were pretty short. You CAN do it in about 2 lines.
The final code I wrote does not require knowing the value of n, since it can derive that itself. The final version has a check in there to validate the vector has a length that is appropriate to possibly create a square symmetric matrix. And that final version does not fail for one extremely rare case. Finally, that final code had a lengthy block of help in it, since next year when you want to use this code, having help in it would be a nice thing.
Those are all good things I would want to have in a professionally written piece of code.
So, you can do it in a short way, that leaves me unhappy for several reasons. Or you can do it in a longer way, one that is as close to technical goodness as I wanted to take it. The final code is what I would write if I wanted a code to solve this problem.
Steven Lord
Steven Lord on 18 May 2020
This is something that I've sometimes been surprised that is not already a function in MATLAB, thus, to create a symmetric matrix from only the list of the unique triangle of elements.
There's something close to this in Statistics and Machine Learning Toolbox, the squareform function.
John D'Errico
John D'Errico on 18 May 2020
Yet another time when Steven's vast knowledge of MATLAB teaches me a new trick! But this is how Answers makes us all better, because we can all teach each other.

Sign in to comment.

More Answers (2)

Fabio Freschi
Fabio Freschi on 16 May 2020
Not as elegant as it could be but it works, especially for small matrices
% matrix dimension
n = 3;
% your entries (here dummy values)
p = rand(n*(n+1)/2,1);
% get the indices of the position into the matrix. I understand it is the triangular upper part
[ir,jc] = find(triu(ones(n)));
% create the upper triangular matrix
Pup = accumarray([ir,jc],p,[n,n]);
% now make the symmetric matrix
P = (Pup+Pup.').*.5*eye(n)

  0 Comments

Sign in to comment.


Mehmed Saad
Mehmed Saad on 16 May 2020
Here is one with for loop
function P = Using_for_loop(p,n)
P = zeros(n);
[I,J]=ind2sub(size(P),1:numel(P));
for ii =1:n
sz = length(P(I==ii & J>ii-1));
P(I==ii & J>ii-1) = p(1:sz);
P(J==ii & I>ii-1) = p(1:sz);
p(1:sz) = [];
end
end
Now call it
p = [11;12;13;14;22;23;24;33;34;44];
n = 4;
P = Using_for_loop(p,n)
P =
11 12 13 14
12 22 23 24
13 23 33 34
14 24 34 44

  0 Comments

Sign in to comment.


Translated by