Take upper triangular part of matrix









up vote
7
down vote

favorite
2












I would like to extract the upper triangular part of a square matrix into a flat list. I am looking for fast ways to do this. I am primarily interested in solutions that are fast for non-packed arrays as well. For packed arrays, a compiled solution will always be very fast.



Here are three successively faster implementations to get the ball rolling.



takeUpper1[mat_?SquareMatrixQ] := 
Join @@ Table[mat[[i, j]], i, Length[mat], j, i + 1, Length[mat]];

takeUpper2[mat_?SquareMatrixQ] :=
Extract[mat, Subsets[Range@Length[mat], 2]]

takeUpper3[mat_?SquareMatrixQ] :=
Join @@ Pick[mat, UpperTriangularize[ConstantArray[1, Dimensions[mat]], 1], 1]

(* added later, also slower than takeUpper3 *)
takeUpper4[mat_?SquareMatrixQ] :=
Join @@ Table[mat[[i, i + 1 ;;]], i, Length[mat]]


Benchmarks:



mat = RandomReal[1, 1000, 1000]; 
mat2 = Developer`FromPackedArray[mat];


Packed:



res1 = takeUpper1[mat]; // RepeatedTiming
(* 0.218, Null *)

res2 = takeUpper2[mat]; // RepeatedTiming
(* 0.0840, Null *)

res3 = takeUpper3[mat]; // RepeatedTiming
(* 0.017, Null *)

res1 == res2 == res3
(* True *)


Non-packed:



res1 = takeUpper1[mat2]; // RepeatedTiming
(* 0.839, Null *)

res2 = takeUpper2[mat2]; // RepeatedTiming
(* 0.0949, Null *)

res3 = takeUpper3[mat2]; // RepeatedTiming
(* 0.018, Null *)

res1 == res2 == res3
(* True *)


You are also welcome to suggest intuitive names for such a function. The final function will work also on non-square matrices and will have a second argument similar to that of UpperTriangularize.










share|improve this question























  • Are you interested in only positive second arguments?
    – Carl Woll
    Nov 9 at 17:08











  • @CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
    – Szabolcs
    Nov 11 at 19:35














up vote
7
down vote

favorite
2












I would like to extract the upper triangular part of a square matrix into a flat list. I am looking for fast ways to do this. I am primarily interested in solutions that are fast for non-packed arrays as well. For packed arrays, a compiled solution will always be very fast.



Here are three successively faster implementations to get the ball rolling.



takeUpper1[mat_?SquareMatrixQ] := 
Join @@ Table[mat[[i, j]], i, Length[mat], j, i + 1, Length[mat]];

takeUpper2[mat_?SquareMatrixQ] :=
Extract[mat, Subsets[Range@Length[mat], 2]]

takeUpper3[mat_?SquareMatrixQ] :=
Join @@ Pick[mat, UpperTriangularize[ConstantArray[1, Dimensions[mat]], 1], 1]

(* added later, also slower than takeUpper3 *)
takeUpper4[mat_?SquareMatrixQ] :=
Join @@ Table[mat[[i, i + 1 ;;]], i, Length[mat]]


Benchmarks:



mat = RandomReal[1, 1000, 1000]; 
mat2 = Developer`FromPackedArray[mat];


Packed:



res1 = takeUpper1[mat]; // RepeatedTiming
(* 0.218, Null *)

res2 = takeUpper2[mat]; // RepeatedTiming
(* 0.0840, Null *)

res3 = takeUpper3[mat]; // RepeatedTiming
(* 0.017, Null *)

res1 == res2 == res3
(* True *)


Non-packed:



res1 = takeUpper1[mat2]; // RepeatedTiming
(* 0.839, Null *)

res2 = takeUpper2[mat2]; // RepeatedTiming
(* 0.0949, Null *)

res3 = takeUpper3[mat2]; // RepeatedTiming
(* 0.018, Null *)

res1 == res2 == res3
(* True *)


You are also welcome to suggest intuitive names for such a function. The final function will work also on non-square matrices and will have a second argument similar to that of UpperTriangularize.










share|improve this question























  • Are you interested in only positive second arguments?
    – Carl Woll
    Nov 9 at 17:08











  • @CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
    – Szabolcs
    Nov 11 at 19:35












up vote
7
down vote

favorite
2









up vote
7
down vote

favorite
2






2





I would like to extract the upper triangular part of a square matrix into a flat list. I am looking for fast ways to do this. I am primarily interested in solutions that are fast for non-packed arrays as well. For packed arrays, a compiled solution will always be very fast.



Here are three successively faster implementations to get the ball rolling.



takeUpper1[mat_?SquareMatrixQ] := 
Join @@ Table[mat[[i, j]], i, Length[mat], j, i + 1, Length[mat]];

