Vai al contenuto principale

Costruire modelli di rumore

Versioni dei pacchetti

Il codice in questa pagina è stato sviluppato utilizzando i seguenti requisiti. Si consiglia di usare queste versioni o versioni più recenti.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17

Questa pagina mostra come usare il modulo noise di Qiskit Aer per costruire modelli di rumore per simulare circuiti quantistici in presenza di errori. Questo è utile per emulare processori quantistici rumorosi e per studiare gli effetti del rumore sull'esecuzione di algoritmi quantistici.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Kraus, SuperOp
from qiskit.visualization import plot_histogram
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator

# Import from Qiskit Aer noise module
from qiskit_aer.noise import (
NoiseModel,
QuantumError,
ReadoutError,
depolarizing_error,
pauli_error,
thermal_relaxation_error,
)

Il modulo noise di Qiskit Aer

Il modulo noise di Qiskit Aer contiene classi Python per costruire modelli di rumore personalizzati per la simulazione. Esistono tre classi principali:

  1. La classe NoiseModel, che memorizza un modello di rumore usato per la simulazione rumorosa.

  2. La classe QuantumError, che descrive errori di gate CPTP. Questi possono essere applicati:

    • Dopo istruzioni di tipo gate o reset
    • Prima di istruzioni di tipo measure.
  3. La classe ReadoutError, che descrive errori di lettura classici.

Inizializzare un modello di rumore da un Backend

Puoi inizializzare un modello di rumore con parametri ricavati dai dati di calibrazione più recenti di un Backend fisico:

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
noise_model = NoiseModel.from_backend(backend)

Questo produrrà un modello di rumore che approssima grossolanamente gli errori che si incontrano quando si usa quel Backend. Se vuoi avere un controllo più dettagliato sui parametri del modello di rumore, dovrai crearne uno personalizzato, come descritto nel resto di questa pagina.

Errori quantistici

Invece di interagire direttamente con l'oggetto QuantumError, esistono molte funzioni di supporto per generare automaticamente un tipo specifico di errore quantistico parametrizzato. Queste sono contenute nel modulo noise e includono funzioni per molti tipi di errore comuni nella ricerca sul calcolo quantistico. I nomi delle funzioni e il tipo di errore che restituiscono sono:

Funzione di errore standardDettagli
kraus_errorun canale di errore CPTP a n qubit generico, fornito come lista di matrici di Kraus [K0,...][K_0, ...].
mixed_unitary_errorun errore unitario misto a n qubit, fornito come lista di matrici unitarie e probabilità [(U0,p0),...][(U_0, p_0),...].
coherent_unitary_errorun errore unitario coerente a n qubit, fornito come singola matrice unitaria UU.
pauli_errorun canale di errore di Pauli a n qubit (unitario misto), fornito come lista di operatori di Pauli e probabilità [(P0,p0),...][(P_0, p_0),...]
depolarizing_errorun canale di errore depolarizzante a n qubit, parametrizzato da una probabilità di depolarizzazione pp.
reset_errorun errore di reset su singolo qubit, parametrizzato dalle probabilità p0,p1p_0, p_1 di reset allo stato 0\vert0\rangle, 1\vert1\rangle.
thermal_relaxation_errorun canale di rilassamento termico su singolo qubit, parametrizzato dalle costanti di tempo di rilassamento T1T_1, T2T_2, dal tempo di gate tt e dalla popolazione termica dello stato eccitato p1p_1.
phase_amplitude_damping_errorun canale di errore generalizzato di smorzamento combinato di fase e ampiezza su singolo qubit, definito da un parametro di smorzamento di ampiezza λ\lambda, un parametro di smorzamento di fase γ\gamma e una popolazione termica dello stato eccitato p1p_1.
amplitude_damping_errorun canale di errore generalizzato di smorzamento di ampiezza su singolo qubit, definito da un parametro di smorzamento di ampiezza λ\lambda e una popolazione termica dello stato eccitato p1p_1.
phase_damping_errorun canale di errore di smorzamento di fase su singolo qubit, definito da un parametro di smorzamento di fase γ\gamma.

Combinare errori quantistici

Le istanze di QuantumError possono essere combinate tramite composizione, prodotto tensoriale ed espansione tensoriale (prodotto tensoriale in ordine inverso) per produrre nuovi QuantumError:

  • Composizione: E(ρ)=E2(E1(ρ))\cal{E}(\rho)=\cal{E_2}(\cal{E_1}(\rho)) come error = error1.compose(error2)
  • Prodotto tensoriale: E(ρ)=(E1E2)(ρ)\cal{E}(\rho) =(\cal{E_1}\otimes\cal{E_2})(\rho) come error = error1.tensor(error2)
  • Prodotto di espansione: E(ρ)=(E2E1)(ρ)\cal{E}(\rho) =(\cal{E_2}\otimes\cal{E_1})(\rho) come error = error1.expand(error2)

Esempio

Per costruire un errore di bit-flip su singolo qubit con probabilità del 5%:

# Construct a 1-qubit bit-flip and phase-flip errors
p_error = 0.05
bit_flip = pauli_error([("X", p_error), ("I", 1 - p_error)])
phase_flip = pauli_error([("Z", p_error), ("I", 1 - p_error)])
print(bit_flip)
print(phase_flip)
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.05, Circuit =
┌───┐
q: ┤ X ├
└───┘
P(1) = 0.95, Circuit =
┌───┐
q: ┤ I ├
└───┘
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.05, Circuit =
┌───┐
q: ┤ Z ├
└───┘
P(1) = 0.95, Circuit =
┌───┐
q: ┤ I ├
└───┘
# Compose two bit-flip and phase-flip errors
bitphase_flip = bit_flip.compose(phase_flip)
print(bitphase_flip)
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.0025000000000000005, Circuit =
┌───┐┌───┐
q: ┤ X ├┤ Z ├
└───┘└───┘
P(1) = 0.0475, Circuit =
┌───┐┌───┐
q: ┤ X ├┤ I ├
└───┘└───┘
P(2) = 0.0475, Circuit =
┌───┐┌───┐
q: ┤ I ├┤ Z ├
└───┘└───┘
P(3) = 0.9025, Circuit =
┌───┐┌───┐
q: ┤ I ├┤ I ├
└───┘└───┘
# Tensor product two bit-flip and phase-flip errors with
# bit-flip on qubit-0, phase-flip on qubit-1
error2 = phase_flip.tensor(bit_flip)
print(error2)
QuantumError on 2 qubits. Noise circuits:
P(0) = 0.0025000000000000005, Circuit =
┌───┐
q_0: ┤ X ├
├───┤
q_1: ┤ Z ├
└───┘
P(1) = 0.0475, Circuit =
┌───┐
q_0: ┤ I ├
├───┤
q_1: ┤ Z ├
└───┘
P(2) = 0.0475, Circuit =
┌───┐
q_0: ┤ X ├
├───┤
q_1: ┤ I ├
└───┘
P(3) = 0.9025, Circuit =
┌───┐
q_0: ┤ I ├
├───┤
q_1: ┤ I ├
└───┘

Convertire da e verso operatori QuantumChannel

È anche possibile convertire avanti e indietro tra oggetti QuantumError in Qiskit Aer e oggetti QuantumChannel in Qiskit.

# Convert to Kraus operator
bit_flip_kraus = Kraus(bit_flip)
print(bit_flip_kraus)
Kraus([[[-9.74679434e-01+0.j,  0.00000000e+00+0.j],
[ 0.00000000e+00+0.j, -9.74679434e-01+0.j]],

[[ 0.00000000e+00+0.j, 2.23606798e-01+0.j],
[ 2.23606798e-01+0.j, -4.96506831e-17+0.j]]],
input_dims=(2,), output_dims=(2,))
# Convert to Superoperator
phase_flip_sop = SuperOp(phase_flip)
print(phase_flip_sop)
SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],
[0. +0.j, 0.9+0.j, 0. +0.j, 0. +0.j],
[0. +0.j, 0. +0.j, 0.9+0.j, 0. +0.j],
[0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]],
input_dims=(2,), output_dims=(2,))
# Convert back to a quantum error
print(QuantumError(bit_flip_kraus))

# Check conversion is equivalent to original error
QuantumError(bit_flip_kraus) == bit_flip
QuantumError on 1 qubits. Noise circuits:
P(0) = 1.0, Circuit =
┌───────┐
q: ┤ kraus ├
└───────┘
True

Errore di readout

Gli errori di readout classici sono specificati da una lista di vettori di probabilità di assegnazione P(AB)P(A|B):

  • AA è il valore del bit classico registrato
  • BB è il valore del bit reale restituito dalla misura

Ad esempio, per un qubit: P(AB)=[P(A0),P(A1)] P(A|B) = [P(A|0), P(A|1)].

# Measurement misassignment probabilities
p0given1 = 0.1
p1given0 = 0.05

ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])
ReadoutError([[0.95 0.05]
[0.1 0.9 ]])

Anche gli errori di readout possono essere combinati usando compose, tensor ed expand, come per gli errori quantistici.

Aggiungere errori a un modello di rumore

Quando si aggiunge un errore quantistico a un modello di rumore, è necessario specificare il tipo di istruzione su cui agisce e a quali qubit applicarlo. Esistono due casi di errori quantistici:

  1. Errore quantistico su tutti i qubit
  2. Errore quantistico su qubit specifici

