3
$\begingroup$

There is a function VectorToSymmetricMatrix that does the following:

Statistics`Library`VectorToSymmetricMatrix[{1, 2, 3}, {5, 6, 7}, 3] // MatrixForm

$$\left( \begin{array}{ccc} 5 & 1 & 2 \\ 1 & 6 & 3 \\ 2 & 3 & 7 \\ \end{array} \right)$$

I find it quite inconvenient to use because my data is differently ordered:

v={5, 1, 6, 2, 3, 7}

I am searching for a simple way to convert this vector to a symmetric matrix shown above.

There are similar posts, however, I could not find one addressing my problem. Please, try to provide a solution as simple and pedagogical as possible. I prefer to use documented functions even if the solution is slower.

A test case could be:

v = RandomReal[{0, 1}, {100, 75 (75 + 1)/2}];
m = Table[UnpackUpTrg[vi], {vi, v}]; 

My current implementation is

ind[i_, j_] := If[i < j, i + j (j - 1)/2, j + i (i - 1)/2]
UnpackUpTrg[v_] := Module[{k},
  k = 1/2 (-1 + Sqrt[1 + 8 Length[v]]);
  Table[v[[ind[i, j]]], {i, k}, {j, k}]
  ]

but it is a bit slow and procedural.

$\endgroup$
2
  • $\begingroup$ v1=v[[{2, 4, 5, 1, 3, 6}]] gives he Order you want. and StatisticsLibraryVectorToSymmetricMatrix[v1[[1 ;; 3]], v1[[4 ;; 6]], 3] creates the matrix. $\endgroup$ Jun 23, 2021 at 20:40
  • $\begingroup$ @DanielHuber Yes, but this was just an example of how results should look like. But the intended use case is given below. $\endgroup$
    – yarchik
    Jun 23, 2021 at 20:42

1 Answer 1

3
$\begingroup$
toSymmetricMat = Transpose[LowerTriangularize[#, -1]] + # & @
    PadRight[TakeList[#, Range[1/2 (-1 + Sqrt[1 + 8 Length[#]])]]] &;

Examples:

toSymmetricMat[{5, 1, 6, 2, 3, 7}] // MatrixForm // TeXForm

$\left( \begin{array}{ccc} 5 & 1 & 2 \\ 1 & 6 & 3 \\ 2 & 3 & 7 \\ \end{array} \right)$

toSymmetricMat[{5, 1, 6, 2, 4, 7, 3, 5, 6, 8}] // MatrixForm // TeXForm

$\left( \begin{array}{cccc} 5 & 1 & 2 & 3 \\ 1 & 6 & 4 & 5 \\ 2 & 4 & 7 & 6 \\ 3 & 5 & 6 & 8 \\ \end{array} \right)$

SeedRandom[1]
rvec = RandomInteger[10, 7 (7 + 1)/2];
toSymmetricMat[rvec] // MatrixForm // TeXForm

$\left( \begin{array}{ccccccc} 1 & 4 & 7 & 8 & 1 & 1 & 0 \\ 4 & 0 & 0 & 6 & 8 & 3 & 2 \\ 7 & 0 & 0 & 0 & 5 & 2 & 6 \\ 8 & 6 & 0 & 4 & 1 & 10 & 4 \\ 1 & 8 & 5 & 1 & 1 & 1 & 5 \\ 1 & 3 & 2 & 10 & 1 & 6 & 4 \\ 0 & 2 & 6 & 4 & 5 & 4 & 3 \\ \end{array} \right)$

Update: For versions 11.1 and older, replace TakeList with Internal`PartitionRagged:

toSymmetricMat2 = Transpose[LowerTriangularize[#, -1]] + # &@
  PadRight[Internal`PartitionRagged[#, Range[1/2 (-1 + Sqrt[1 + 8 Length[#]])]]] &;


And @@ (toSymmetricMat2[#] == toSymmetricMat[#] & /@ 
   {{5, 1, 6, 2, 3, 7}, {5, 1, 6, 2, 4, 7, 3, 5, 6, 8}, rvec})
True
$\endgroup$
3
  • $\begingroup$ Thank you! I wanted to try your code, but I found that I do not have the TakeList function. It was introduced in MA11.2, but I have MA11.1.1. Can you maybe suggest a solution using older functions? $\endgroup$
    – yarchik
    Jun 24, 2021 at 12:05
  • $\begingroup$ @yarchik, please see the update. $\endgroup$
    – kglr
    Jun 24, 2021 at 13:38
  • 1
    $\begingroup$ Thank you, it is actually 10 times faster than my code and 3 times faster than my compiled code :). I will wait a little bit with accepting the answer, maybe someone will come up with a function that does not use undocumented functions, as explicitly asked in OP. $\endgroup$
    – yarchik
    Jun 24, 2021 at 14:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.