takeUpper2[mat_?SquareMatrixQ] :=
Extract[mat, Subsets[Range@Length[mat], 2]]

takeUpper3[mat_?SquareMatrixQ] :=
Join @@ Pick[mat, UpperTriangularize[ConstantArray[1, Dimensions[mat]], 1], 1]

(* added later, also slower than takeUpper3 *)
takeUpper4[mat_?SquareMatrixQ] :=
Join @@ Table[mat[[i, i + 1 ;;]], i, Length[mat]]


Benchmarks:



mat = RandomReal[1, 1000, 1000]; 
mat2 = Developer`FromPackedArray[mat];


Packed:



res1 = takeUpper1[mat]; // RepeatedTiming
(* 0.218, Null *)

res2 = takeUpper2[mat]; // RepeatedTiming
(* 0.0840, Null *)

res3 = takeUpper3[mat]; // RepeatedTiming
(* 0.017, Null *)

res1 == res2 == res3
(* True *)


Non-packed:



res1 = takeUpper1[mat2]; // RepeatedTiming
(* 0.839, Null *)

res2 = takeUpper2[mat2]; // RepeatedTiming
(* 0.0949, Null *)

res3 = takeUpper3[mat2]; // RepeatedTiming
(* 0.018, Null *)

res1 == res2 == res3
(* True *)


You are also welcome to suggest intuitive names for such a function. The final function will work also on non-square matrices and will have a second argument similar to that of UpperTriangularize.










share|improve this question















I would like to extract the upper triangular part of a square matrix into a flat list. I am looking for fast ways to do this. I am primarily interested in solutions that are fast for non-packed arrays as well. For packed arrays, a compiled solution will always be very fast.



Here are three successively faster implementations to get the ball rolling.



takeUpper1[mat_?SquareMatrixQ] := 
Join @@ Table[mat[[i, j]], i, Length[mat], j, i + 1, Length[mat]];

takeUpper2[mat_?SquareMatrixQ] :=
Extract[mat, Subsets[Range@Length[mat], 2]]

takeUpper3[mat_?SquareMatrixQ] :=
Join @@ Pick[mat, UpperTriangularize[ConstantArray[1, Dimensions[mat]], 1], 1]

(* added later, also slower than takeUpper3 *)
takeUpper4[mat_?SquareMatrixQ] :=
Join @@ Table[mat[[i, i + 1 ;;]], i, Length[mat]]


Benchmarks:



mat = RandomReal[1, 1000, 1000]; 
mat2 = Developer`FromPackedArray[mat];


Packed:



res1 = takeUpper1[mat]; // RepeatedTiming
(* 0.218, Null *)

res2 = takeUpper2[mat]; // RepeatedTiming
(* 0.0840, Null *)

res3 = takeUpper3[mat]; // RepeatedTiming
(* 0.017, Null *)

res1 == res2 == res3
(* True *)


Non-packed:



res1 = takeUpper1[mat2]; // RepeatedTiming
(* 0.839, Null *)

res2 = takeUpper2[mat2]; // RepeatedTiming
(* 0.0949, Null *)

res3 = takeUpper3[mat2]; // RepeatedTiming
(* 0.018, Null *)

res1 == res2 == res3
(* True *)


You are also welcome to suggest intuitive names for such a function. The final function will work also on non-square matrices and will have a second argument similar to that of UpperTriangularize.







list-manipulation performance-tuning






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 9 at 13:59

























asked Nov 9 at 13:46









Szabolcs

156k13428915




156k13428915











  • Are you interested in only positive second arguments?
    – Carl Woll
    Nov 9 at 17:08











  • @CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
    – Szabolcs
    Nov 11 at 19:35
















  • Are you interested in only positive second arguments?
    – Carl Woll
    Nov 9 at 17:08











  • @CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
    – Szabolcs
    Nov 11 at 19:35















Are you interested in only positive second arguments?
– Carl Woll
Nov 9 at 17:08





Are you interested in only positive second arguments?
– Carl Woll
Nov 9 at 17:08













@CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
– Szabolcs
Nov 11 at 19:35




@CarlWoll Also negative ones, but I think that would be too much for this question. This question was only for the square case, no second argument, and focusing on performance. I was useful to learn about UpperTriangularMatrixToVector.
– Szabolcs
Nov 11 at 19:35










2 Answers
2






active

oldest

votes

















up vote
11
down vote



accepted










Is using an internal, undocumented symbol acceptable?



r1 = Statistics`Library`UpperTriangularMatrixToVector[mat]; //RepeatedTiming
r2 = Statistics`Library`UpperTriangularMatrixToVector[mat2]; //RepeatedTiming
r3 = takeUpper3[mat]; //RepeatedTiming
r4 = takeUpper3[mat2]; //RepeatedTiming

