Skip to main content
IBM Quantum Platform

Depurar trabajos de Qiskit Runtime

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

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    qiskit-aer~=0.17
    

Puedes utilizar la Neat clase para analizar el impacto del ruido en una carga de trabajo de Estimator. Para verificar la sintaxis, utiliza el modo de prueba local.


Neat uso de la clase

Antes de enviar una carga de trabajo de Qiskit Runtime que consuma muchos recursos para que se ejecute en hardware, puede utilizar la clase Qiskit Runtime Neat (Noisy Estimator Analyzer Tool) para verificar que la carga de trabajo de Estimator está configurada correctamente, que es probable que devuelva resultados precisos, que utiliza las opciones más adecuadas para el problema especificado, etc.

Neat Cliffordiza los circuitos de entrada para una simulación eficaz, conservando su estructura y profundidad. Los circuitos Clifford sufren niveles similares de ruido y son un buen sustituto para estudiar el circuito original de interés.

En primer lugar, importe los paquetes pertinentes y autentíquese en el servicio Qiskit Runtime.

Prepare el entorno

import numpy as np
import random

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
from qiskit_ibm_runtime.debug_tools import Neat

from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent
# Instruction Set Architecture (ISA) circuit.

pm = generate_preset_pass_manager(backend=backend, optimization_level=0)

# Set the random seed
random.seed(10)

Inicializar un circuito objetivo

Consideremos un circuito de seis qubits que tiene las siguientes propiedades:

  • Alterna entre rotaciones aleatorias RZ y capas de puertas CNOT.
  • Tiene una estructura especular, es decir, aplica un U unitario seguido de su inverso.
def generate_circuit(n_qubits, n_layers):
    r"""
    A function to generate a pseudo-random a circuit with ``n_qubits`` qubits
    and ``2*n_layers`` entangling layers of the type used in this notebook.
    """
    # An array of random angles
    angles = [
        [random.random() for q in range(n_qubits)] for s in range(n_layers)
    ]

    qc = QuantumCircuit(n_qubits)
    qubits = list(range(n_qubits))

    # do random circuit
    for layer in range(n_layers):
        # rotations
        for q_idx, qubit in enumerate(qubits):
            qc.rz(angles[layer][q_idx], qubit)

        # cx gates
        control_qubits = (
            qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
        )
        for qubit in control_qubits:
            qc.cx(qubit, qubit + 1)

    # undo random circuit
    for layer in range(n_layers)[::-1]:
        # cx gates
        control_qubits = (
            qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
        )
        for qubit in control_qubits:
            qc.cx(qubit, qubit + 1)

        # rotations
        for q_idx, qubit in enumerate(qubits):
            qc.rz(-angles[layer][q_idx], qubit)

    return qc


# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)

qc.draw("mpl", idle_wires=0)

Output:

Output of the previous code cell

Elija operadores Z de un solo Pauli como observables y utilícelos para inicializar los bloques unificados primitivos (PUB).

# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")

# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]

# Initialize the PUBs, which consist of six-qubit circuits
# with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]

pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]

Output:

Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

Cliffordizar los circuitos

Los circuitos PUB definidos anteriormente no son Clifford, lo que dificulta su simulación clásica. Sin embargo, puede utilizar el método Neat to_clifford para convertirlos en circuitos Clifford y conseguir una simulación más eficaz. El método to_clifford es una envoltura del método ConvertISAToClifford que también puede utilizarse de forma independiente. En concreto, sustituye las puertas single-qubit no Clifford del circuito original por puertas single-qubit Clifford, pero no muta las puertas two-qubit, el número de qubits ni la profundidad del circuito.

Consulte Simulación eficiente de circuitos estabilizadores con primitivas Qiskit Aer para obtener más información sobre la simulación de circuitos Clifford.

En primer lugar, inicializa Neat.

# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None

# Initialize `Neat`
analyzer = Neat(backend, noise_model)

A continuación, Cliffordiza los PUB.

clifford_pubs = analyzer.to_clifford(pubs)

clifford_pubs[0].circuit.draw("mpl", idle_wires=0)

Output:

Output of the previous code cell

Aplicación 1: Analizar el impacto del ruido en las salidas del circuito

Este ejemplo muestra cómo utilizar Neat para estudiar el impacto de diferentes modelos de ruido en los PUB en función de la profundidad del circuito, ejecutando simulaciones tanto en condiciones ideales (ideal_sim) como ruidosas (noisy_sim). Esto puede resultar útil para establecer expectativas sobre la calidad de los resultados experimentales antes de ejecutar un trabajo en una QPU. Para obtener más información sobre los modelos de ruido, consulte Simulación exacta y ruidosa con primitivas Aer de Qiskit.

Los resultados simulados admiten operaciones matemáticas, por lo que pueden compararse entre sí (o con resultados experimentales) para calcular cifras de mérito.

Caution

Una QPU puede verse afectada por distintos tipos de ruido. El modelo de ruido Qiskit Aer utilizado aquí sólo simula algunos de ellos y, por tanto, es probable que sea menos grave que el ruido de una QPU real.

