8

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

credwood
  • 81
  • 1

3 Answers3

8

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

DumpsterDoofus
  • 11,857
  • 1
  • 29
  • 49
7
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

kglr
  • 394,356
  • 18
  • 477
  • 896
6

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.

ybeltukov
  • 43,673
  • 5
  • 108
  • 212
  • 1
    So the most ridiculously complex and labored solution is the fastest? Just avoid functions that are supposed to be optimized for this? Yikes! – Zibadawa Sep 15 '14 at 04:09