Subject: Dirac bra-kets: subtler than you might think

From: [email-masked] (Michael Haggerty)
Date: 30 Aug 1994 02:41:40 GMT

Hello,

I've experimented for hours and finally found a general macro to get
TeX or LaTeX to output Dirac bra-ket notation as used in quantum
physics.  The problem is much subtler than it looks, as described
below.  My solution is still messy, and I'd welcome suggestions which
simplify the macros.  [There is a package on some sites called
"hep.tex" which does Dirac bra-kets, but it doesn't worry about
delimiter sizes---they're always small size---so it doesn't meet my
requirements.]

The goal is to write a macro so that

    $\braket{a}{b}$

generates something like:

    $\bigl< a \bigm| b \bigr>$

which of course looks like

    <a|b>

when typeset, a notation used frequently in quantum physics.  The big
problem (pun intended) is to get TeX to automatically choose the
correct size (big, bigg, Big, etc or some other size) of delimiter
satisfying: (1) appropriate to contain each of the two arguments 'a'
and 'b', which may be complex expressions, and (2) with all three
delimiters the same size.  [There is a package on some sites called
"hep.tex" which does Dirac bra-kets, but it doesn't attempt to get the
right delimiter sizes---they're always small size---so it doesn't meet
my requirements.]

[What is really called for is:

    \def\braket#1#2{\left< #1 \middle| #2 \right>}

but unfortunately there is no \middle operator.]

The first trick is to use \vphantom to force all of the delimiters to
be the same size:

    \def\braket#1#2{\left< #1 \vphantom{#2} \right|
                    \left. #2 \vphantom{#1} \right>}

Well, that's almost it---the delimiters are the right size, and
everything's where it belongs...except that a null delimiter (the
"\left.") actually causes TeX to put a little space, of width
\nulldelimiterspace, in the output.  So the result looks like:

    <a| b>

The obvious thing is to set \nulldelimiterspace=0pt; how to do it is a
little less obvious, though.  Simply putting it in the macro as
written fails, because math mode spacing is not computed until the
moment that math mode is left, which doesn't happen until after the
context of the macro has been left.  By that time our value of
\nulldelimiterspace has been restored back to its old value, so the
space is still there.  Still, following the example of the definitions
of \bigr, etc in plain.tex, we try the \hbox trick:

    \def\braket#1#2{\hbox{$\left< #1 \vphantom{#2} \right|
                           \left. #2 \vphantom{#1} \right>
                           \nulldelimiterspace=0pt
                           \mathsurround=0pt$}}

(the \mathsurround=0pt is to prevent TeX adding additional space
because of the "$ $" pair).  This is almost correct; the only
remaining problem is that the arguments are now set in \textstyle,
even if the original context was a \displaystyle.  So if, for example,
a fraction is put in the bra-ket, it is typeset small rather than
large.  That's not what I wanted.  The only way I found to get around
this is to use the combersome \mathchoice command, which requires four
copies of the argument.  To shorten the notation, I defined an
auxiliary function \layermath, which adds a layer of \hbox to a math
expression, and the following (final!) version of \braket, and a
couple more trivial analogous commands dealing with "bra"s and "ket"s.
(The only reason that the others are more trivial is that they have an
even number of delimiters and so don't need any null delimiters.)

================= The final version: ======================================

\def\bra#1{\left< #1 \right|}
\def\ket#1{\left| #1 \right>}
\def\layermath#1{
  \mathchoice
  {\hbox{$\displaystyle      #1 \mathsurround=0pt$}}
  {\hbox{$\textstyle         #1 \mathsurround=0pt$}}
  {\hbox{$\scriptstyle       #1 \mathsurround=0pt$}}
  {\hbox{$\scriptscriptstyle #1 \mathsurround=0pt$}}
}
% produce <#1|#2> (a Dirac "bra-ket" inner product):
\def\braket#1#2{\mathinner{\layermath{
  \mathopen{\left< #1 \vphantom{#2} \right|}
  \left. #2 \vphantom{#1} \right>
  \nulldelimiterspace=0pt
}}}
% produce <#1|#2|#3> (a Dirac matrix element):
\def\matrixel#1#2#3{\mathinner{
  \mathopen{\left< #1 \vphantom{#2#3} \right|}
  #2
  \mathclose{\left| #3 \vphantom{#1#2} \right>}
}}
% produce |#1><#2| (a Dirac projection operator):
\def\ketbra#1#2{
  \mathord{\left| #1 \vphantom{#2} \right>}
  \mathord{\left< #2 \vphantom{#1} \right|}
}

===========================================================================

Everything there has been explained above, except the use of
\mathinner, \mathopen, \mathclose, and \mathord, which ensure that the
spacing surrounding this expression is correct.

This solution seems to work; let me know if you find any problems.
However, it is quite inefficient, due to the fact that each of the
arguments is typeset 8 (!) times to produce a \braket.  I suppose that
this could be improved by storing them in boxes during processing, but
the arguments are typically small so I didn't worry about it.  (Let me
know if you make this modification.)

I hope this posting prevents at least one person from having to figure
these contortions out again.  I will try to upload these macros to one
of the archive sites, too.

Michael
[email-masked]