.. _grouping_explanation: Group operator terms: exploit the operator structure ==================================================== As described in the :ref:`operators guide `, operators can store a **groups array** - an optional part of the sparse data structure that associates each term with a group index. Terms sharing the same group index form a group, enabling systematic exploitation of structure in downstream processing. By grouping related terms (whether by physical properties, algebraic relationships, or problem-specific symmetries), you can unlock several benefits: - **Optimized circuit synthesis**: Grouped terms carry problem-specific information through the stack, enabling simpler optimization of the synthesized circuits, resulting in reduced circuit depth and gate count. - **Physical structure preservation**: Groups encode meaningful structure from the original problem (for example, interaction patterns or symmetries), that can be exploited for more physically meaningful decompositions. - **Flow set selection**: Grouping can naturally encode flow sets and other graph-theoretic structures relevant to quantum simulation, as shown in [1]_. - **Improved algorithmic stability**: As demonstrated in the :ref:`SqDRIFT routine `, some algorithms can benefit from improved stability or performance due to the preservation of physical properties. Usage ----- The simplest way to define an operator grouping is by setting the target operator's ``groups`` attribute. Below is an example for grouping terms according to the *line flow sets* as shown in Figure 1c of [1]_. .. hint:: The :mod:`qiskit_fermions.operators.grouping` module provides convenience methods for automatically identifying structure in an operator and assigning group indices accordingly. .. 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 two by three lattice of Majorana modes connected by directed edges. Each edge represents a term in our operator acting on the two connected modes. We want to group these terms according to their edge labels (shown on the graph): terms connected by edges with the same label are placed in the same group. The following code shows how that can be done: .. tab-set-code:: .. code-block:: python >>> from qiskit_fermions.operators import MajoranaOperator >>> >>> # Construct operator directly using sparse arrays >>> # 7 terms, each with coefficient 1.0, modes from edge endpoints >>> op = MajoranaOperator( ... coeffs=[1.0] * 7, ... modes=[node for edge in edges for node in edge], ... boundaries=[0, 2, 4, 6, 8, 10, 12, 14], ... ) >>> >>> # Assign group indices to terms (groups is part of the sparse data structure) >>> op.groups = groups >>> >>> # Partition operator by groups >>> grouped_ops = op.split_out_groups() >>> >>> # Inspect each group >>> for i, g in enumerate(grouped_ops): ... print(f"Group {i}: {list(sorted(g.iter_terms()))}") Group 0: [([0, 1], (1+0j)), ([1, 2], (1+0j))] Group 1: [([3, 0], (1+0j)), ([5, 2], (1+0j))] Group 2: [([1, 4], (1+0j))] Group 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 `_.