Grouping Operator Terms¶
The operator representations provided by the operators
module provide a groups attribute which can optionally be used to specify
group indices for each term.
What this means concretely, is that all the terms of an operator can be associated with an integer value and terms with identical indices are considered to be part of the same group of terms. Grouping information provided like this can be exploited in different processing steps, some examples of which include:
optimizing the time evolution of an operator with grouped terms
Hint
A tutorial for this will be provided soon!
more physically meaningful circuits generated by the SqDRIFT routine
the selection of flow sets [1]
Usage¶
The simplest way of defining an operator grouping is by setting the groups
attribute of the target operator. Below we give a simple example for grouping
terms according to the line flow sets as shown in Fig. 1c of [1].
>>> import rustworkx as rx
>>> graph = rx.PyDiGraph()
>>> _ = graph.add_nodes_from(range(6))
>>> edges = [(0, 1), (1, 2), (3, 0), (1, 4), (5, 2), (5, 4), (4, 3)]
>>> groups = [0, 0, 1, 2, 1, 3, 3]
>>> edges_with_group_as_payload = [(i, j, g) for (i, j), g in zip(edges, groups)]
>>> _ = graph.add_edges_from(edges_with_group_as_payload)
>>> from rustworkx.visualization import mpl_draw
>>> mpl_draw(
... graph,
... pos={i: (i % 3, i // 3) for i in range(6)},
... edge_labels=str,
... with_labels=True,
... )
<Figure size 640x480 with 1 Axes>
Here, we have a simple 2 by 3 lattice of Majorana modes which are connected with directional information. We want to group the corresponding operator terms according to their edge labels. The code below shows have that can be done:
>>> from qiskit_fermions.operators import MajoranaOperator
>>> op = MajoranaOperator(
... [1.0] * 7,
... [node for edge in edges for node in edge],
... [0, 2, 4, 6, 8, 10, 12, 14],
... )
>>> op.groups = groups
>>> groups = op.split_out_groups()
>>> for i, g in enumerate(groups):
... print(f"{i}: {list(sorted(g.iter_terms()))}")
0: [([0, 1], (1+0j)), ([1, 2], (1+0j))]
1: [([3, 0], (1+0j)), ([5, 2], (1+0j))]
2: [([1, 4], (1+0j))]
3: [([4, 3], (1+0j)), ([5, 4], (1+0j))]
#include <qiskit_fermions.h>
uint64_t num_terms = 7;
uint64_t num_modes = 14;
uint32_t modes[14] = {0, 1, 1, 2, 3, 0, 1, 4, 5, 2, 5, 4, 4, 3};
QkComplex67 coeffs[7] = {{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0},
{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}};
uint32_t boundaries[8] = {0, 2, 4, 6, 8, 10, 12, 14};
QfMajoranaOperator *op = qf_maj_op_new(num_terms, num_modes, coeffs, modes, boundaries);
uint32_t groups[7] = {0, 0, 1, 2, 1, 3, 3};
qf_maj_op_set_groups(op, groups, num_terms);
QfMajoranaOperator *group_ops[4];
qf_maj_op_split_out_groups(op, group_ops);