Skip to main content
IBM Quantum Platform

Depurar tarefas do Qiskit Runtime

  • O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos o uso dessas versões ou de versões mais recentes.

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

Você pode usar a Neat classe para analisar o impacto do ruído em uma carga de trabalho do Estimator. Para verificar a sintaxe, use o modo de teste local.


Neat uso da classe

Antes de enviar uma carga de trabalho Qiskit Runtime com uso intensivo de recursos para ser executada no hardware, você pode usar a classe Qiskit Runtime Neat (Noisy Estimator Analyzer Tool) para verificar se a carga de trabalho do Estimator está configurada corretamente, se provavelmente retornará resultados precisos, se usa as opções mais adequadas para o problema especificado e muito mais.

Neat Cliffordiza os circuitos de entrada para uma simulação eficiente, ao mesmo tempo em que mantém sua estrutura e profundidade. Os circuitos Clifford sofrem níveis semelhantes de ruído e são um bom substituto para o estudo do circuito original de interesse.

Primeiro, importe os pacotes relevantes e autentique-se no serviço Qiskit Runtime.

Preparar o ambiente

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 um circuito alvo

Considere um circuito de seis qubits que tenha as seguintes propriedades:

  • Alterna entre rotações aleatórias RZ e camadas de portas CNOT.
  • Tem uma estrutura de espelho, ou seja, aplica um U unitário seguido de seu 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

Escolha operadores de Pauli Z únicos como observáveis e use-os para inicializar os blocos unificados primitivos (PUBs).

# 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']

Cliffordize os circuitos

Os circuitos definidos anteriormente em PUB não são de Clifford, o que dificulta a simulação clássica. No entanto, você pode usar o método Neat to_clifford para mapeá-los para circuitos Clifford para uma simulação mais eficiente. O método to_clifford é um invólucro em torno da passagem ConvertISAToClifford que também pode ser usado de forma independente. Em particular, ele substitui as portas de qubit único não Clifford no circuito original por portas de qubit único Clifford, mas não altera as portas de dois qubits, o número de qubits ou a profundidade do circuito.

Consulte Simulação eficiente de circuitos estabilizadores com primitivos Qiskit Aer para obter mais informações sobre a simulação de circuitos Clifford.

Primeiro, inicialize 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)

Em seguida, Cliffordize os PUBs.

clifford_pubs = analyzer.to_clifford(pubs)

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

Output:

Output of the previous code cell

Aplicação 1: Analisar o impacto do ruído nas saídas do circuito

Este exemplo mostra como usar Neat para estudar o impacto de diferentes modelos de ruído em PUBs em função da profundidade do circuito, executando simulações em condições ideais (ideal_sim) e ruidosas (noisy_sim). Isso pode ser útil para definir expectativas sobre a qualidade dos resultados experimentais antes de executar um trabalho em uma QPU. Para saber mais sobre modelos de ruído, consulte Simulação exata e ruidosa com primitivas Qiskit Aer.

Os resultados simulados dão suporte a operações matemáticas e, portanto, podem ser comparados entre si (ou com resultados experimentais) para calcular as figuras de mérito.

Caution

Uma QPU pode ser afetada por diferentes tipos de ruído. O modelo de ruído do Qiskit Aer usado aqui simula apenas alguns deles e, portanto, é provável que seja menos grave do que o ruído em uma QPU real.

Para obter detalhes sobre quais erros são incluídos ao inicializar um modelo de ruído de uma QPU, consulte a referência da API Aer NoiseModel Referência da API.

Comece realizando simulações clássicas ideais e com ruído.

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

Em seguida, aplique operações matemáticas para calcular a diferença absoluta. O restante do guia usa a diferença absoluta como uma figura de mérito para comparar resultados ideais com resultados ruidosos ou experimentais, mas figuras de mérito semelhantes podem ser configuradas.

A diferença absoluta mostra que o impacto do ruído aumenta com o tamanho dos 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%

Você pode seguir essas diretrizes aproximadas e simplificadas para melhorar circuitos desse tipo:

Como todas as diferenças absolutas acima são inferiores a 90%, espera-se que a aplicação da AEP ao circuito original melhore a qualidade de seus resultados.

Você pode especificar diferentes modelos de ruído no analisador. O exemplo a seguir executa o mesmo teste, mas adiciona um modelo de ruído 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%

Conforme mostrado, com um modelo de ruído, você pode tentar quantificar o impacto do ruído nas PUBs de interesse (versão Cliffordizada das) antes de executá-las em uma QPU.


Aplicação 2: Comparar diferentes estratégias

Este exemplo usa o site Neat para ajudar a identificar as melhores opções para seus PUBs. Para isso, considere a execução de um problema de estimativa com PEA, que não pode ser simulado com qiskit_aer. Você pode usar o site Neat para ajudar a determinar quais fatores de amplificação de ruído funcionarão melhor e, em seguida, usar esses fatores ao executar o experimento original em uma 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%

O resultado com a menor diferença sugere quais opções devem ser escolhidas.


Próximas etapas

Recomendações
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.