Skip to main content
IBM Quantum Platform

Utilizar la poselección en las cargas de trabajo

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o posteriores.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    qiskit-addon-utils~=0.3.1
    

A la hora de optimizar la estrategia de mitigación de errores de una carga de trabajo, suele resultar útil descartar las mediciones que se sabe que han sido contaminadas por procesos de ruido no markovianos (correlacionados). Uno de los métodos para lograrlo consiste en añadir al circuito una etapa de posprocesamiento en la que se miden los qubits activos y los qubits «espectadores» adyacentes, se aplica una rotación lenta a cada qubit y, a continuación, se vuelven a medir. En los casos en que las dos mediciones no confirman que el qubit haya cambiado de estado como se esperaba, el disparo se descarta aplicando una máscara a los resultados.

El paquete de utilidades del complemento Qiskit ofrece un conjunto de pasadas del transpilador y una función de poselección para aplicar la máscara. Esta página ofrece orientación sobre cómo incorporar la poselección en sus cargas de trabajo cuánticas utilizando como ejemplo un estado GHZ de cuatro qubits.


CREATE WORKLOAD

Empieza por preparar el circuito para ejecutarlo y compilarlo en un backend que admita puertas fraccionarias.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager

circuit = QuantumCircuit(4)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.measure_all()


service = QiskitRuntimeService()
backend = service.least_busy(use_fractional_gates=True)
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)

transpiled_circuit = pm.run(circuit)
transpiled_circuit.draw("mpl")

Output:

Output of the previous code cell

Añadir pasadas del transpilador de poselección

A continuación, crea un gestor de pases predefinidos que incluya los AddPostSelectionMeasures pases y AddSpectatorMeasures del qiskit-addon-utils paquete. Esto añadirá al circuito una secuencia de rotaciones de ángulo RX pequeño (lo que, en la práctica, da lugar a una puerta larga X ), junto con un segundo conjunto de mediciones.

from qiskit.transpiler import PassManager
from qiskit_addon_utils.noise_management.post_selection import PostSelector
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
    AddPostSelectionMeasures,
    AddSpectatorMeasures,
)


post_selection_pm = PassManager(
    [
        AddSpectatorMeasures(backend.coupling_map, add_barrier=True),
        AddPostSelectionMeasures(x_pulse_type="rx"),
    ]
)

template_circuit_ps = post_selection_pm.run(transpiled_circuit)
template_circuit_ps.draw("mpl", fold=-1, idle_wires=False)

Output:

Output of the previous code cell

Ejecutar programa cuántico

A continuación, prepara un QuantumProgram objeto que contenga el circuito que se va a ejecutar.

from qiskit_ibm_runtime import QuantumProgram, Executor

shots = 4000

program = QuantumProgram(shots=shots)
program.append_circuit_item(template_circuit_ps)

# Initialize the Executor job and run
executor = Executor(backend)
executor_job = executor.run(program)
print(f"Job ID: {executor_job.job_id()}")

Output:

Job ID: d9bjj43v6alc73ct56rg

Ahora ya puedes interpretar los resultados. El resultado del ejecutor es un diccionario con varias claves.

executor_result = executor_job.result()[0]
executor_result.keys()

Output:

KeysView(QuantumProgramItemResult({'meas': array([[False, False, False, False],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       ...,
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False]], shape=(4000, 4)), 'spec': array([[False, False],
       [False, False],
       [False, False],
       ...,
       [False, False],
       [False, False],
       [False, False]], shape=(4000, 2)), 'meas_ps': array([[ True,  True,  True,  True],
       [False, False, False, False],
       [False, False, False, False],
       ...,
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], shape=(4000, 4)), 'spec_ps': array([[ True,  True],
       [ True,  True],
       [ True,  True],
       ...,
       [ True,  True],
       [ True,  True],
       [ True,  True]], shape=(4000, 2))}, metadata=ItemMetadata()))

Estas claves corresponden a los qubits activos y de observación antes de las rx instrucciones (meas y spec) y después de las rx instrucciones (meas_ps y spec_ps). Cada uno de ellos es una matriz de matrices basada en el número de disparos y de qubits. En este caso, la forma es (1000, 4).


Crear máscara de poselección

A partir de estas medidas, puedes crear una máscara utilizando la PostSelector clase de qiskit-addon-utils. Esta máscara es una matriz booleana en la que cada fotograma se marca como o False``True en función de una de las dos estrategias de poselección. nodeLa primera estrategia utiliza la información de los qubits para decidir si se debe descartar una medición, mientras que la segunda edgeutiliza la información sobre la conectividad entre vecinos más cercanos para tomar esta decisión.

