Truncar los términos de Pauli durante la retropropagación
Esta guía explica cómo configurar el mecanismo de truncamiento del término de Pauli que ofrece el módulo « qiskit_addon_obp.utils.truncating ».
La retropropagación de operadores (OBP) puede utilizarse para reducir la profundidad de los circuitos cuánticos, a costa de obtener un observable más complejo. Para obtener resultados significativos con el OBP, normalmente es necesario truncar los términos de la variable observable para evitar que adquiera un tamaño excesivo. Una forma de permitir una retropropagación más profunda en el circuito, evitando al mismo tiempo que el operador adquiera un tamaño excesivo, consiste en truncar los términos con coeficientes pequeños, en lugar de añadirlos al operador. El truncamiento de términos puede reducir el número de circuitos cuánticos que hay que ejecutar, pero ello da lugar a un error en el cálculo del valor esperado final, proporcional a la magnitud de los coeficientes de los términos truncados.
El método de retropropagación admite un parámetro opcional denominado TruncationErrorBudget, que configura el truncamiento de los términos de Pauli de bajo peso para cada observable tras la retropropagación satisfactoria de cada segmento.
El número de términos que se truncan depende de diversos parámetros de configuración especificados por el usuario.
Por el momento, solo hay disponible una estrategia de truncamiento: el método truncate_binary_search.
Dada una observable y un presupuesto determinado, realizará una búsqueda binaria entre los términos de Pauli y los coeficientes de dicha observable para hallar el umbral óptimo,
de tal forma que la suma de los coeficientes truncados sea máxima, pero inferior al presupuesto.
Nota : De forma predeterminada, se utiliza la norma L1 para evaluar y limitar el error de truncamiento; sin embargo, la opción p_norm permite especificar la norma Lp que se va a utilizar.
Para obtener más información sobre cómo utilizar esa configuración, consulta la guía «Utilizar diferentes normas Lp para el truncamiento del término de Pauli ».
Los siguientes ejemplos ilustran diversas formas de crear un « TruncationErrorBudget » utilizando la función «setup_budget» adjunta.
Construye un circuito de ejemplo
Esta guía utiliza los siguientes segmentos de circuito:
import rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
PauliOrderStrategy,
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
# Generate a linear chain of 10 qubits
linear_chain = rustworkx.generators.path_graph(10)
# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
linear_chain,
coupling_constants=(0.05, 0.02, 0.0),
ext_magnetic_field=(0.02, 0.08, 0.0),
pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)
# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)Output:
Analizaremos una única magnitud observable sencilla:
from qiskit.quantum_info import SparsePauliOp
obs = SparsePauliOp("IIIIIZIIII")El caso más sencillo: un presupuesto de truncamiento fijo para cada segmento
El presupuesto disponible para la truncación de los términos de Pauli puede variar en cada paso de la retropropagación. Para entender cómo funciona esto, analizaremos primero el caso más sencillo: un presupuesto de truncamiento fijo, tal y como lo especifique el usuario.
La forma más sencilla de especificar el presupuesto de truncamiento es utilizando el argumento max_error_per_slice .
De hecho, esto es lo que se hace en el tutorial «Reducción de la profundidad de los circuitos mediante la retropropagación de operadores ». Al float establecer max_error_per_slice en, a cada segmento se le asigna un presupuesto igual a ese valor.
En el ejemplo siguiente, establecemos este valor en, lo que 0.001 garantiza un error de truncamiento implícito de, como máximo 0.018,, si se aplicara la retropropagación a las 18 rebanadas.
Ten en cuenta que cualquier margen de error restante tras la retropropagación de un segmento y el truncamiento de los términos con coeficientes pequeños siempre se sumará al margen de error del segmento siguiente.
from qiskit_addon_obp.utils.truncating import setup_budget
truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 11 circuit slices.
New observable contains 29 terms and 10 commuting groups.
A continuación, utilizamos la instancia OBPMetadata y las herramientas que ofrece el módulo de visualización para visualizar el proceso de retropropagación.
- El gráfico de la parte superior izquierda muestra que disponemos de presupuesto suficiente para empezar a truncar los términos observables tras la retropropagación del tercer segmento. A partir del tercer segmento, sabemos que truncamos al menos un término de cada segmento que retropropagamos, ya que se produce un error de truncamiento tras cada segmento.
- El gráfico de la esquina superior derecha muestra que el margen de error aumenta hasta
.003en el tercer segmento. Observamos una fuerte caída en el presupuesto restante, lo que significa que se han eliminado algunos términos de los datos observables. Esto concuerda con lo que dedujimos del gráfico de la parte superior izquierda. - El gráfico de la parte inferior izquierda muestra que, a medida que eliminamos términos de nuestra variable observable, nuestro error acumulado total va aumentando de forma monótona. Este gráfico también confirma que no se truncó ningún término hasta después de que se hubiera realizado la retropropagación de la tercera sección.
- El gráfico de la parte inferior derecha muestra que el número de grupos de Pauli en movimiento en nuestro observable ha aumentado hasta el límite especificado de
10. Este gráfico también muestra cómo, al aplicar la retropropagación a una capa más, nuestra variable observable sobrepasaría el límite especificado, tal y como se puede observar en el punto en el que se cruzan las líneas negra y roja.
Ten en cuenta que, en todos estos gráficos, el eje x enumera los segmentos sometidos a retropropagación; sin embargo, dado que el OBP actúa al final del circuito, slice 1 es el último segmento, slice 2 es el anterior a este, y así sucesivamente.
from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
plot_accumulated_error,
plot_left_over_error_budget,
plot_num_qwc_groups,
plot_slice_errors,
)
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Especificar el presupuesto por segmento de forma explícita
Si sabes cómo asignar un presupuesto a cada segmento, de modo que se optimice el rendimiento de la retropropagación, quizá te interese asignar explícitamente un presupuesto a cada segmento. A efectos de demostración, asignaremos un presupuesto cero a los tres primeros segmentos y utilizaremos un presupuesto de por .001 segmento para los segmentos restantes.
# Zero out the first 3 slices' budgets
max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 11 circuit slices.
New observable contains 32 terms and 10 commuting groups.
Al eliminar el presupuesto de las tres primeras capas, no se truncó ningún término hasta después de la cuarta capa, tal y como se puede comprobar en tres de estos gráficos. Lo que quizá resulte un poco sorprendente es que, aunque a la cuarta parte no se le transfiriera ningún remanente presupuestario, se recortó un trimestre al utilizar el presupuesto .001 asignado. Esto se aprecia claramente en el gráfico de la parte superior izquierda, pero también se puede observar en el gráfico de la parte superior derecha, ya que la curva del presupuesto restante se aplana entre los segmentos tres y cuatro. Otro detalle a destacar es que, a partir de este momento, se truncó al menos un término, al igual que en el ejemplo anterior.
La conclusión principal es que, aunque en el segundo ejemplo se produjo un menor error de truncamiento al poner a cero los presupuestos de algunos segmentos, pudimos realizar la retropropagación en el mismo número de segmentos, y nuestro observable contiene el mismo número de grupos de Pauli conmutativos. Podemos confirmar que el límite de nuestro error es menor en el segundo ejemplo al observar el gráfico de la esquina inferior izquierda.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Especificar el presupuesto de forma periódica
Si tienes un circuito con algún patrón repetitivo, como un circuito de Trotter, es posible que quieras especificar un presupuesto para ese subconjunto repetido de segmentos y que dicho presupuesto se aplique a todas las repeticiones posteriores de esos segmentos.
Más concretamente, el circuito de ejemplo que estamos utilizando tiene seis secciones que se repiten tres veces, lo que da un total de 18 secciones. Asignaremos arbitrariamente un presupuesto nulo a las capas de un solo qubit y a las capas RYY , y un presupuesto .003 a cada una de las capas RXX . Veremos cómo, al especificar el presupuesto como una secuencia « length-6 », este se aplica de forma cíclica a los 18 cortes.
Una vez más, ten en cuenta que las secciones se someten a retropropagación en orden inverso (es decir, empezando por el final). Por lo tanto, la primera entrada de nuestro presupuesto cíclico se utiliza, en realidad, para la última porción; la segunda entrada, para la porción anterior a esa, y así sucesivamente.
# Specify a length-6 per-slice budget.
# This will be cycled over three times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)
op_budget = OperatorBudget(max_qwc_groups=20)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 13 circuit slices.
New observable contains 49 terms and 14 commuting groups.
Como se puede observar en las imágenes de la parte superior izquierda y de la parte inferior izquierda, no se realizó ningún recorte en los cuatro primeros cortes, ya que no se les asignó ningún margen de error. En las fases cinco y seis se recortaron algunos de los plazos a medida que se disponía de presupuesto.
Como se aprecia en la imagen superior izquierda, se produjo un error relativamente grande tras la retropropagación del corte siete, a pesar de que a ese corte no se le había asignado ningún presupuesto. Esto se debe a que solo se utilizó aproximadamente del .002 presupuesto total .006 asignado a las franjas cinco y seis, por lo que el resto se transfirió a la franja siete y se gastó en su mayor parte, como se puede ver en la imagen de la esquina superior derecha.
Las imágenes de la parte superior izquierda y derecha muestran que la pequeña cantidad de presupuesto restante se agota entre los tramos 8 y 10, y que, tal y como se esperaba, en el tramo 11 vuelve a haber presupuesto disponible. Las imágenes de la parte superior izquierda, la parte superior derecha y la parte inferior izquierda muestran el comportamiento cíclico del argumento max_error_per_slice cuando su longitud es inferior al número de segmentos. Este comportamiento cíclico habría continuado a lo largo de todos los cortes, pero se alcanzó el criterio de parada max_qwc_groups tras la retropropagación de 13 cortes, como se aprecia en la imagen de la parte inferior derecha.
También resulta interesante que el número de grupos de Pauli disminuya, de hecho, una vez que se dispone de la segunda ronda de presupuesto en la porción 11. Esto se debe a que había grupos con coeficientes pequeños que no pudieron truncarse hasta que hubo más presupuesto tras la retropropagación del segmento 11, por lo que se acumularon en el observable durante varias iteraciones. Este caso también pone de manifiesto que max_qwc_groups debe superarse para que el algoritmo termine.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Limitar el error total
Además de especificar el margen de error por segmento, se puede indicar la cantidad máxima de error que puede producirse debido al truncamiento. Una vez alcanzado ese límite, no se realizarán más truncamientos; sin embargo, la retropropagación continuará hasta que la observable adquiera un valor demasiado elevado y se cumpla uno de los criterios de parada.
A continuación, repetimos el experimento anterior, estableciendo un límite máximo para max_error_total de tal forma que el presupuesto de error se agote tras la retropropagación del segmento siete.
# Specify a length-6 per-slice budget.
# This will be cycled over 3 times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice, max_error_total=0.006
)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 10 circuit slices.
New observable contains 67 terms and 20 commuting groups.
Como era de esperar, nuestro error de truncamiento alcanza su valor máximo en .006 (gráfico de la parte inferior izquierda ). Cabe destacar que, en esta ejecución, no pudimos realizar la retropropagación de la rebanada 11. Esto se debe a que no disponíamos de presupuesto suficiente para truncar los términos y el número de grupos de Pauli en movimiento superó el límite de 20, tal y como se aprecia en la imagen de la parte inferior derecha.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Podría ser conveniente limitar simplemente el presupuesto total a max_error_total sin especificar max_error_per_slice.
truncation_error_budget = setup_budget(max_error_total=0.018)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)
El resultado de la celda anterior puede resultar un poco sorprendente, ya que el per_slice_budget está establecido en el max_error_total. Esto indica que se agotará por completo todo el presupuesto disponible.
Puedes verlo de esta manera: cada porción dispone del presupuesto completo (porque recorremos todas las per_slice_budget). Sin embargo, cualquier presupuesto que ya se haya consumido se deducirá del presupuesto disponible en ese momento concreto del algoritmo.
op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 9 circuit slices.
New observable contains 25 terms and 9 commuting groups.
La imagen de la parte superior derecha muestra cómo se pone a disposición de la primera sección la totalidad del margen de error .018 . No se realiza ningún recorte hasta el tercer segmento, por lo que el presupuesto restante se mantiene constante. El presupuesto disminuye de forma monótona, ya que se pone a disposición de cada segmento de retropropagación la totalidad del presupuesto hasta que se agota.
Cabe destacar que este experimento arrojó dos cortes con retropropagación menos en comparación con el primer experimento de este cuaderno, que es prácticamente idéntico. Esto demuestra que, para algunos problemas, distribuir el presupuesto de manera equitativa podría ser la solución óptima. En el caso de otros problemas, permitir que las secciones utilicen sin restricciones la totalidad del presupuesto podría ofrecer un mejor rendimiento.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Limitar conjuntamente el número de cortes sometidos a retropropagación y el error total
Si no quieres distribuir su presupuesto de error a lo largo de todo el circuito, pero tampoco quieres agotarlo de forma excesiva, puedes especificar el número de segmentos que pretendes someter a retropropagación (num_slices), junto con un presupuesto de error total (max_error_total). De este modo, el presupuesto de error se distribuirá de manera uniforme (de acuerdo con p_norm) entre los segmentos de entrada.
En este caso, limitaremos a el número de capas que podemos someter a retropropagación 12, y mantendremos el mismo presupuesto de error total.
num_slices = 12
truncation_error_budget = setup_budget(
max_error_total=0.018, num_slices=num_slices, p_norm=1
)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)
Ahora intentaremos realizar la retropropagación de los 12 segmentos para los que asignamos un presupuesto en el paso anterior. Para ello, simplemente pasamos las 12 secciones finales a nuestro circuito. Con p_norm=1, cada una de las 12 partes dispone de un presupuesto de 0.018 / num_slices = 0.0015.
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices[-num_slices:],
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)Dado que hemos pasado un subconjunto de nuestras secciones (en el código slices[-num_slices:]) a backpropagate, debemos combinar las secciones que quedan tras la retropropagación con las secciones que nunca se enviaron a la retropropagación (en el código slices[:-num_slices]).
Una vez que hayamos combinado todas las secciones restantes, podemos utilizar la función combine_slices para generar el QuantumCircuit de profundidad reducida. A continuación, analizamos cuántas rebanadas se han sometido a retropropagación en comparación con el aumento del tamaño de nuestro observable.
# Recombine the slices remaining after backprop with the rest of the original circuit
reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)
print(f"Backpropagated {num_slices - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 12 circuit slices.
New observable contains 29 terms and 9 commuting groups.
Los gráficos muestran que hemos logrado aplicar con éxito la retropropagación a los 12 cortes, manteniendo al mismo tiempo nuestro observable dentro de 10 grupos de Pauli conmutativos. También podemos observar que el uso de num_slices junto con max_error_total da lugar a una distribución de los presupuestos entre los segmentos y que el presupuesto no utilizado se transfiere de nuevo al siguiente segmento. Esto se aprecia con mayor claridad en el gráfico de la parte superior derecha, ya que el presupuesto se gasta y se repone a lo largo de la retropropagación.
Cabe destacar que este método de distribución del error arrojó los mejores resultados (un mayor número de cortes retropropagados), en comparación con el primer experimento de este cuaderno y con el ejemplo anterior a este. En todos estos ejemplos se asignó un «presupuesto de .018 error», pero la retropropagación se comportó de forma diferente en función de cómo se distribuyera dicho presupuesto.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output:
Trabajar con varios observables
Este método qiskit_addon_obp.backpropagate permite pasar una secuencia de observables. Esto simplifica el flujo de trabajo a la hora de gestionar múltiples observables de destino.
Mencionamos esto expresamente aquí para explicarte cómo gestiona la estrategia de truncamiento un caso de este tipo. A efectos de este ejemplo, añadimos un observable adicional al que hemos estado utilizando hasta ahora:
obs = [SparsePauliOp("IIIIIZIIII"), SparsePauliOp("IIIIIXIIII")]Por último, repetimos el primer experimento de este tutorial., pero con dos observables en los que realizar la retropropagación del circuito.
En este ejemplo, esto no afecta al número de cortes que podrían someterse a retropropagación. Sin embargo, podemos observar que los dos observables dieron lugar a un número diferente de términos de Pauli y de grupos conmutativos.
truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)Output:
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups."
)
print(
f"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups."
)Output:
Backpropagated 11 circuit slices.
The new first observable contains 29 terms and 10 commuting groups.
The new second observable contains 23 terms and 8 commuting groups.
Los gráficos que se muestran a continuación ayudan a ilustrar cómo gestiona el algoritmo de retropropagación múltiples observables.
En primer lugar, los gráficos de la parte superior izquierda, la parte superior derecha y la parte inferior izquierda muestran que el presupuesto para el truncamiento de términos se establece de forma individual para cada variable observable. En otras palabras, ambas variables observables tienen la capacidad de truncar términos suponiendo un error de 0.001 por cada segmento retropropagado.
Debido a la naturaleza diferente de las variables observables, esto da lugar a un consumo diferente del presupuesto. En este ejemplo, se observa un gran solapamiento, algo que no siempre ocurre.
El gráfico de la parte inferior derecha muestra que max_qwc_groups tiene en cuenta todas las variables observables. Esto significa que los términos de todas las observables se agrupan para obtener un número final de grupos conmutativos por qubit, que se compara con max_qwc_paulis. Lo mismo se aplica al umbral max_paulis (que no se trata en este cuaderno), lo que permite establecer un límite en el número de términos de Pauli en todas las observables.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])Output: