Zum Hauptinhalt springen

Rauschmodelle erstellen

Paketversionen

Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen die Verwendung dieser oder neuerer Versionen.

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

Diese Seite zeigt, wie du das Qiskit Aer noise-Modul verwendest, um Rauschmodelle zur Simulation von Quantenschaltkreisen in Anwesenheit von Fehlern zu erstellen. Dies ist nützlich für die Emulation verrauschter Quantenprozessoren und für die Untersuchung der Auswirkungen von Rauschen auf die Ausführung von Quantenalgorithmen.

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

Qiskit Aer noise-Modul

Das Qiskit Aer noise-Modul enthält Python-Klassen zum Erstellen angepasster Rauschmodelle für die Simulation. Es gibt drei Hauptklassen:

  1. Die NoiseModel-Klasse, die ein Rauschmodell für verrauschte Simulation speichert.

  2. Die QuantumError-Klasse, die CPTP-Gate-Fehler beschreibt. Diese können angewendet werden:

    • Nach Gate- oder Reset-Anweisungen
    • Vor Mess-Anweisungen.
  3. Die ReadoutError-Klasse, die klassische Auslese-Fehler beschreibt.

Initialisierung eines Rauschmodells von einem Backend

Du kannst ein Rauschmodell mit Parametern initialisieren, die aus den neuesten Kalibrierungsdaten für ein physisches Backend festgelegt werden:

from qiskit_ibm_runtime import QiskitRuntimeService

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

Dies ergibt ein Rauschmodell, das die Fehler, denen man bei der Verwendung dieses Backends begegnen würde, grob approximiert. Wenn du eine detailliertere Kontrolle über die Parameter des Rauschmodells haben möchtest, musst du dein eigenes Rauschmodell erstellen, wie im Rest dieser Seite beschrieben.

Quantenfehler

Anstatt direkt mit dem QuantumError-Objekt zu arbeiten, existieren viele Hilfsfunktionen, um automatisch einen bestimmten Typ parametrisierter Quantenfehler zu generieren. Diese sind im noise-Modul enthalten und umfassen Funktionen für viele gängige Fehlertypen, die in der Quantencomputing-Forschung verwendet werden. Die Funktionsnamen und der Fehlertyp, den sie zurückgeben, sind:

Standard-FehlerfunktionDetails
kraus_errorein allgemeiner n-Qubit-CPTP-Fehlerkanal, gegeben als Liste von Kraus-Matrizen [K0,...][K_0, ...].
mixed_unitary_errorein n-Qubit-gemischter unitärer Fehler, gegeben als Liste unitärer Matrizen und Wahrscheinlichkeiten [(U0,p0),...][(U_0, p_0),...].
coherent_unitary_errorein n-Qubit-kohärenter unitärer Fehler, gegeben als einzelne unitäre Matrix UU.
pauli_errorein n-Qubit-Pauli-Fehlerkanal (gemischt unitär), gegeben als Liste von Paulis und Wahrscheinlichkeiten [(P0,p0),...][(P_0, p_0),...]
depolarizing_errorein n-Qubit-Depolarisierungsfehlerkanal, parametrisiert durch eine Depolarisierungswahrscheinlichkeit pp.
reset_errorein Ein-Qubit-Reset-Fehler, parametrisiert durch Wahrscheinlichkeiten p0,p1p_0, p_1 für das Zurücksetzen auf den 0\vert0\rangle-, 1\vert1\rangle-Zustand.
thermal_relaxation_errorein Ein-Qubit-thermischer-Relaxationskanal, parametrisiert durch Relaxationszeitkonstanten T1T_1, T2T_2, Gate-Zeit tt und thermische Population des angeregten Zustands p1p_1.
phase_amplitude_damping_errorein Ein-Qubit-generalisierter kombinierter Phasen- und Amplitudendämpfungsfehlerkanal, gegeben durch einen Amplitudendämpfungsparameter λ\lambda, einen Phasendämpfungsparameter γ\gamma und eine thermische Population des angeregten Zustands p1p_1.
amplitude_damping_errorein Ein-Qubit-generalisierter Amplitudendämpfungsfehlerkanal, gegeben durch einen Amplitudendämpfungsparameter λ\lambda und eine thermische Population des angeregten Zustands p1p_1.
phase_damping_errorein Ein-Qubit-Phasendämpfungsfehlerkanal, gegeben durch einen Phasendämpfungsparameter γ\gamma.

Kombination von Quantenfehlern

QuantumError-Instanzen können durch Komposition, Tensorprodukt und Tensorerweiterung (umgekehrtes Tensorprodukt) kombiniert werden, um neue QuantumErrors zu erzeugen als:

  • Komposition: E(ρ)=E2(E1(ρ))\cal{E}(\rho)=\cal{E_2}(\cal{E_1}(\rho)) als error = error1.compose(error2)
  • Tensorprodukt: E(ρ)=(E1E2)(ρ)\cal{E}(\rho) =(\cal{E_1}\otimes\cal{E_2})(\rho) als error = error1.tensor(error2)
  • Expand-Produkt: E(ρ)=(E2E1)(ρ)\cal{E}(\rho) =(\cal{E_2}\otimes\cal{E_1})(\rho) als error = error1.expand(error2)

