Skip to main content
IBM Quantum Platform

Términos de los operadores de grupo: utiliza la estructura de los operadores

La agrupación es una de las operaciones incluidas en qiskit_fermions.operators.terms, que recoge rutinas que dividen los términos de un operador en función de su estructura.

Tal y como se describe en la guía del operador, los operadores pueden almacenar una matriz de grupos, una parte opcional de la estructura de datos dispersa que asocia cada término con un índice de grupo. Los términos que comparten el mismo índice de grupo forman un grupo. Esto te permite utilizar de forma sistemática la estructura en las fases posteriores del procesamiento.

Al agrupar términos relacionados (ya sea por propiedades físicas, relaciones algebraicas o simetrías específicas de cada problema), se pueden obtener varias ventajas:

  • Síntesis optimizada de circuitos : los términos agrupados transmiten información específica del problema a lo largo de la pila, lo que permite una optimización más sencilla de los circuitos sintetizados. Esto se traduce en una menor profundidad del circuito y un menor número de puertas lógicas.
  • Conservación de la estructura física : Los grupos codifican la estructura significativa del problema original (por ejemplo, patrones de interacción o simetrías) que puede utilizarse para realizar descomposiciones con mayor significado físico.
  • Selección de conjuntos de flujos : La agrupación puede codificar de forma natural conjuntos de flujos y otras estructuras de la teoría de grafos relevantes para la simulación cuántica, tal y como se muestra en [1].
  • Mayor estabilidad algorítmica : tal y como se demuestra en la rutina « SqDRIFT », algunos algoritmos pueden beneficiarse de una mayor estabilidad o rendimiento gracias a la conservación de las propiedades físicas.
  • Factores relacionados con la fórmula del producto : Una fórmula del producto fermiónico divide un operador en sus grupos, por lo que la agrupación determina tanto el coste como el error de Trotter de una evolución temporal. Véase «Síntesis de una puerta de evolución en el espacio fermiónico ».

Uso

La forma más sencilla de definir una agrupación de operadores es configurando el atributo groups del operador de destino. A continuación se muestra un ejemplo de agrupación de términos según los conjuntos de flujo de líneas, tal y como se ilustra en la figura 1c de [1].

Sugerencia

El módulo qiskit_fermions.operators.terms.grouping ofrece métodos prácticos para identificar automáticamente la estructura de un operador y asignar los índices de grupo en consecuencia.

>>> 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 ... with 1 Axes>
Un grafo dirigido sencillo sobre el que definir un « MajoranaOperator ».

Se trata de una sencilla red de 2 por 3 modos de Majorana conectados por aristas dirigidas. Cada arista representa un término del operador que actúa sobre los dos modos conectados. El objetivo es agrupar estos términos según las etiquetas de sus aristas (que se muestran en el gráfico). Los términos conectados por aristas con la misma etiqueta se colocan en el mismo grupo. El siguiente código muestra cómo se puede hacer:

>>> 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))]

Analizar una agrupación

Los grupos no atribuyen ningún significado por sí mismos: la estructura de los grupos recoge qué términos van juntos, pero nunca por qué. Ninguna rutina de esta biblioteca indica si una agrupación es «correcta», ya que no existe un único concepto de «corrección» con el que compararla. No obstante, los consumidores posteriores sí asumen las propiedades de una agrupación, y las funciones de esta sección te permiten comprobar esa suposición desde el principio, en lugar de tener que pagar por ello en cada llamada.

La convención más habitual es que cada grupo sea hermitiano por separado. Eso es precisamente lo que hace posible que existan las fórmulas de producto aleatorio fermiónico: un único ap†aqa^\dagger_p a_q con p≠qp \neq q no es hermitiano, por lo que su evolución temporal no es unitaria y no puede muestrearse por sí solo. Si lo combinamos con aq†apa^\dagger_q a_p, se obtiene un grupo que podría ser.

