5
$\begingroup$

I'd like to be able to use Mathematica to preform some basic quantum mechanics and quantum computation operations using Dirac's Bra-Ket notation.

I've seen several solutions to defining basic behaviors, and can roll my own with something rudimentary that takes lists of components expressed in a fixed basis, like

Bra[1]=Bra[{1,0}];Ket[1]=Ket[{1,0}];
Bra[0]=Bra[{0,1}];Ket[0]=Ket[{0,1}];
Bra[l_]:={Conjugate[l]}
Ket[l_]:=Transpose@{l}
AngleBracket[0,y_]:=0
AngleBracket[x_,0]:=0
AngleBracket[x_,y_]:=Bra[x].Ket[y]//First//First
Clear[VerticalSeparator]
AngleBracket[bra___,VerticalSeparator[x_,y_],ket___]:=AngleBracket[x,y]

But this only goes so far. For example I'd minimally also like to implement outer products

$$\left|u\right>\left<v\right|$$

basic tensor notation

$$\left|u\right>\otimes\left|v\right>\otimes\left|w\right>\equiv\left|u,v,w\right>\equiv\left|uvw\right>$$

and the key properties of bras and kets — e.g. ($\left|u\right>)^*=\left<v\right|$.

Ideally I'd like more, such as factoring and decomposition, or easy change of bases.

