[Tex/LaTex] Obstacles to simulating an amsmath matrix by a TiKZ matrix of math nodes

amsmathmatricestikz-pgf

I'm trying to figure out a TiKZ-based solution to this problem involving arrows between matrix rows. To this end, I would like to at least be able to make a matrix of math nodes simulate the behaviour of the matrix environment from amsmath.

I have encountered several problems in doing so. One of them is the appearance of the matrices themselves and their relation to the surrounding text. Consider the following example:

\documentclass{article}
\usepackage{amsmath,tikz}
\usetikzlibrary{matrix}

\newlength\mtxrowsep   \setlength\mtxrowsep{2ex}
\newlength\mtxcolsep   \setlength\mtxcolsep{\arraycolsep}

\begin{document}
\begin{gather*}
    4 \times
    \left[\begin{matrix} a & b \\ c & d \end{matrix}\right]
\qquad
    4 \times
    \left[\begin{tikzpicture}
      \matrix [matrix of math nodes, ampersand replacement=\&,
               inner sep=0pt, column sep=\mtxcolsep, row sep=\mtxrowsep]
          { a \& b \\ c \& d \\ };
    \end{tikzpicture}\right]
\qquad
    4 \times
    \left[\begin{minipage}{1.7em}
      {}\hfill\begin{tikzpicture}
        \matrix [matrix of math nodes, ampersand replacement=\&,
                 inner sep=0pt, column sep=\mtxcolsep, row sep=\mtxrowsep]
                { a \& b \\ c \& d \\   };
      \end{tikzpicture}\hfill{}
    \end{minipage}\right]
\end{gather*}
\end{document}

This produces the following output:

comparison of matrices

The genuine amsmath matrix is on the left. The matrix in the middle suffers from the fact that the matrix of math nodes in the tikzpicture environment is apparently sitting on top of the baseline of the surrounding math. The matrix on the right is properly aligned because it is sitting inside of a minipage, whose width was manually specified. (Not pictured is anything using left delimiter or right delimiter, which has spacing issues of its own and suffers from the same vertical alignment issues.)

Main questions.

  1. Is there any way native to tikz to change the vertical alignment of the tikzpicture for the matrix in the middle? (I didn't find anything whilst scanning through the manual…)

  2. Is there any way to extract the width of an image automatically, to allow automatic resizing of the minipage for the matrix on the right?

Best Answer

Okay, here's my first attempt. It needs a little tweaking with spacing either side, and I've only tested it with the 2x2 matrix given and without any font size changes, so some of the choices for lengths might be amiss.

The vertical centring is handled by setting the baseline option on the enclosing tikzpicture. We want the actual centre of the matrix to be a bit above the baseline (about the middle of an =, I guess). Trial and error gave me a height of .7ex. Next is the delimiters. TikZ can handle them itself, so I figured it best to let it deal with them rather than putting stuff in boxes or minipages (the node text is already in boxes and so forth so putting in more seems overkill). I found the delimiters to be vertically a little off, so I shifted them down a point, and also a little far out. I didn't want to touch the inner sep as that changes both vertical and horizontal, so I simply shifted them in a little. These are the every (left|right|) delimiter styles. I wanted a little inner sep on the matrix itself but not on the entries themselves. I also adjusted the row and column separations a little to better match the AMS styles.

Next was the ampersand. I'm a little wary of this, but what I do is to change it to an active character. It checks to see if it is inside a TikZ matrix. If it is, then it produces \pgfmatrixnextcell and if not, it produces the old ampersand. This could be made a little more robust by only checking for if we're in one of our special TikZ matrices. I don't know if this is a good solution or not.

After that, it's simply a matter of wrapping it all up in an environment.

Let's start with the result. In each row, the first is a tikzpicture, the second is the same but wrapped in to an environment, the third is the AMS original.

tikz matrix replacement

And now the code:

\documentclass{standalone}
%\url{http://tex.stackexchange.com/q/26866/86}
\usepackage{amsmath,tikz}
\usetikzlibrary{matrix}

\newlength\mtxrowsep   \setlength\mtxrowsep{1.5ex}
\newlength\mtxcolsep   \setlength\mtxcolsep{2\arraycolsep}

\tikzset{
  ams/.style={
    baseline=-.7ex,
    every delimiter/.style={yshift=-1pt},
    every left delimiter/.style={xshift=2pt},
    every right delimiter/.style={xshift=-2pt},
    every node/.style={inner sep=0pt},
  },
  ams matrix/.style={
    inner sep=1pt,
    column sep=\mtxcolsep,
    row sep=\mtxrowsep,
%    ampersand replacement=\&,
    matrix of math nodes,
  },
  bmatrix/.style={
    ams,
    every matrix/.style={
      ams matrix,
      left delimiter={[},
      right delimiter={]},
    }
  },
  Bmatrix/.style={
    ams,
    every matrix/.style={
      ams matrix,
      left delimiter={\lbrace},
      right delimiter={\rbrace},
    }
  },
  pmatrix/.style={
    ams,
    every matrix/.style={
      ams matrix,
      left delimiter={(},
      right delimiter={)},
    }
  },
  vmatrix/.style={
    ams,
    every matrix/.style={
      ams matrix,
      left delimiter={|},
      right delimiter={|},
    }
  },
  Vmatrix/.style={
    ams,
    every matrix/.style={
      ams matrix,
      left delimiter={\|},
      right delimiter={\|},
    }
  },
}

\let\matamp=&

\catcode`\&=13
\makeatletter
\def&{\iftikz@is@matrix
  \pgfmatrixnextcell
  \else
  \matamp
  \fi}
\makeatother

%\usepackage{environ}
\def\endtikzmatrix{\\\egroup;\end{tikzpicture}}
\foreach \mtype in {b,B,p,v,V} {

\expandafter\xdef\csname tikz\mtype matrix\endcsname{%
    \noexpand\begin{tikzpicture}[\mtype matrix]
    \noexpand\matrix \noexpand\bgroup}
\expandafter\global\expandafter\let\csname endtikz\mtype matrix\endcsname=\endtikzmatrix
}

\begin{document}
\foreach \mtype in {b,B,p,v,V} {
  \edef\metype{\mtype matrix}
  \edef\tmetype{tikz\mtype matrix}
\begin{gather*}
\begin{tikzpicture}[\metype]
\matrix (m) {
  a & b \\ c & d \\};
\end{tikzpicture}
\begin{\tmetype} a & b \\ c & d \end{\tmetype}
\begin{\metype} a & b \\ c & d \end{\metype}
\end{gather*}
}

\end{document}

(The \foreach loops are just to save me cut-and-pasting a lot.)