r1 === r2 === r3 === r4



0.00039, Null



0.0027, Null



0.017, Null



0.018, Null



True







share|improve this answer




















  • Wow! That's fast. Have to remember that one.
    – Henrik Schumacher
    Nov 9 at 15:12






  • 2




    Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
    – Szabolcs
    Nov 9 at 15:30










  • Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
    – Szabolcs
    Nov 9 at 16:51

















up vote
5
down vote













I tried the following; for packed arrays, it appears to be on par with takeUpper3, but it needs twice the time for unpacked arrays. So I think, your trick using Pick is already pretty good.



LinearToTriangularIndexing[k_?VectorQ, n_Integer] := Module[i, j,
i = n - 1 - Floor[Sqrt[4. n (n - 1) - 8. k + 1.]/2.0 - 0.5];
j = Subtract[
k + i + Quotient[Subtract[n + 1, i] Subtract[n, i], 2],
Quotient[n (n - 1), 2]];
Transpose[i, j]
];

takeUpper5[mat_?SquareMatrixQ] := With[n = Length[mat],
Extract[mat, LinearToTriangularIndexing[Range[1, n (n - 1)/2], n]]
]





share|improve this answer




















  • For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
    – Szabolcs
    Nov 9 at 14:11










Your Answer





StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
);
);
, "mathjax-editing");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "387"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f185690%2ftake-upper-triangular-part-of-matrix%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
11
down vote



accepted










Is using an internal, undocumented symbol acceptable?



r1 = Statistics`Library`UpperTriangularMatrixToVector[mat]; //RepeatedTiming
r2 = Statistics`Library`UpperTriangularMatrixToVector[mat2]; //RepeatedTiming
r3 = takeUpper3[mat]; //RepeatedTiming
r4 = takeUpper3[mat2]; //RepeatedTiming

r1 === r2 === r3 === r4



0.00039, Null



0.0027, Null



0.017, Null



0.018, Null



True







share|improve this answer




















  • Wow! That's fast. Have to remember that one.
    – Henrik Schumacher
    Nov 9 at 15:12






  • 2




    Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
    – Szabolcs
    Nov 9 at 15:30










  • Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
    – Szabolcs
    Nov 9 at 16:51














up vote
11
down vote



accepted










Is using an internal, undocumented symbol acceptable?



r1 = Statistics`Library`UpperTriangularMatrixToVector[mat]; //RepeatedTiming
r2 = Statistics`Library`UpperTriangularMatrixToVector[mat2]; //RepeatedTiming
r3 = takeUpper3[mat]; //RepeatedTiming
r4 = takeUpper3[mat2]; //RepeatedTiming

r1 === r2 === r3 === r4



0.00039, Null



0.0027, Null



0.017, Null



0.018, Null



True







share|improve this answer




















  • Wow! That's fast. Have to remember that one.
    – Henrik Schumacher
    Nov 9 at 15:12






  • 2




    Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
    – Szabolcs
    Nov 9 at 15:30










  • Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
    – Szabolcs
    Nov 9 at 16:51












up vote
11
down vote



accepted







up vote
11
down vote



accepted






Is using an internal, undocumented symbol acceptable?



r1 = Statistics`Library`UpperTriangularMatrixToVector[mat]; //RepeatedTiming
r2 = Statistics`Library`UpperTriangularMatrixToVector[mat2]; //RepeatedTiming
r3 = takeUpper3[mat]; //RepeatedTiming
r4 = takeUpper3[mat2]; //RepeatedTiming

r1 === r2 === r3 === r4



0.00039, Null



0.0027, Null



0.017, Null



0.018, Null



True







share|improve this answer












Is using an internal, undocumented symbol acceptable?



r1 = Statistics`Library`UpperTriangularMatrixToVector[mat]; //RepeatedTiming
r2 = Statistics`Library`UpperTriangularMatrixToVector[mat2]; //RepeatedTiming
r3 = takeUpper3[mat]; //RepeatedTiming
r4 = takeUpper3[mat2]; //RepeatedTiming

r1 === r2 === r3 === r4



0.00039, Null



0.0027, Null



0.017, Null



0.018, Null



True








share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 9 at 15:11









Carl Woll

65k285170




65k285170











  • Wow! That's fast. Have to remember that one.
    – Henrik Schumacher
    Nov 9 at 15:12






  • 2




    Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
    – Szabolcs
    Nov 9 at 15:30










  • Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
    – Szabolcs
    Nov 9 at 16:51
















  • Wow! That's fast. Have to remember that one.
    – Henrik Schumacher
    Nov 9 at 15:12






  • 2




    Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
    – Szabolcs
    Nov 9 at 15:30










  • Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
    – Szabolcs
    Nov 9 at 16:51















