Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

What is the best way to populate an upper triangular (or alternatively: lower triangular or symmetric) matrix from a vector of elements?

This example input:

{1, 2, 3, 4, 5, 6}

should give

{{0, 1, 2, 3},
 {0, 0, 4, 5},
 {0, 0, 0, 6},
 {0, 0, 0, 0}}

or alternatively

{{0, 1, 2, 3}, 
 {1, 0, 4, 5}, 
 {2, 4, 0, 6}, 
 {3, 5, 6, 0}}

or even

{{0, 0, 0, 0},
 {1, 0, 0, 0},
 {2, 3, 0, 0},
 {4, 5, 6, 0}}

This problem has many solutions of course. I'm looking for the best (criteria: most elegant/readable, shortest, fastest) ones.

The input has length $\binom{n}{2} = \frac{n(n-1)}{2}$. Consider $n$ known. Feel free to use any version 10 functionality.

share|improve this question
    
@MrWizard I hope you don't mind, I know we went through this once in the past. Perhaps v10 has new ways. Note: I do have a number of solutions to the problem (including MrW's), but I chose not to share them in order not to bias the answers. –  Szabolcs 17 hours ago
    
The elements are machine integers only? Or...? –  Oleksandr R. 17 hours ago
    
@OleksandrR. I was thinking of a general solution, but actually I will only use machine integers in practice. A fast solution using machine integers only will definitely be useful. –  Szabolcs 17 hours ago
    
Sorry, I just read "not to bias the answers" -- would you prefer that I delete the copied answer I just posted? –  Mr.Wizard 16 hours ago
    
@Mr.Wizard No, keep it! –  Szabolcs 14 hours ago
add comment

5 Answers 5

Probably not fast, but very simple:

upperTriangular[elements_, n_] :=
 SparseArray[
  Thread[
    SymmetrizedIndependentComponents[{n, n}, Antisymmetric[{1, 2}]] ->    
    elements]]
share|improve this answer
2  
SymmetrizedIndependentComponents? These function names are getting out of hand. +1 nevertheless as I don't recall seeing that function used before. –  Mr.Wizard 14 hours ago
    
Great! I didn't mention it but I was hoping for something using this functionality. Is StructuredArray useful here? –  Szabolcs 14 hours ago
add comment
s = 4;
lis = Range[s (s - 1)/2];
PadLeft[lis[[1 + # s - (# (1 + #))/2 ;; (1 + #) s - ((# + 1) (2 + #))/
         2]], s, 0] & /@ Range[0, s - 1] // MatrixForm
share|improve this answer
add comment

Here is a semi-imperative function to create an upper-triangular array:

upperTriangular[v_] := upperTriangular[v, (1 + Sqrt[1 + 8*Length@v])/2]

upperTriangular[v_, n_] := Module[{i = 0}, Array[If[# >= #2, 0, v[[++i]]]&, {n, n}]]

The function is expressed using two definitions for a reason that will become clear in a moment. Here it is in action:

upperTriangular @ Range @ 6

(* {{0,1,2,3},{0,0,4,5},{0,0,0,6},{0,0,0,0}} *)

If we know the square matrix size n ahead of time, and we intend to perform many such triangularizations, then it might be useful to precompile the transformation function. The following function does that, using the two-argument form of upperTriangular to write the matrix construction code:

upperTriangulizer[n_Integer, type_:_Integer] :=
  Module[{v, r}
  , Quiet[With[{r = upperTriangular[v, n]}, Compile[{{v, type, 1}}, r]], Part::partd]
  ]

Here it is in action:

upperTriangulizer[5] @ Range[10]

(* {{0,1,2,3,4},{0,0,5,6,7},{0,0,0,8,9},{0,0,0,0,10},{0,0,0,0,0}} *)

The result is a packed array:

Developer`PackedArrayQ @ %

(* True *)

We can control the data type of the matrix elements:

upperTriangulizer[4, _Real] @ Range[6]

(* {{0.,1.,2.,3.},{0.,0.,4.,5.},{0.,0.,0.,6.},{0.,0.,0.,0.}} *)

An interesting feature of the generated function is that it involves no loops -- the resultant matrix is created by direct construction. The loops have been completely unrolled, a desirable situation for some applications:

CompiledFunctionTools`CompilePrint@upperTriangulizer[3, _Real]

(*
    1 argument
    4 Integer registers
    9 Real registers
    2 Tensor registers
    Underflow checking off
    Overflow checking off
    Integer overflow checking on
    RuntimeAttributes -> {}

    T(R1)0 = A1
    I0 = 0
    I2 = 2
    I1 = 1
    I3 = 3
    Result = T(R2)1

1   R0 = Part[ T(R1)0, I1]
2   R1 = Part[ T(R1)0, I2]
3   R2 = Part[ T(R1)0, I3]
4   R3 = I0
5   R4 = I0
6   R5 = I0
7   R6 = I0
8   R7 = I0
9   R8 = I0
10  T(R2)1 = {{R3, R0, R1}, {R4, R5, R2}, {R6, R7, R8}}
11  Return
*)
share|improve this answer
add comment

Copy and paste from Code Review:

Here are a few methods for your consideration and feedback.

All use:

s = 5
elems = Range[s(s-1)/2]

This one uses the core of my "Dynamic Partition" function. It is the fastest method I know for this problem. Also, perhaps refactoring the code like this makes it more intelligible.

dynP[l_, p_] := 
 MapThread[l[[# ;; #2]] &, {{0}~Join~Most@# + 1, #} &@Accumulate@p]

#~PadRight~s & /@ {{}} ~Join~ dynP[elems, Range[s-1]]

This one is slightly shorter, using a different way to construct the indices, but also slightly slower, and perhaps less transparent.

#~PadRight~s & /@ 
 Prepend[
   elems~Take~# & /@ 
     Array[{1 + # (# - 1)/2, # (# + 1)/2} &, s - 1],
   {}
 ]

This one is terse, but slow. Legibility is debatable.

SparseArray[Join @@ Table[{i, j}, {i, s}, {j, i - 1}] -> elems, s] // MatrixForm

Here is one I just came up with, and I am quite pleased with it. I think it may be more understandable than most of the ones above.

Take[FoldList[RotateLeft, elems, Range[0, s - 1]] ~LowerTriangularize~ -1, All, s + 1]
share|improve this answer
add comment

Here's a procedural, hence compilable solution:

Matrixify = Compile[{{lis, _Integer, 1}},
  Module[{n, mat, pl = 1, pm = 2, i = 1},

    n = Round[(Sqrt[8 Length[lis] + 1] + 1)/2];
    mat = Table[0, {n^2}];

    Do[
      mat[[pm ;; pm + k - 1]] = lis[[pl ;; pl + k - 1]];
      i++;
      pl += k;
      pm += k + i,
      {k, n - 1, 1, -1}
    ];

    Partition[mat, n]

  ],
  CompilationTarget -> "C",
  Parallelization -> True,
  RuntimeOptions -> "Speed"
];

Here are some results:

Matrixify[Range[6]]
(* {{0, 1, 2, 3}, {0, 0, 4, 5}, {0, 0, 0, 6}, {0, 0, 0, 0}} *)

Matrixify[Range[Binomial[1000, 2]]]; // AbsoluteTiming
(* {0.032628, Null} *)
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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