How do you Typeset block diagonal matrices?

I would like to typeset a block diagonal matrix similarly to how it is done in this Math Stack Exchange answer, I have tried nesting mat inside other mat but it doesn’t look too good

3 Likes

Far away from being a final solution, but using tables could be a workaround if matrix colspan/rowspan is not available.

$(
  #table(
    columns: (auto,) * 4,
    stroke: none,
    align: (center + horizon,) * 4,
    $40$, $0$, $0$, $0$,
    $0$, $80$, $100$, $0$,
    $0$, $40$, $120$, $0$,
    $0$, $0$, $0$, $60$,
  )
)^(-1) =
(
  #table(
    columns: (auto,) * 4,
    stroke: none,
    align: (center + horizon,) * 4,
    $(40)^(-1)$, $0$, $0$, $0$,
    $0$, table.cell(colspan: 2, rowspan: 2)[$mat(80, 100; 40, 120)^(-1)$], $0$,
    $0$, $0$,
    $0$, $0$, $0$, $(60)^(-1)$,
  )
)$

Note that [ and ] could have been used, and another inner table for the 2x2 matrix. There is some work to do to finalize proper padding / gap.

1 Like

Thank you, having the numbers of different sizes looks a bit off, I will try to play a bit with the paddings

Here is another increment, which I’d love to finish but can’t right now.

  • I’d like to use one mat, grid or table for all levels so that element alignment is correct

    • maybe keeping in mind that equation alignment markers is a fourth way that we haven’t explored
  • either we have to use rowspan in tables or we need to place brackets outside of the grid of cells. I’ve tried to do the latter.

This kind of works, but needs more work on the placement and sizes. I use smashop so that supscripts don’t disturb element alignment, but supscripts are clearly misplaced right now

// By Eric on forum
// but modified/(hackjob) for placement
#let smashop(body, side: center) = math.display(
  move(dy: -0.4em, dx: 0.2em,
  box(baseline: 0em, height: 0pt, width: 0pt, align(
    side.inv(),
    box(baseline: 0em, height: 1em, width: float.inf * 1pt, $ sscript(body) $))
  ))
)

// place big bracket (left), spanning n rows. Assuming constant row height.
#let brl(n, elt: none, inset: 5pt, delim: [\[], align: top + left, dx: -1em, sup: []) = context {
  let elt = elt
  if elt == none { elt = $ 1 $}
  let h = n * measure(math.display(elt)).height + (2*n)*inset
  place(align, dx: dx)[$lr(#delim, size: #h)^#sup$]
}

#let brr = brl.with(delim: [\]], align: top + right, dx: 1em)

  #table(
    columns: 4,
    stroke: none,
    align: (center + horizon),
    {brl(4); $[40]^smashop(-1)$ }, $0$, $0$, {$0$; brr(4)},
    $0$, {brl(2);$80$}, {$100$; brr(2, sup: $smashop(-1)$)}, $0$,
    $0$, $40$, $120$, $0$,
    $0$, $0$, $0$, $[60]^smashop(-1)$,
  )

The goal of the exercise is to find a good way to layout it, then after that find a nice way to express it as reusable functions.

3 Likes

Thank you, but this unfortunately results in some weird behavior when math numbering is enabled. I would prefer achieving the correct look in normal math mode, I personally don’t like too much the table approach, but this is the best I could come up with:

But it’s still not really ideal due to the spacing being a bit uneven and the font size being a bit small (which I’m not really sure how to change inside the matrix). The code is the following

#set math.mat(column-gap: 1em, delim: "[")
#set math.equation(numbering: "(1)")

$
  C_"2D" = mat(
    lambda + 2 mu, lambda, 0;
    lambda, lambda + 2 mu, 0;
    0, 0, mu
  )\ C_"3D" = mat(
    lambda + 2 mu, lambda, lambda, 0, 0, 0;
    lambda, lambda + 2 mu, lambda, 0, 0, 0;
    lambda, lambda, lambda + 2 mu, 0, 0, 0;
    0, 0, 0, mu, 0, 0;
    0, 0, 0, 0, mu, 0;
    0, 0, 0, 0, 0, mu
  )
$
$
  C_"2D"^(-1) = mat(
    column-gap: #0em,
    space space space mat(
      lambda + 2 mu, lambda;
      lambda, lambda + 2 mu
    )^(-1),
    mat(delim: #none, 0; 0);
    mat(delim: #none, column-gap: #2.5em, 0, 0), space space space mat(mu)^(-1)
  )\ C_"3D"^(-1) = mat(
    column-gap: #0em,
    space space space mat(
      lambda + 2 mu, lambda, lambda;
      lambda, lambda + 2 mu, lambda;
      lambda, lambda, lambda + 2 mu
    )^(-1),
    mat(column-gap: #2.5em, delim: #none, 0, 0, 0; 0, 0, 0; 0, 0, 0);
    mat(column-gap: #2.5em, delim: #none, 0, 0, 0; 0, 0, 0; 0, 0, 0),
    space space space mat(column-gap: #2.5em, mu, 0, 0; 0, mu, 0; 0, 0, mu)^(-1)
  )
$
1 Like

The spacing issue is probably non-trivial to fix but to fix the font size you can pass the matrix entries to math.display:

#let dmat(..args) = {
  if type(args.pos().first()) != array {
    return math.mat(
      ..args.named(),
      ..args.pos().map(math.display)
    )
  }
  return math.mat(
    ..args.named(),
    ..args.pos().map(i => i.map(math.display))
  )
}

$
  C_"2D"^(-1) = dmat(  // math.display sized matrix
    column-gap: #0em,
    space space space mat(
      lambda + 2 mu, lambda;
      lambda, lambda + 2 mu
    )^(-1),
    mat(delim: #none, 0; 0);
    mat(delim: #none, column-gap: #2.5em, 0, 0), space space space mat(mu)^(-1)
  ) 
$

1 Like

Oh, that’s a nice trick. With the spacing set to 3.5em for the matrices under the lambdas it looks basically perfect now. Thank you!

1 Like