55

For example, like this:

enter image description here

I know Join works, but it is a bit troublesome for multiple matrices. I also tried DiagonalMatrix, but it can only form a matrix from a list of elements.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
novice
  • 2,325
  • 1
  • 24
  • 30

3 Answers3

68

ybeltukov's blockArray from Speeding up generation of block diagonal matrix blows the methods below out of the water by orders of magnitude, in terms of performance.


a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

b = {{1, 2}, {3, 4}};

ArrayFlatten[{{a, 0}, {0, b}}] // MatrixForm

Mathematica graphics

You can Fold this operation over a list of matrices to get a diagonal:

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
b = {{1, 2}, {3, 4}};
c = {{1, 2, 3}, {4, 5, 6}};
d = {{1, 2}, {3, 4}, {5, 6}};

Fold[ArrayFlatten[{{#, 0}, {0, #2}}] &, a, {b, c, d}] // MatrixForm

Mathematica graphics

Here is another way to do this, illustrating a forcing of DiagonalMatrix by using an arbitrary head (Hold) on top of List:

DiagonalMatrix[Hold /@ {a, b, c, d}] // ReleaseHold // ArrayFlatten // MatrixForm

(same output)

Or a bit more cleanly using Unevaluated (though this may be harder to apply in a program as opposed to interactive input because the elements of your matrix list will probably not be named):

DiagonalMatrix[Unevaluated @ {a, b, c, d}] // ArrayFlatten // MatrixForm

(same output)

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Mr.Wizard What about a list of Matrices? I only examplified two matrices to form one block-diagonal matrix. Better automatic method. – novice Feb 18 '13 at 09:31
  • @user5463 what do you mean? – Mr.Wizard Feb 18 '13 at 09:31
  • Suppose my matrices are not predefined, but generated in the middle of my program. – novice Feb 18 '13 at 09:37
  • @user5463 see my updated answer; is that what you want? – Mr.Wizard Feb 18 '13 at 09:43
  • @user5463 You're welcome. I added a second method I think you will find interesting. – Mr.Wizard Feb 18 '13 at 09:53
  • @Mr.Wizard, it's been a while since I used ArrayFlatten but if I remember correctly it uses quite a bit of memory and was somewhat slow for large matrices. –  Feb 18 '13 at 18:49
  • @ruebenko ArrayFlatten is best for packed arrays and slower otherwise. I cannot recall it being a memory hog though of course I expect a SparseArray to be much better there. – Mr.Wizard Feb 18 '13 at 22:01
  • @Mr.Wizard, yea, I think I tried ArrayFlatten with SparseArray and that did not work to well memory wise. If I recall correctly, the best was to create an empty SparseArray and then set the respective parts. If you ever come across an example where this is an issue let me know. Thanks any ways. –  Feb 19 '13 at 10:22
  • I have to say your Fold and Unevaluated make me shine at the moment.Can you give a same amazing solution in this topic about the opposite operation of you? :) – yode Jul 12 '16 at 12:23
  • @yode I saw that question when it was posted and nothing useful came to mind. However your use of "BoundingBoxes" is great! So thanks for the kind words but you've surely outdone me this time. :-) – Mr.Wizard Jul 12 '16 at 22:36
  • @Mr.Wizard Brilliant answer. Thanks a lot. But why the last 'Unevaluated' one doesn't work if we replace {a,b,c,d} by something like Table{a,4}? However, the 'Hold' one works. – xiaohuamao Apr 17 '17 at 14:26
  • 1
    @huotuichang Let me see if I can explain in a comment. Diagonal needs to "see" a simple vector (list) of elements (i.e. not matrices themselves) for it to work in the manner I need here. Unevaluated @ {a, b, c, d} works because {a, b, c, d} is expressly a vector of elements, and Unevaluated keeps the evaluator from changing it before Diagonal "sees" it. However Table[. . .] is not expressly a vector and therefore Diagonal doesn't know how to operate on it. This is what I meant by "may be harder to apply in a program." (continued) – Mr.Wizard Apr 17 '17 at 16:26
  • Hold /@ Table[. . .] should work because first the Table evaluates, then Hold wraps each individual element making Diagonal "see" a simple vector even if elements (now wrapped in Hold) are themselves deeper arrays.. – Mr.Wizard Apr 17 '17 at 16:28
  • @Mr.Wizard Thank you for the clarification! – xiaohuamao Apr 18 '17 at 12:28
39

Update: Just bumped into this: SparseArray`SparseBlockMatrix:

bmF = With[{r = MapIndexed[#2[[1]] {1, 1}-># &, #, 1]}, SparseArray`SparseBlockMatrix[r]]&;

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
b = {{1, 2}, {3, 4}};
c = {{x, y, z}, {u, v, w}};

bmF[{a, b, c}]  // MatrixForm

Mathematica graphics

Original post:

 diagF = With[{dims = Total@(Dimensions /@ {##})}, 
    SparseArray[Band[{1, 1}, dims] -> {##}, dims]] &;

Edit: Much more elegant form (thanks to Mr.Wizard)

 diagF = SparseArray[Band[{1, 1}] -> {##}] &

Example:

 a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
 b = {{1, 2}, {3, 4}};
 c = {{1, 2, 3}, {4, 5, 6}};
 d = {{1, 2}, {3, 4}, {5, 6}};
 diagF[a, b, d, b, c] // MatrixForm

enter image description here

kglr
  • 394,356
  • 18
  • 477
  • 896
  • 1
    I forgot about that. Nice. – Mr.Wizard Feb 18 '13 at 10:16
  • 2
    If I am not mistaken this function can be reduced to: diagF = SparseArray[Band[{1, 1}] -> {##}] & – Mr.Wizard Feb 18 '13 at 10:21
  • 1
    @Mr.Wizard, you are right! (somehow I did not get that form working when I first tried; need to use ClearAll more often.) – kglr Feb 18 '13 at 10:31
  • 4
    +1 Great solution. It may be worth pointing out, though, that the example is not a block-diagonal matrix. By definition, a block-diagonal matrix represents an endomorphism of a product of vector spaces in which each component space is mapped to itself; ergo, the blocks must be square. But it is evident that this solution will work correctly when its input matrices are all square; it can be thought of as a generalization of the block-diagonal form in which the matrix represents a Cartesian product of arbitrary linear maps (rather than just endomorphisms). – whuber Feb 18 '13 at 18:10
  • @whuber, thanks. Great observation. – kglr Feb 18 '13 at 18:32
  • @kguler diagF fails when I'm running diagF[{0, 1, 0}, {1, 0, 0}]. Is it because Mathematica treat row vector and column vector the same way? – novice Mar 12 '13 at 03:11
  • @user5463, what is the expected output from diagF[{0, 1, 0}, {1, 0, 0}]? Does diagF[{{0, 1, 0}}, {{1, 0, 0}}] give what is expected? – kglr Mar 12 '13 at 05:03
  • MatrixForm[SparseArray[Automatic, {6, 2}, 0, {1, {{0, 0, 1, 1, 2, 2, 2}, {{1}, {2}}}, {1, 1}}]] is what i want. – novice Mar 12 '13 at 05:45
  • @user5463, you can use diagF[{{0}, {1}, {0}}, {{1}, {0}, {0}}] or diagF[Transpose@{{0, 1, 0}}, Transpose@{{1, 0, 0}}] to get column matrices on the "diagonal". – kglr Mar 12 '13 at 06:06
  • @kguler Yes that is what i want, but I'm wondering why diagF fails in this case. – novice Mar 12 '13 at 07:05
  • @user5463, each argument to diagF need to be a 2d array (in other words, ArrayDepth[argi] needs to be 2 for each array argi in the argument sequence). – kglr Mar 12 '13 at 07:18
  • @kguler I noticed that, I want to rewritie diagF[] to include the 1d list situation, Any advice? – novice Mar 12 '13 at 07:27
  • @user5463, if you want to use mixtures of kX1, 1Xm and nXp arrays as input the most convenient way I can think of is the current form of diagF: for example, diagF[(* a 3X1 array *){{x}, {y}, {z}},(* a 2X3 array *){{a, b, c}, {d, e, f}},(* and a 1X3 array *){{r, s, t}}]. – kglr Mar 12 '13 at 08:06
  • ... or, if you wish to get a version that takes only columns as input you can define diagColF = diagF @@ (List /@ # & /@ {##}) & and use as diagColF[{0, 1, 0}, {1, 0, 0}]. Similarly, define diagRowF = diagF @@ (List /@ {##}) & for a version that takes rows as input and use as diagRowF[{0, 1, 0}, {1, 0, 0}]. Hope this helps. – kglr Mar 12 '13 at 08:11
  • What's wrong with CirclePlus := SparseArray[Band[{1, 1}] -> {##}] &; a_\[CirclePlus]b_ := CirclePlus[a, b] – Eden Harder Jan 07 '14 at 09:12
  • Hi, kglr. It fails for sparse block. For a fix, diagF = SparseArray[Band[{1, 1}] -> Normal /@ {##}] & – matheorem Apr 21 '16 at 05:56
  • @matheorem, it may be a version problem -- In version 9 it works when a,b,c,d are SparseArrays. – kglr Apr 21 '16 at 06:17
  • Oops, I am wrong. It is working for SparseArray, while not working for SymmetrizedArray type. Check it again : ) – matheorem Apr 21 '16 at 06:42
3

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

b = {{1, 2}, {3, 4}}; SparseArray[Band[{1, 1}] -> {a, b}] // MatrixForm

enter image description here

Reda.Kebbaj
  • 674
  • 3
  • 13