1. Errore quantistico su tutti i qubit

Questo applica lo stesso errore a qualsiasi occorrenza di un'istruzione, indipendentemente dai qubit su cui agisce.

Si aggiunge con noise_model.add_all_qubit_quantum_error(error, instructions):

# Create an empty noise model
noise_model = NoiseModel()

# Add depolarizing error to all single qubit u1, u2, u3 gates
error = depolarizing_error(0.05, 1)
noise_model.add_all_qubit_quantum_error(error, ["u1", "u2", "u3"])

# Print noise model info
print(noise_model)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
All-qubits errors: ['u1', 'u2', 'u3']

2. Errore quantistico su qubit specifici

Questo applica l'errore a qualsiasi occorrenza di un'istruzione che agisce su una lista specificata di qubit. Si noti che l'ordine dei qubit è importante: ad esempio, un errore applicato ai qubit [0, 1] per un gate a due qubit è diverso da uno applicato ai qubit [1, 0].

Si aggiunge con noise_model.add_quantum_error(error, instructions, qubits):

# Create an empty noise model
noise_model = NoiseModel()

# Add depolarizing error to all single qubit u1, u2, u3 gates on qubit 0 only
error = depolarizing_error(0.05, 1)
noise_model.add_quantum_error(error, ["u1", "u2", "u3"], [0])

# Print noise model info
print(noise_model)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
Qubits with noise: [0]
Specific qubit errors: [('u1', (0,)), ('u2', (0,)), ('u3', (0,))]

Nota sugli errori quantistici non locali

NoiseModel non supporta l'aggiunta di errori quantistici non locali. Questi devono essere gestiti al di fuori di NoiseModel. Ciò suggerisce che dovresti scrivere il tuo transpiler pass personalizzato (TransformationPass) ed eseguirlo appena prima di avviare il simulatore, se hai bisogno di inserire i tuoi errori quantistici nel circuito alle tue condizioni.

Eseguire una simulazione rumorosa con un modello di rumore

Il comando AerSimulator(noise_model=noise_model) restituisce un simulatore configurato con il modello di rumore fornito. Oltre a impostare il modello di rumore del simulatore, sovrascrive anche i gate di base del simulatore, in base ai gate del modello di rumore.

Esempi di modelli di rumore

Vedremo ora alcuni esempi di modelli di rumore. Per le nostre dimostrazioni usiamo un semplice circuito di test che genera uno stato GHZ a n qubit:

# System Specification
n_qubits = 4
circ = QuantumCircuit(n_qubits)

# Test Circuit
circ.h(0)
for qubit in range(n_qubits - 1):
circ.cx(qubit, qubit + 1)
circ.measure_all()
print(circ)
┌───┐                ░ ┌─┐
q_0: ┤ H ├──■─────────────░─┤M├─────────
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├──■────────░──╫─┤M├──────
└───┘┌─┴─┐ ░ ║ └╥┘┌─┐
q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───
└───┘┌─┴─┐ ░ ║ ║ └╥┘┌─┐
q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├
└───┘ ░ ║ ║ ║ └╥┘
meas: 4/════════════════════════╩══╩══╩══╩═
0 1 2 3

Simulazione ideale

# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))

Output of the previous code cell

Esempio di rumore 1: modello di rumore con errore di bit-flip semplice

Consideriamo un semplice esempio di modello di rumore comune nella ricerca sulla teoria dell'informazione quantistica:

  • Quando si applica un gate a singolo qubit, il qubit viene capovolto con probabilità p_gate1.
  • Quando si applica un gate a due qubit, si applicano errori su singolo qubit a ciascun qubit.
  • Quando si resetta un qubit, si resetta a 1 invece di 0 con probabilità p_reset.
  • Quando si misura un qubit, il suo stato viene capovolto con probabilità p_meas.
# Example error probabilities
p_reset = 0.03
p_meas = 0.1
p_gate1 = 0.05

# QuantumError objects
error_reset = pauli_error([("X", p_reset), ("I", 1 - p_reset)])
error_meas = pauli_error([("X", p_meas), ("I", 1 - p_meas)])
error_gate1 = pauli_error([("X", p_gate1), ("I", 1 - p_gate1)])
error_gate2 = error_gate1.tensor(error_gate1)

# Add errors to noise model
noise_bit_flip = NoiseModel()
noise_bit_flip.add_all_qubit_quantum_error(error_reset, "reset")
noise_bit_flip.add_all_qubit_quantum_error(error_meas, "measure")
noise_bit_flip.add_all_qubit_quantum_error(error_gate1, ["u1", "u2", "u3"])
noise_bit_flip.add_all_qubit_quantum_error(error_gate2, ["cx"])

print(noise_bit_flip)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'measure', 'cx', 'reset', 'u1']
All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']