post_selector = PostSelector.from_circuit(
    circuit=template_circuit_ps, coupling_map=backend.coupling_map
)

mask_node = post_selector.compute_mask(executor_result, strategy="node")
mask_edge = post_selector.compute_mask(executor_result, strategy="edge")

Tanto la estrategia de nodos como la de arcos suelen descartar diferentes tiros. Puedes elegir cualquiera de ellos. Este cuaderno utiliza una operación AND bit a bit, lo cual es una estrategia conservadora que solo retiene una jugada si esta supera tanto la estrategia de nodos como la de aristas.

mask = mask_node & mask_edge
print(f"The combined mask: {mask}")
count_retained = 0

for m in mask:
    count_retained += m

print(
    f"Percentage of the shots retained is after post selection "
    f"{100 * count_retained / shots}"
)

Output:

The combined mask: [ True  True  True ...  True  True  True]
Percentage of the shots retained is after post selection 84.675

Compara la distribución de probabilidad con y sin poselección. El siguiente fragmento de código calcula la distribución de probabilidad antes y después de la postselección, así como la distancia entre la distribución observada y la distribución ideal.

counts = {}
counts_ps = {}


for idx, measurement in enumerate(executor_result["meas"]):
    bitstring = ""
    for bit in measurement:
        bitstring += str(int(bit))

    if bitstring in counts:
        counts[bitstring] += 1
    else:
        counts[bitstring] = 1

    # Compute count data for postselected shots based on the mask
    if mask[idx]:
        bitstring = ""
        for bit in measurement:
            bitstring += str(int(bit))

        if bitstring in counts_ps:
            counts_ps[bitstring] += 1
        else:
            counts_ps[bitstring] = 1

for key, val in counts.items():
    counts[key] = val / shots


for key, val in counts_ps.items():
    counts_ps[key] = float(val / count_retained)

Para demostrar cómo la postselección ha modificado tus resultados, calcula la distancia entre la distribución de probabilidad ideal y las distribuciones observadas.

import itertools
from qiskit.visualization import plot_histogram

bitstrings = ["".join(i) for i in itertools.product("01", repeat=4)]
counts_ideal = {}
for bitstring in bitstrings:
    counts_ideal[bitstring] = 0.0
counts_ideal["1111"] = 0.5
counts_ideal["0000"] = 0.5


prob_distance = 0.0
prob_distance_ps = 0.0

for bitstring in counts_ideal.keys():
    dist = 0.0
    dist_ps = 0.0
    if bitstring in counts:
        dist = abs(counts[bitstring] - counts_ideal[bitstring])
    if bitstring in counts_ps:
        dist_ps = abs(counts_ps[bitstring] - counts_ideal[bitstring])
    prob_distance += dist
    prob_distance_ps += dist_ps


print(
    f"Distance from ideal distribution before postselection: "
    f"{1-prob_distance*0.5}"
)
print(
    f"Distance from ideal distribution before after-selection: "
    f"{1-prob_distance_ps*0.5}"
)


plot_histogram([counts, counts_ps], legend=["Normal", "Post selected"])

Output:

Distance from ideal distribution before postselection: 0.95025
Distance from ideal distribution before after-selection: 0.9337171538234426
Output of the previous code cell

Aunque la poselección puede mejorar considerablemente la calidad de los resultados al descartar las mediciones de resultados que se vieron afectadas por ruido no markoviano, por sí sola no constituye una solución completa para la mitigación de errores. La postselección reduce el impacto de ciertos errores al descartar los resultados de medición no válidos, pero esto conlleva un mayor esfuerzo de muestreo y no resuelve todos los mecanismos de error presentes en el hardware cuántico a corto plazo. Por lo tanto, es probable que no basarse únicamente en la poselección resulte insuficiente para circuitos más complejos o de mayor profundidad. Por el contrario, la poselección resulta más eficaz cuando se utiliza como parte de una estrategia más amplia de mitigación de errores —complementando técnicas como la mitigación de errores de medición, la compilación de circuitos sensible al ruido o la cancelación probabilística de errores— para mejorar la fiabilidad de las cargas de trabajo cuánticas, al tiempo que se logra un equilibrio entre la precisión y el coste de los recursos.


Próximos pasos

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