Beispiel

Um einen 5% Ein-Qubit-Bit-Flip-Fehler zu konstruieren:

# 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 ├
└───┘

Konvertierung zu und von QuantumChannel-Operatoren

Wir können auch zwischen QuantumError-Objekten in Qiskit Aer und QuantumChannel-Objekten in Qiskit hin- und herkonvertieren.

# 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

Auslese-Fehler

Klassische Auslese-Fehler werden durch eine Liste von Zuweisungswahrscheinlichkeitsvektoren P(AB)P(A|B) spezifiziert:

  • AA ist der aufgezeichnete klassische Bitwert
  • BB ist der wahre Bitwert, der von der Messung zurückgegeben wird

Zum Beispiel für ein 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 ]])

Auslese-Fehler können auch mit compose, tensor und expand kombiniert werden, wie bei Quantenfehlern.

Fehler zu einem Rauschmodell hinzufügen

Beim Hinzufügen eines Quantenfehlers zu einem Rauschmodell müssen wir den Typ der Anweisung angeben, auf die er wirkt, und auf welche Qubits er angewendet wird. Es gibt zwei Fälle von Quantenfehlern:

  1. All-Qubit-Quantenfehler
  2. Spezifischer Qubit-Quantenfehler

1. All-Qubit-Quantenfehler

Dies wendet denselben Fehler auf jedes Vorkommen einer Anweisung an, unabhängig davon, auf welche Qubits sie wirkt.

Es wird hinzugefügt als 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. Spezifischer Qubit-Quantenfehler

Dies wendet den Fehler auf jedes Vorkommen einer Anweisung an, die auf eine angegebene Liste von Qubits wirkt. Beachte, dass die Reihenfolge der Qubits wichtig ist: Zum Beispiel ist ein Fehler, der auf Qubits [0, 1] für ein Zwei-Qubit-Gate angewendet wird, anders als einer, der auf Qubits [1, 0] angewendet wird.

Es wird hinzugefügt als 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,))]

Hinweis zu nicht-lokalen Qubit-Quantenfehlern

NoiseModel unterstützt das Hinzufügen von nicht-lokalen Qubit-Quantenfehlern nicht. Diese sollten außerhalb von NoiseModel behandelt werden. Das bedeutet, du solltest deinen eigenen Transpiler-Pass schreiben (TransformationPass) und den Pass ausführen, kurz bevor du den Simulator ausführst, wenn du Quantenfehler unter deinen eigenen Bedingungen in deinen Schaltkreis einfügen musst.

Ausführung einer verrauschten Simulation mit einem Rauschmodell

Der Befehl AerSimulator(noise_model=noise_model) gibt einen Simulator zurück, der für das angegebene Rauschmodell konfiguriert ist. Zusätzlich zum Festlegen des Rauschmodells des Simulators überschreibt er auch die Basis-Gates des Simulators gemäß den Gates des Rauschmodells.

Rauschmodell-Beispiele

Wir geben nun einige Beispiele für Rauschmodelle. Für unsere Demonstrationen verwenden wir einen einfachen Testschaltkreis, der einen n-Qubit-GHZ-Zustand erzeugt:

# 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

Ideale Simulation

# 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

Rauschbeispiel 1: Einfaches Bit-Flip-Fehler-Rauschmodell

Betrachten wir ein einfaches Spielzeug-Rauschmodell-Beispiel, das in der Quanteninformationstheorie-Forschung üblich ist:

  • Beim Anwenden eines Ein-Qubit-Gates wird der Zustand des Qubits mit Wahrscheinlichkeit p_gate1 umgedreht.
  • Beim Anwenden eines Zwei-Qubit-Gates werden Ein-Qubit-Fehler auf jedes Qubit angewendet.
  • Beim Zurücksetzen eines Qubits wird auf 1 statt auf 0 mit Wahrscheinlichkeit p_reset zurückgesetzt.
  • Beim Messen eines Qubits wird der Zustand des Qubits mit Wahrscheinlichkeit p_meas umgedreht.
# 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']

Ausführung der verrauschten Simulation

# 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

Beispiel 2: T1/T2 thermische Relaxation

Betrachten wir nun ein realistischeres Fehlermodell, das auf thermischer Relaxation mit der Qubit-Umgebung basiert:

  • Jedes Qubit wird durch eine thermische Relaxationszeitkonstante T1T_1 und eine Dephasierungszeitkonstante T2T_2 parametrisiert.
  • Beachte, dass wir T22T1T_2 \le 2 T_1 haben müssen.
  • Fehlerraten bei Anweisungen werden durch Gate-Zeiten und Qubit-T1T_1-, T2T_2-Werte bestimmt.
# 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))]

Ausführung der verrauschten Simulation

# 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

Nächste Schritte

Empfehlungen