Wow! That's fast. Have to remember that one.
– Henrik Schumacher
Nov 9 at 15:12




Wow! That's fast. Have to remember that one.
– Henrik Schumacher
Nov 9 at 15:12




2




2




Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
– Szabolcs
Nov 9 at 15:30




Yes, it is acceptable. But it obsoletes all the work I did on this so far ... This stuff should really be documented.
– Szabolcs
Nov 9 at 15:30












Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
– Szabolcs
Nov 9 at 16:51




Just a note that this function does not simply re-pack mat2 and use the implementation for packed arrays. I tested this by trying it on matrices of strings. It is still fast.
– Szabolcs
Nov 9 at 16:51










up vote
5
down vote













I tried the following; for packed arrays, it appears to be on par with takeUpper3, but it needs twice the time for unpacked arrays. So I think, your trick using Pick is already pretty good.



LinearToTriangularIndexing[k_?VectorQ, n_Integer] := Module[i, j,
i = n - 1 - Floor[Sqrt[4. n (n - 1) - 8. k + 1.]/2.0 - 0.5];
j = Subtract[
k + i + Quotient[Subtract[n + 1, i] Subtract[n, i], 2],
Quotient[n (n - 1), 2]];
Transpose[i, j]
];

takeUpper5[mat_?SquareMatrixQ] := With[n = Length[mat],
Extract[mat, LinearToTriangularIndexing[Range[1, n (n - 1)/2], n]]
]





share|improve this answer




















  • For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
    – Szabolcs
    Nov 9 at 14:11














up vote
5
down vote













I tried the following; for packed arrays, it appears to be on par with takeUpper3, but it needs twice the time for unpacked arrays. So I think, your trick using Pick is already pretty good.



LinearToTriangularIndexing[k_?VectorQ, n_Integer] := Module[i, j,
i = n - 1 - Floor[Sqrt[4. n (n - 1) - 8. k + 1.]/2.0 - 0.5];
j = Subtract[
k + i + Quotient[Subtract[n + 1, i] Subtract[n, i], 2],
Quotient[n (n - 1), 2]];
Transpose[i, j]
];

takeUpper5[mat_?SquareMatrixQ] := With[n = Length[mat],
Extract[mat, LinearToTriangularIndexing[Range[1, n (n - 1)/2], n]]
]





share|improve this answer




















  • For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
    – Szabolcs
    Nov 9 at 14:11












up vote
5
down vote










up vote
5
down vote









I tried the following; for packed arrays, it appears to be on par with takeUpper3, but it needs twice the time for unpacked arrays. So I think, your trick using Pick is already pretty good.



LinearToTriangularIndexing[k_?VectorQ, n_Integer] := Module[i, j,
i = n - 1 - Floor[Sqrt[4. n (n - 1) - 8. k + 1.]/2.0 - 0.5];
j = Subtract[
k + i + Quotient[Subtract[n + 1, i] Subtract[n, i], 2],
Quotient[n (n - 1), 2]];
Transpose[i, j]
];

takeUpper5[mat_?SquareMatrixQ] := With[n = Length[mat],
Extract[mat, LinearToTriangularIndexing[Range[1, n (n - 1)/2], n]]
]





share|improve this answer












I tried the following; for packed arrays, it appears to be on par with takeUpper3, but it needs twice the time for unpacked arrays. So I think, your trick using Pick is already pretty good.



LinearToTriangularIndexing[k_?VectorQ, n_Integer] := Module[i, j,
i = n - 1 - Floor[Sqrt[4. n (n - 1) - 8. k + 1.]/2.0 - 0.5];
j = Subtract[
k + i + Quotient[Subtract[n + 1, i] Subtract[n, i], 2],
Quotient[n (n - 1), 2]];
Transpose[i, j]
];

takeUpper5[mat_?SquareMatrixQ] := With[n = Length[mat],
Extract[mat, LinearToTriangularIndexing[Range[1, n (n - 1)/2], n]]
]






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 9 at 14:02









Henrik Schumacher

44.9k265130




44.9k265130











  • For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
    – Szabolcs
    Nov 9 at 14:11
















  • For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
    – Szabolcs
    Nov 9 at 14:11















For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
– Szabolcs
Nov 9 at 14:11




For packed arrays, I have a C solution that runs un 0.0007 s for this matrix. This is why I am focused on things that are not packable.
– Szabolcs
Nov 9 at 14:11

















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f185690%2ftake-upper-triangular-part-of-matrix%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kleinkühnau

Makov (Slowakei)

Deutsches Schauspielhaus