Analogously to slicing state vectors, it is also necessary to slice LazyDensityOperator objects because for calculating quantum expectation values of subsystem-observables (e.g. in Composite objects), the partial-trace density operator is needed.
For the partial trace, however, only such elements of the full density operator are needed as are diagonal in the indices not belonging to the given subsystem (dummy indices). This is the reason why the tool performing the iteration is called DiagonalIterator.
Slicing is fully recursive, a sliced LazyDensityOperator (usually obtained by dereferencing a DiagonalIterator) can be further sliced.
Difficulty: LazyDensityOperator is an abstract interface, and the organization of its data (and hence the actual procedure of slicing) varies along implementations.
Implemented using a classical inheritance-based strategy idiom, together with both compile-time and run-time implementation selection (similarly to blitzplusplus::basi::Iterator, a special implementation (DiagonalIterator::DI_ImplSpecial) is needed when the size of the compile-time vector V
equals RANK
).
The run-time polymorphy of LazyDensityOperator necessitates run-time implementation selection. This happens when the actual implementation (DI_SV_Impl
or DI_DO_Impl
, or their non-orthogonal counterparts) is chosen depending on whether the given LazyDensityOperator is a StateVector or a DensityOperator (or their non-orthogonal counterparts).