8
$\begingroup$

This question already has an answer here:

I'm looking to generate a tridiagonal 50x50 matrix, ideally without using loops. Suggestions on most efficient code for this?

$\endgroup$

marked as duplicate by Mr.Wizard Sep 15 '14 at 5:19

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 5
    $\begingroup$ DiagonalMatrix[list,k] $\endgroup$ – Gregory Rut Sep 14 '14 at 17:15
  • 2
    $\begingroup$ I think this question is a duplicate of: (13004) -- please review that question and if you feel this is not a duplicate explain why. $\endgroup$ – Mr.Wizard Sep 15 '14 at 5:18
  • $\begingroup$ Your question is closed because its a duplicate , probably you should read the policy about that. $\endgroup$ – rhermans Sep 15 '14 at 16:49
8
$\begingroup$

As Gregory Rut mentioned, DiagonalMatrix already has built-in support for generating banded matrices from lists:

Inner[DiagonalMatrix, RandomInteger[{0, 5}, #] & /@ {49, 50, 49}, {-1, 0, 1}]

which yields the following (when ArrayPlot is applied):

enter image description here

$\endgroup$
  • 1
    $\begingroup$ I just start writing that Inner will be more elegant then explicit sum! :) $\endgroup$ – ybeltukov Sep 14 '14 at 17:50
  • $\begingroup$ @ybeltukov I believe a disadvantage of Inner is that more memory will be used. $\endgroup$ – Mr.Wizard Sep 15 '14 at 5:24
7
$\begingroup$
diag = RandomChoice[CharacterRange["a", "z"], #] & /@ {49, 50, 49};
m = SparseArray[Inner[Rule, {Band[{1, 2}], Band[{1, 1}], Band[{2, 1}]}, diag, List]]

MatrixPlot[m, Mesh -> True]

enter image description here

Timings: for a 50X50 matrix the timings of two methods based on Band and DiagonalMatrix are both 0. For larger matrices, Band is much faster:

ClearAll[f1, f2];
f1 = SparseArray[Inner[Rule, {Band[{1, 2}], Band[{1, 1}], Band[{2, 1}]}, #, List]] &;
f2 = Inner[DiagonalMatrix, #, {1, 0, -1}] &;
functions = {f1, f2};

testdata1 = RandomReal[5, #] & /@ {49, 50, 49};
testdata2 = RandomReal[5, #] & /@ {499, 500, 499};
testdata3 = RandomReal[5, #] & /@ {4999, 5000, 4999};
testdata4 = RandomReal[5, #] & /@ {9999, 10000, 9999};

(Equal @@ (Through@functions@#)) & /@ {testdata1, testdata2, testdata3, testdata4}
(* {True,True,True, True} *)

timings = Outer[N[First[AbsoluteTiming[#@#2;]], 5] &, 
                functions, {testdata1, testdata2, testdata3, testdata4}, 1];
TableForm[timings, TableHeadings -> {{Band, DiagonalMatrix},
                                     {"n = 50", "n = 500", "n = 5000", "n = 10000"}}]

enter image description here

$\endgroup$
  • $\begingroup$ Unfortunately, Band is not so efficient. For example, f3 = Inner[DiagonalMatrix[SparseArray@#, #2] &, #, {1, 0, -1}] & is 10 times faster. $\endgroup$ – ybeltukov Sep 14 '14 at 20:33
  • $\begingroup$ @ybeltukov, really nice! I suggest you should post that as an answer. $\endgroup$ – kglr Sep 14 '14 at 20:49
  • $\begingroup$ Done! I went further :) $\endgroup$ – ybeltukov Sep 14 '14 at 21:36
  • $\begingroup$ @ybeltukov and kguler: I marked this question as a duplicate. Please review my action. If you disagree or feel another action should be taken let me know. $\endgroup$ – Mr.Wizard Sep 15 '14 at 5:22
  • $\begingroup$ @Mr.Wizard, i agree. Btw, do you remember if the linked q/a was on the Related list on the right before you linked to it? $\endgroup$ – kglr Sep 15 '14 at 5:39
6
$\begingroup$

Here is my expanded response to kguler.

I noticed that usually Band is less effective then DiagonalMatrix@SparseArray or manual constructing of the resulting SparseArray

f1 = SparseArray[Inner[Rule, {Band[{1, 2}], Band[{1, 1}], Band[{2, 1}]}, #, List]] &;
f2 = Inner[DiagonalMatrix, #, {1, 0, -1}] &;;
f3 = Inner[DiagonalMatrix[SparseArray@#, #2] &, #, {1, 0, -1}] &;
f4 = SparseArray[Join[Transpose@{Most@#, Rest@#}, Transpose@{#, #}, 
        Transpose@{Rest@#, Most@#}] &@Range@Length@#[[2]] -> Join @@ #] &;
f5 = With[{n = Length@#[[2]]}, SparseArray @@ {Automatic, {n, n}, 0.,
      {1,
       {Join[{0}, Range[2, 3 n - 3, 3], {3 n - 2}],
        Transpose@{Flatten[Transpose@Range[{0, 1, 2}, {n - 1, n, n + 1}]][[2 ;; -2]]}},
       Flatten[Transpose@{Prepend[#[[3]], 0.], #[[2]], Append[#[[1]], 0.]}][[2 ;; -2]]
       }}] &;
functions = {f1, f2, f3, f4, f5};

testdata = RandomReal[5, #] & /@ {# - 1, #, # - 1} & /@ {50, 500, 5000, 10000};

(Equal @@ (Through@functions@#)) & /@ testdata
(* {True, True, True, True} *)


timings = Outer[N[First[AbsoluteTiming[#@#2;]], 5] &, functions, testdata, 1];
TableForm[timings, TableHeadings -> {{"f1", "f2", "f3", "f4", "f5"}, {"n = 50", 
    "n = 500", "n = 5000", "n = 10000"}}]

enter image description here

Functions:

  1. kguler's solution with SparseArray and Band

  2. DumpsterDoofus's DiagonalMatrix with dense matrices

  3. DiagonalMatrix with SparseArray

  4. Manual creating of Sparse array with SparseArray[indexes -> values]

  5. Completely manual creating of SparseArray as SparseArray[Automatic, dimensions, defaultElement, {1, {splitting, columns}, values}]. You can learn this format by investigating InputForm of any available SparseArray.

$\endgroup$
  • 1
    $\begingroup$ So the most ridiculously complex and labored solution is the fastest? Just avoid functions that are supposed to be optimized for this? Yikes! $\endgroup$ – Zibadawa Sep 15 '14 at 4:09

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