>>> from qiskit_fermions.operators import FermionOperator
>>> from qiskit_fermions.operators.terms.grouping import (
...     group_coeff_means,
...     groups_are_hermitian,
...     groups_have_uniform_coeffs,
... )
>>>
>>> # a conjugate pair, plus one unpaired term. The operator is built from the sparse
>>> # arrays rather than from a dictionary, because only the former fixes the term order
>>> # that the group indices below are paired with.
>>> pair_and_single = FermionOperator(
...     [1.0, 1.0, 1.0],
...     [True, False, True, False, True, False],
...     [0, 1, 1, 0, 2, 3],
...     [0, 2, 4, 6],
... )
>>> pair_and_single.groups = [0, 0, 1]
>>>
>>> groups_are_hermitian(pair_and_single)  # group 1 is unpaired
[True, False]
>>> group_coeff_means(pair_and_single)
[1.0, 1.0]

Se exige un índice de grupo por trimestre

La matriz de grupos contiene un índice por término, y al asignarle uno de longitud diferente se produce un error. Nada más adelante vuelve a comprobar la invariante, por lo que se aplica allí donde los índices entran en el operador: una matriz demasiado corta omitiría silenciosamente los términos finales, y una demasiado larga inventaría grupos que ningún término contiene.

>>> pair_and_single.groups = [0, 0]  # one index short, on a three-term operator
Traceback (most recent call last):
ValueError: expected one group index per term, but got 2 for 3 terms

Siempre None está permitido borrar los índices de grupo asignándoles un valor.

Las dos comprobaciones son independientes

groups_have_uniform_coeffs() indica si los coeficientes de un grupo coinciden, que es la hipótesis que se parte group_coeff_means() al calcular la media de las magnitudes. No se trata de una forma más débil de la comprobación de hermiticidad: ninguna de las dos comprobaciones implica a la otra, en ningún sentido.

>>> from qiskit_fermions.operators.terms.grouping import (
...     groups_are_hermitian,
...     groups_have_uniform_coeffs,
... )
>>> # uniform coefficients, but not Hermitian: the terms are not each other's adjoint
>>> uniform = FermionOperator(
...     [1.0, 1.0],
...     [True, False, True, False],
...     [0, 1, 2, 3],
...     [0, 2, 4],
... )
>>> uniform.groups = [0, 0]
>>> groups_have_uniform_coeffs(uniform), groups_are_hermitian(uniform)
([True], [False])
>>> # Hermitian, but with mixed magnitudes: two conjugate pairs at different scales
>>> mixed = FermionOperator(
...     [1.0, 1.0, 5.0, 5.0],
...     [True, False, True, False, True, False, True, False],
...     [0, 1, 1, 0, 2, 3, 3, 2],
...     [0, 2, 4, 6, 8],
... )
>>> mixed.groups = [0, 0, 0, 0]
>>> groups_are_hermitian(mixed), groups_have_uniform_coeffs(mixed)
([True], [False])

Por defecto, se comparan las magnitudes, ya que eso es lo que se tiene group_coeff_means() en cuenta para calcular la media. Un par conjugado hermítico con coeficientes complejos tiene magnitudes iguales pero coeficientes desiguales, por lo que solo cumple la forma predeterminada.

>>> conjugate = FermionOperator(
...     [1.0j, -1.0j],
...     [True, False, True, False],
...     [0, 1, 1, 0],
...     [0, 2, 4],
... )
>>> conjugate.groups = [0, 0]
>>> groups_have_uniform_coeffs(conjugate)
[True]
>>> groups_have_uniform_coeffs(conjugate, abs=False)
[False]
Nota

groups_are_hermitian() hereda la garantía unidireccional de is_hermitian(): una entrada True es siempre fiable, mientras que una False entrada solo es fiable para los tipos de operadores cuya forma normal sea una forma canónica genuina. Consulta el tipo de operador concreto para saber cuál es el que corresponde.

[1] ( 1, 2 )

A Gandon et al., Simulación cuántica basada en estabilizadores de la dinámica de fermiones con codificaciones locales de qubits, arXiv:2512.11418v2.

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.