.. _grouping_explanation: Grouping Operator Terms ======================= The operator representations provided by the :mod:`~qiskit_fermions.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 :ref:`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]_. .. plot:: :alt: A simple directed graph on which we define our MajoranaOperator. :context: close-figs :include-source: >>> 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, ... )
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: .. tab-set-code:: .. code-block:: python >>> 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))] .. code-block:: c #include 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); .. [1] A. Gandon et al., Stabilizer-based quantum simulation of fermion dynamics with local qubit encodings, `arXiv:2512.11418v2 `_.