I also understand that there are some sophisticated packages that do all this and more. But these seem to be far more complex than I need, and are also quite old (and produce odd formatting that makes me wonder if they can even be relied on in the current version of MMA (the latest version mentioned for QUANTUM in 8).

There are two possible solutions I'd be interested in:

  1. Are there simple things I can do myself, e.g., extending the rudimentary start I've made above, that will get me most of what I want?

  2. Is there a more recent comprehensive package stable current package available than QUANTUM. (Alternatively perhaps someone can confirm that QUANTUM works error-free with v.11; and can help fix the odd formatting of bras and kets.)

Truth be told, I'd prefer the former solution, to keep things simple and to avoid introducing too many definitions and redefinitions; but I'd also be interested in the latter if a current easy to use and well documented package is available.


Using Notation I can get something started with

<<Notation`
Notation[|1\[RightAngleBracket] \[DoubleLongRightArrow] |{1,0}\[RightAngleBracket]];Notation[\[LeftAngleBracket]1| \[DoubleLongRightArrow] \[LeftAngleBracket]{1,0}|];
Notation[|0\[RightAngleBracket] \[DoubleLongRightArrow] |{0,1}\[RightAngleBracket]];Notation[\[LeftAngleBracket]0| \[DoubleLongRightArrow] \[LeftAngleBracket]{0,1}|];
Notation[|x_\[RightAngleBracket] \[DoubleLongRightArrow] Transpose@{x_}]
Notation[\[LeftAngleBracket]y_| \[DoubleLongRightArrow] {Conjugate[y_]}]
Notation[\[LeftAngleBracket]x_\[VerticalSeparator]y_\[RightAngleBracket] \[DoubleLongRightArrow] \[LeftAngleBracket]x_|.|y_\[RightAngleBracket]]
Notation[|x_\[RightAngleBracket]\[LeftAngleBracket]y_| \[DoubleLongRightArrow] |x_\[RightAngleBracket].\[LeftAngleBracket]y_|]

(It looks neater in MMA.)

$\endgroup$
11
  • $\begingroup$ One question: why are you mucking around with AngleBracket? There's the symbol BraKet which you could work with instead. And [esc]bra[esc] will give you a Bra to work with, [esc]ket[esc] will give you a Ket, and [esc]brakey[esc] will give you a BraKet. $\endgroup$
    – b3m2a1
    Sep 14, 2017 at 20:38
  • $\begingroup$ @b3m2a1: Notation looks interesting. $\endgroup$
    – orome
    Sep 14, 2017 at 20:42
  • $\begingroup$ In this case it's not needed though. I've got something in the works that will hopefully get you on a better path. $\endgroup$
    – b3m2a1
    Sep 14, 2017 at 20:46
  • $\begingroup$ @b3m2a1: I don't know — Notation looks pretty cool (see addition to Q). $\endgroup$
    – orome
    Sep 14, 2017 at 20:51
  • 1
    $\begingroup$ I mean you can do it, but you lose the benefits of working with a symbolic language. The best thing to do here is to leave everything unevaluated until you apply some evaluator function. That way you can work with bras and kets and bra-kets as symbolic objects. $\endgroup$
    – b3m2a1
    Sep 14, 2017 at 20:52

2 Answers 2

6
$\begingroup$

So Mathematica will actually handle most of this formatting for us, so the question then is how do we want to handle setting the rules.

Since Mathematica is a symbolic language I think it's best to keep things as symbolic as possible. Because of that what we'll do is register our own evaluation rules to be applied once we've done all the symbolic manipulation we want.

We'll set up rules on a function BraKetRule, and we'll apply them with BraKetEval. To make setting these more convenient we'll use two functions BraKetAlias and BraKetAliasCore (the latter will remain unevaluated until the last step) that will look like this:

ClearAll[BraKetAlias];
BraKetAlias[HoldPattern[SetDelayed[x_, y_]]] :=

  SetDelayed[BraKetRule[x], y];
BraKetAlias[
   e__] :=
  {BraKetAlias /@ HoldComplete[e] // ReleaseHold};
BraKetAlias[{exprs___}] :=
  BraKetAlias[exprs];
BraKetAlias[HoldPattern[CompoundExpression[e__]]] :=

  BraKetAlias[e];
BraKetAlias[Null] := Null;
BraKetAlias~SetAttributes~HoldAllComplete;

ClearAll[BraKetAliasCore];
BraKetAliasCore[HoldPattern[SetDelayed[x_, y_]]] :=

  SetDelayed[BraKetRule[x] /; TrueQ[$eBraKet], y];
BraKetAliasCore[
   e__] :=
  {BraKetAliasCore /@ HoldComplete[e] // ReleaseHold};
BraKetAliasCore[{exprs___}] :=
  BraKetAliasCore[exprs];
BraKetAliasCore[HoldPattern[CompoundExpression[e__]]] :=

  BraKetAliasCore[e];
BraKetAliasCore[Null] := Null;
BraKetAliasCore~SetAttributes~HoldAllComplete;

Then we set up our rules:

Clear[BraKetRule];
BraKetAliasCore[
  Bra[1] := BraKetRule@Bra[{1, 0}];
  Ket[1] := BraKetRule@Ket[{1, 0}];
  Bra[0] := BraKetRule@Bra[{0, 1}];
  Ket[0] := BraKetRule@Ket[{0, 1}];
  Bra[l_] := {Conjugate[Flatten[{l}, 1]]};
  Ket[l_] := Transpose@{Flatten[{l}, 1]};
  BraKet[0, y_] := 0;
  BraKet[x_, 0] := 0;
  BraKet[x_, y_] := Bra[x].Ket[y] // First // First;
  ];
BraKetAlias[
  NonCommutativeMultiply[Ket[y_], Bra[x_]] :=

   With[{f = Flatten[{x}, 1]},
    NonCommutativeMultiply[BraKet[f, {#}], Bra[y]] &
    ];
  CircleTimes[k__Ket] :=
   Thread[Times[k], Ket];
  Conjugate[Ket[v_]] :=
   Bra[Conjugate[v]];
  ];
BraKetRule[e_] := e;

(Note the use of NonCommutativeMultiply. That's important.)

Then here are some rules that we'll set on Bra, Ket, and BraKet themselves for convenience:

Clear[Bra, Ket, BraKet];
Times[Bra[x_], Ket[y_]] ^:= BraKet[x, y];
BraKet[x : Except[_List], y_] := BraKet[{x}, y];
BraKet[x_, y : Except[_List]] := BraKet[x, {y}];

And finally our little BraKetEval function:

Clear[BraKetEval];
BraKetEval[e_] :=
 With[{base = BraKetRule //@ e},
  Block[{$eBraKet = True},
    base /. {
      r : (_Bra | _Ket | _BraKet) :> BraKetRule[r]
      }
    ] /. HoldPattern[BraKetRule[r_]] :> r
  ]

Now we can handle all this junk symbolically.

First make a Bra, Ket, or BraKet with [esc]bra[esc], [esc]ket[esc], or [esc]braket[esc] respectively (note that [tab] will move you to the place holders).

Then you can do junk like:

Bra[1] Ket[1]

BraKet[{1}, {1}]

Or

Ket[1] ** Bra[1] // BraKetEval

BraKet[{1}, {#1}] ** Bra[1] &

Or

Ket[f]⊗Ket[g]⊗Ket[h] // BraKetEval

{{f g h}}

Ket[1]⊗Ket[1]⊗Ket[1] // BraKetEval

{{1}, {0}}

BraKetEval Cells

We can make this somewhat more convenient by defining a cell type that will auto-eval using BraKetEval:

Options[BraKetCell] =
  Join[
   Options[NotebookWrite],
   Options[Cell]
   ];
BraKetCell[
  nb : _CellObject | _NotebookObject | Automatic : Automatic,
  e : Except[_?OptionQ] : Automatic,
  sel : Except[_?OptionQ] : Before,
  ops : OptionsPattern[]
  ] :=
 With[{obj = Replace[nb, Automatic :> InputNotebook[]]},
  NotebookWrite[obj,
   Cell[BoxData@ToBoxes@Replace[e, Automatic :> Placeholder[]],
    "BraKetCell",
    "Input",
    Sequence @@
     FilterRules[{
       ops,
       InputAutoReplacements -> {
         ParentList,
         "bra" -> TemplateBox[{"\[Placeholder]"}, "Bra"],
         "ket" -> TemplateBox[{"\[Placeholder]"}, "Ket"],
         "braket" -> 
          TemplateBox[{"\[Placeholder]", "\[Placeholder]"}, "BraKet"]
         },
       CellEvaluationFunction -> BraKetEval@*ToExpression
       },
      Options[Cell]
      ]
    ],
   sel,
   Sequence @@ FilterRules[{ops}, Options[NotebookWrite]]
   ]
  ]

It also uses bra and ket, and braket as InputAutoReplacements for convenience.

Then it's just used like this:

BraKetCell[]

Bra[1] 

{{1, 0}}

BraKetCell[]

Ket[2]

{{2}}

BraKetCell[]

BraKet[{f}, {g}]

f

You could add it as a style to a stylesheet too, for even greater convenience.

$\endgroup$
1
0
$\begingroup$

To answer your question in the title, there is the fully open-sourced and free-to-use BernDirac package on Github which I implemented myself. The documentation is fully laid out on the readme page.

This package does not have any dependencies and is quite lightweight and simple to use compared to the ones you linked.

It uses the built-in Ket and Bra operator and the notation is quite similar to what you described, but it only supports 0's and 1's as input for now.

Since this is open source, you can dig into my fairly-short code and hopefully you can figure out how to implement what you want or even improve on it.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.