Eseguire la simulazione rumorosa

# Create noisy simulator backend
sim_noise = AerSimulator(noise_model=noise_bit_flip)

# Transpile circuit for noisy basis gates
passmanager = generate_preset_pass_manager(
optimization_level=3, backend=sim_noise
)
circ_tnoise = passmanager.run(circ)

# Run and get counts
result_bit_flip = sim_noise.run(circ_tnoise).result()
counts_bit_flip = result_bit_flip.get_counts(0)

# Plot noisy output
plot_histogram(counts_bit_flip)

Output of the previous code cell

Esempio 2: rilassamento termico T1/T2

Consideriamo ora un modello di errore più realistico basato sul rilassamento termico con l'ambiente del qubit:

  • Ogni qubit è parametrizzato da una costante di tempo di rilassamento termico T1T_1 e da una costante di tempo di dephasing T2T_2.
  • Si noti che deve valere T22T1T_2 \le 2 T_1.
  • I tassi di errore sulle istruzioni sono determinati dai tempi di gate e dai valori T1T_1, T2T_2 dei qubit.
# T1 and T2 values for qubits 0-3
T1s = np.random.normal(
50e3, 10e3, 4
) # Sampled from normal distribution mean 50 microsec
T2s = np.random.normal(
70e3, 10e3, 4
) # Sampled from normal distribution mean 50 microsec

# Truncate random T2s <= T1s
T2s = np.array([min(T2s[j], 2 * T1s[j]) for j in range(4)])

# Instruction times (in nanoseconds)
time_u1 = 0 # virtual gate
time_u2 = 50 # (single X90 pulse)
time_u3 = 100 # (two X90 pulses)
time_cx = 300
time_reset = 1000 # 1 microsecond
time_measure = 1000 # 1 microsecond

# QuantumError objects
errors_reset = [
thermal_relaxation_error(t1, t2, time_reset) for t1, t2 in zip(T1s, T2s)
]
errors_measure = [
thermal_relaxation_error(t1, t2, time_measure) for t1, t2 in zip(T1s, T2s)
]
errors_u1 = [
thermal_relaxation_error(t1, t2, time_u1) for t1, t2 in zip(T1s, T2s)
]
errors_u2 = [
thermal_relaxation_error(t1, t2, time_u2) for t1, t2 in zip(T1s, T2s)
]
errors_u3 = [
thermal_relaxation_error(t1, t2, time_u3) for t1, t2 in zip(T1s, T2s)
]
errors_cx = [
[
thermal_relaxation_error(t1a, t2a, time_cx).expand(
thermal_relaxation_error(t1b, t2b, time_cx)
)
for t1a, t2a in zip(T1s, T2s)
]
for t1b, t2b in zip(T1s, T2s)
]

# Add errors to noise model
noise_thermal = NoiseModel()
for j in range(4):
noise_thermal.add_quantum_error(errors_reset[j], "reset", [j])
noise_thermal.add_quantum_error(errors_measure[j], "measure", [j])
noise_thermal.add_quantum_error(errors_u1[j], "u1", [j])
noise_thermal.add_quantum_error(errors_u2[j], "u2", [j])
noise_thermal.add_quantum_error(errors_u3[j], "u3", [j])
for k in range(4):
noise_thermal.add_quantum_error(errors_cx[j][k], "cx", [j, k])

print(noise_thermal)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'measure', 'cx', 'reset']
Qubits with noise: [0, 1, 2, 3]
Specific qubit errors: [('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('u2', (0,)), ('u2', (1,)), ('u2', (2,)), ('u2', (3,)), ('u3', (0,)), ('u3', (1,)), ('u3', (2,)), ('u3', (3,)), ('cx', (0, 0)), ('cx', (0, 1)), ('cx', (0, 2)), ('cx', (0, 3)), ('cx', (1, 0)), ('cx', (1, 1)), ('cx', (1, 2)), ('cx', (1, 3)), ('cx', (2, 0)), ('cx', (2, 1)), ('cx', (2, 2)), ('cx', (2, 3)), ('cx', (3, 0)), ('cx', (3, 1)), ('cx', (3, 2)), ('cx', (3, 3))]

Eseguire la simulazione rumorosa

# Run the noisy simulation
sim_thermal = AerSimulator(noise_model=noise_thermal)

# Transpile circuit for noisy basis gates
passmanager = generate_preset_pass_manager(
optimization_level=3, backend=sim_thermal
)
circ_tthermal = passmanager.run(circ)

# Run and get counts
result_thermal = sim_thermal.run(circ_tthermal).result()
counts_thermal = result_thermal.get_counts(0)

# Plot noisy output
plot_histogram(counts_thermal)

Output of the previous code cell

Passi successivi

Raccomandazioni