Para más detalles sobre qué errores se incluyen al inicializar un modelo de ruido desde una QPU, consulte la referencia de la API Aer NoiseModel API reference.

Comience realizando simulaciones clásicas ideales y ruidosas.

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")

Output:

Ideal results:
 NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])

Noisy results:
 NeatResult([NeatPubResult(vals=array([0.99414062, 0.99414062, 0.99804688, 0.99609375, 0.98828125,
       0.99023438])), NeatPubResult(vals=array([0.9765625 , 0.97851562, 0.9765625 , 0.98632812, 0.98828125,
       0.99414062])), NeatPubResult(vals=array([0.953125  , 0.9609375 , 0.9609375 , 0.97265625, 0.97851562,
       0.97460938])), NeatPubResult(vals=array([0.953125  , 0.94335938, 0.94140625, 0.97070312, 0.96679688,
       0.99414062])), NeatPubResult(vals=array([0.94335938, 0.92382812, 0.95703125, 0.96875   , 0.96679688,
       0.97265625])), NeatPubResult(vals=array([0.92773438, 0.90429688, 0.91210938, 0.93554688, 0.95117188,
       0.97265625]))])

A continuación, aplica operaciones matemáticas para calcular la diferencia absoluta. En el resto de la guía se utiliza la diferencia absoluta como cifra de mérito para comparar resultados ideales con resultados ruidosos o experimentales, pero pueden establecerse cifras de mérito similares.

La diferencia absoluta muestra que el impacto del ruido crece con el tamaño de los circuitos.

# Figure of merit: Absolute difference
def rdiff(res1, re2):
    r"""The absolute difference between `res1` and re2`.

    --> The closer to `0`, the better.
    """
    d = abs(res1 - re2)
    return np.round(d.vals * 100, 2)


for idx, (ideal_res, noisy_res) in enumerate(
    zip(ideal_results, noisy_results)
):
    vals = rdiff(ideal_res, noisy_res)

    # Print the mean absolute difference for the observables
    mean_vals = np.round(np.mean(vals), 2)
    print(
        f"Mean absolute difference between ideal and noisy results "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_vals}%\n"
    )

Output:

Mean absolute difference between ideal and noisy results for circuits with 1 layers:
  0.65%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
  1.66%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
  3.32%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
  3.84%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
  4.46%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
  6.61%

Puedes seguir estas pautas aproximadas y simplificadas para mejorar los circuitos de este tipo:

Dado que todas las diferencias absolutas anteriores son inferiores al 90%, es de esperar que la aplicación de PEA al circuito original mejore la calidad de sus resultados.

Puede especificar diferentes modelos de ruido en el analizador. El siguiente ejemplo realiza la misma prueba pero añade un modelo de ruido personalizado.

# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
    noise_model.add_quantum_error(
        depolarizing_error(0.02, 2), ["ecr", "cx"], qubits
    )

# Update the analyzer's noise model
analyzer.noise_model = noise_model

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)

# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
    zip(ideal_results, noisy_results)
):
    values = rdiff(ideal_res, noisy_res)

    # Print the mean absolute difference for the observables
    mean_values = np.round(np.mean(values), 2)
    print(
        f"Mean absolute difference between ideal and noisy results "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_values}%\n"
    )

Output:

Mean absolute difference between ideal and noisy results for circuits with 1 layers:
  0.0%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
  0.0%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
  0.0%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
  0.0%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
  0.0%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
  0.0%

Como se muestra, dado un modelo de ruido, se puede tratar de cuantificar el impacto del ruido en la (versión Cliffordizada de los) PUBs de interés antes de ejecutarlos en una QPU.


Aplicación 2: Comparar diferentes estrategias

Este ejemplo utiliza Neat para ayudar a identificar las mejores opciones para sus PUBs. Para ello, considere la posibilidad de ejecutar un problema de estimación con PEA, que no puede simularse con qiskit_aer. Puede utilizar Neat para ayudar a determinar qué factores de amplificación de ruido funcionarán mejor, y luego utilizar esos factores cuando ejecute el experimento original en una QPU.

# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))

# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
    [1, 1.1],
    [1, 1.1, 1.2],
    [1, 1.5, 2],
    [1, 1.5, 2, 2.5, 3],
    [1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"

jobs = []
for factors in noise_factors:
    estimator.options.resilience.zne.noise_factors = factors
    jobs.append(estimator.run(clifford_pubs))

results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly determine
# the best choice for your options
for factors, res in zip(noise_factors, results):
    d = rdiff(ideal_results[0], res[0])
    print(
        f"Mean absolute difference for factors "
        f"{factors}:\n  {np.round(np.mean(d), 2)}%\n"
    )

Output:

Mean absolute difference for factors [1, 1.1]:
  4.57%

Mean absolute difference for factors [1, 1.1, 1.2]:
  5.24%

Mean absolute difference for factors [1, 1.5, 2]:
  2.62%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
  2.4%

Mean absolute difference for factors [1, 4]:
  2.05%

El resultado con la menor diferencia sugiere qué opciones elegir.


Próximos pasos

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