Skip to main content
IBM Quantum Platform

Esecuzione del debug dei processi con NEAT

  • Il codice di questa pagina è stato sviluppato in base ai seguenti requisiti. Si consiglia di utilizzare queste versioni o versioni più recenti.

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

È possibile utilizzare la Neat classe per analizzare l'impatto del rumore su un carico di lavoro di Estimator. Per la verifica della sintassi, utilizza la modalità di test locale.


Neat utilizzo della classe

Prima di inviare un carico di lavoro che richiede un elevato impiego di risorse per l'esecuzione sull'hardware, è possibile utilizzare la classe NEAT (Noisy Estimator Analyzer Tool) di IBM Quantum Compute per verificare che il carico di lavoro dell'Estimator sia configurato correttamente, che sia in grado di restituire risultati accurati, che utilizzi le opzioni più appropriate per il problema specificato e altro ancora.

Neat Cliffordizza i circuiti di ingresso per una simulazione efficiente, pur mantenendo la sua struttura e profondità. I circuiti Clifford soffrono di livelli simili di rumore e sono un buon proxy per studiare il circuito originale di interesse.

Per prima cosa, importa i pacchetti necessari ed effettua l'autenticazione al servizio di comput IBM Quantum.

Prepara l'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)

Inizializzare un circuito di destinazione

Si consideri un circuito a sei qubit con le seguenti proprietà:

  • Alterna rotazioni RZ casuali e strati di porte CNOT.
  • Ha una struttura speculare, cioè applica un U unitario seguito dal suo 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

Scegliere gli operatori single-Pauli Z come osservabili e usarli per inizializzare i blocchi primitivi unificati (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']

Cliffordizza i circuiti

I circuiti PUB definiti in precedenza non sono Clifford, il che li rende difficili da simulare in modo classico. Tuttavia, è possibile utilizzare il metodo Neat to_clifford per mapparli in circuiti di Clifford per una simulazione più efficiente. Il metodo to_clifford è un wrapper attorno al passaggio ConvertISAToClifford che può essere usato anche indipendentemente. In particolare, sostituisce le porte a singolo qubit non Clifford del circuito originale con porte a singolo qubit Clifford, ma non modifica le porte a due qubit, il numero di qubit o la profondità del circuito.

Per ulteriori informazioni sulla simulazione dei circuiti Clifford, vedere Simulazione efficiente di circuiti stabilizzatori con le primitive Aer di Qiskit.

Per prima cosa, inizializzare 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)

Quindi, Cliffordizzare i PUB.

clifford_pubs = analyzer.to_clifford(pubs)

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

Output:

Output of the previous code cell

Applicazione 1: Analizzare l'impatto del rumore sulle uscite del circuito

Questo esempio mostra come utilizzare Neat per studiare l'impatto di diversi modelli di rumore sui PUB in funzione della profondità del circuito, eseguendo simulazioni sia in condizioni ideali (ideal_sim) che rumorose (noisy_sim). Questo può essere utile per definire le aspettative sulla qualità dei risultati sperimentali prima di eseguire un lavoro su una QPU. Per ulteriori informazioni sui modelli di rumore, consultare Simulazione esatta e rumorosa con le primitive Aer di Qiskit.

I risultati simulati supportano le operazioni matematiche e possono quindi essere confrontati tra loro (o con i risultati sperimentali) per calcolare le cifre di merito.

Caution

Una QPU può essere influenzata da diversi tipi di rumore. Il modello di rumore di Qiskit Aer utilizzato qui simula solo alcuni di essi e quindi è probabile che il rumore sia meno grave di quello di una QPU reale.

Per i dettagli su quali errori vengono inclusi quando si inizializza un modello di rumore da una QPU, si veda il riferimento all'API di Aer NoiseModel API.

Iniziare eseguendo simulazioni classiche ideali e rumorose.

# 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.9921875 , 0.99414062, 1.        , 0.99609375, 0.99609375,
       0.99609375])), NeatPubResult(vals=array([0.97460938, 0.97851562, 0.98046875, 0.99609375, 0.99414062,
       0.99609375])), NeatPubResult(vals=array([0.96679688, 0.98242188, 0.97460938, 0.98046875, 0.99414062,
       0.99609375])), NeatPubResult(vals=array([0.96679688, 0.96289062, 0.97460938, 0.9765625 , 0.98632812,
       0.99414062])), NeatPubResult(vals=array([0.921875  , 0.92773438, 0.95898438, 0.9609375 , 0.97265625,
       0.98242188])), NeatPubResult(vals=array([0.90820312, 0.92773438, 0.95507812, 0.9609375 , 0.97460938,
       0.984375  ]))])

Successivamente, applicare le operazioni matematiche per calcolare la differenza assoluta. Il resto della guida utilizza la differenza assoluta come cifra di merito per confrontare i risultati ideali con quelli rumorosi o sperimentali, ma è possibile impostare cifre di merito simili.

La differenza assoluta mostra che l'impatto del rumore cresce con le dimensioni dei circuiti.

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

    --> The closer to `0`, the better.
    """
    d = abs(res1 - res2)
    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.42%

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

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

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

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

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

Per migliorare i circuiti di questo tipo è possibile seguire queste linee guida approssimative e semplificate:

  • Se la differenza media assoluta è superiore al 90%, è probabile che la mitigazione non sia utile.
  • Se la differenza assoluta media è inferiore al 90%, l' amplificazione probabilistica degli errori (PEA) sarà probabilmente in grado di migliorare i risultati.
  • Se la differenza media assoluta è inferiore all'80%, anche la ZNE con ripiegamento delle porte sarà probabilmente in grado di migliorare i risultati.

Poiché tutte le differenze assolute di cui sopra sono inferiori al 90%, applicando la PEA al circuito originale si spera di migliorare la qualità dei risultati.

Nell'analizzatore è possibile specificare diversi modelli di rumore. L'esempio seguente esegue lo stesso test, ma aggiunge un modello di rumore personalizzato.

# 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", "cz"], 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:
  3.78%

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

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

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

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

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

Come mostrato, dato un modello di rumore, è possibile cercare di quantificare l'impatto del rumore sui PUB (versione Cliffordizzata dei PUB) di interesse prima di eseguirli su una QPU.


Applicazione 2: Confronto tra diverse strategie

Questo esempio utilizza Neat per aiutare a identificare le opzioni migliori per i vostri PUB. A tal fine, si consideri l'esecuzione di un problema di stima con PEA, che non può essere simulato con qiskit_aer. È possibile utilizzare Neat per determinare quali fattori di amplificazione del rumore funzionano meglio, quindi utilizzare tali fattori quando si esegue l'esperimento originale su 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]:
  3.92%

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

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

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

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

Il risultato con la differenza minore suggerisce quali opzioni scegliere.


Passi successivi

Suggerimenti
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.