How can we efficiently compute the partial trace of a matrix with Mathematica?
There is some Mathematica code around to compute this, but most of it seems outdates and not very well written. See for example this code on the Wolfram Library Archive. Only one question seems to have been asked here about this problem, but it was about a very special case.
Here is my solution to the problem:
makeIterators[iterators_, lengths_, indices_] := Join @@ Table[
Table[
{iter[k], lengths[[k]]},
{k, indices}
],
{iter, iterators}
];
indicesToIndex[indices_List, lengths_List] := 1 + Total@MapIndexed[
#1 Times @@ lengths[[First@#2 + 1 ;;]] &,
indices - 1
];
ClearAll[partialTrace];
partialTrace[matrix_, lengths_, indicesToKeep_] := Module[{i, j},
With[{indicesToTrace =
Complement[Range@Length@lengths, indicesToKeep]},
With[{
iteratorsInFinalMatrix =
Sequence @@ makeIterators[{i, j}, lengths, indicesToKeep],
iteratorsToTrace =
Sequence @@ makeIterators[{i}, lengths, indicesToTrace]
},
Do[
Plus @@ Flatten @ Table[
matrix[[
indicesToIndex[i /@ Range@Length@lengths, lengths],
indicesToIndex[
j /@ Range@Length@lengths /. j[n_] :> i[n], lengths]
]],
iteratorsToTrace
] // Sow,
iteratorsInFinalMatrix
] // Reap // Last // First //
Partition[#, Times @@ lengths[[indicesToKeep]]] &
]
]
]
This solution basically replicates the steps one would naturally do when computing the partial trace by hand, but I don't like it very much (in particular having to programmatically create the iterators for the Table
and Do
). To name a few problems, it cannot be compiled nor parallelized.
Here is an example of its operation:
testMatrix = KroneckerProduct[Array[a, {2, 2}], Array[b, {2, 2}], Array[c, {2, 2}]];
partialTrace[testMatrix, {2, 2, 2}, {1, 3}] // TraditionalForm
which gives
While an algorithm working for symbolic inputs is nice, I'm mostly interested in a function working efficiently for (potentially big) numerical matrices.
To be clear: the problem is that of finding an algorithm to compute the partial trace of a matrix.
That is, the inputs will be one matrix, the set of dimensions of the bases, and the dimensions to keep (or those to trace away, equivalently).
A solution working on the nested structure given by TensorProduct
is a valid answer only as long as one also provides a mean to convert back and forth between the regular matrix representation and the TensorProduct
representation.