Budowanie modeli szumu
Wersje pakietów
Kod na tej stronie został opracowany przy użyciu poniższych wymagań. Zalecamy korzystanie z tych wersji lub nowszych.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17
Ta strona pokazuje, jak używać modułu noise Qiskit Aer do budowania modeli szumu przeznaczonych do symulacji obwodów kwantowych w obecności błędów. Jest to przydatne do emulowania zaszumionych procesorów kwantowych oraz do badania wpływu szumu na wykonywanie algorytmów kwantowych.
# 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,
)
Moduł noise Qiskit Aer
Moduł noise Qiskit Aer zawiera klasy Pythona do budowania niestandardowych modeli szumu na potrzeby symulacji. Wyróżniamy trzy kluczowe klasy:
-
Klasa
NoiseModel, która przechowuje model szumu używany w symulacji z szumem. -
Klasa
QuantumError, która opisuje błędy bramek CPTP. Można je stosować:- Po instrukcjach bramki lub resetu
- Przed instrukcjami pomiaru.
-
Klasa
ReadoutError, która opisuje klasyczne błędy odczytu.
Inicjalizacja modelu szumu na podstawie Backendu
Możesz zainicjalizować model szumu z parametrami ustawionymi na podstawie najnowszych danych kalibracyjnych fizycznego Backendu:
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
noise_model = NoiseModel.from_backend(backend)
Pozwoli to uzyskać model szumu w przybliżeniu oddający błędy, z jakimi można się spotkać podczas używania danego Backendu. Jeśli chcesz mieć dokładniejszą kontrolę nad parametrami modelu szumu, musisz stworzyć własny model szumu – opisano to w dalszej części tej strony.
Błędy kwantowe
Zamiast bezpośrednio korzystać z obiektu QuantumError, dostępnych jest wiele funkcji pomocniczych do automatycznego generowania określonego rodzaju sparametryzowanego błędu kwantowego. Są one zawarte w module noise i obejmują funkcje dla wielu typowych rodzajów błędów stosowanych w badaniach nad obliczeniami kwantowymi. Nazwy funkcji oraz typ zwracanego przez nie błędu:
| Standardowa funkcja błędu | Szczegóły |
|---|---|
kraus_error | ogólny n-qubitowy kanał błędu CPTP podany jako lista macierzy Krausa . |
mixed_unitary_error | n-qubitowy mieszany unitarny błąd podany jako lista macierzy unitarnych i prawdopodobieństw . |
coherent_unitary_error | n-qubitowy spójny unitarny błąd podany jako pojedyncza macierz unitarna . |
pauli_error | n-qubitowy kanał błędu Pauliego (mieszany unitarny) podany jako lista operatorów Pauliego i prawdopodobieństw |
depolarizing_error | n-qubitowy kanał błędu depolaryzującego sparametryzowany prawdopodobieństwem depolaryzacji . |
reset_error | jednoQubitowy błąd resetu sparametryzowany prawdopodobieństwami zresetowania do stanu , . |
thermal_relaxation_error | jednoQubitowy kanał relaksacji termicznej sparametryzowany stałymi czasu relaksacji , , czasem trwania bramki oraz termiczną populacją wzbudzonego stanu . |
phase_amplitude_damping_error | jednoQubitowy uogólniony kombinowany kanał błędu tłumienia fazy i amplitudy, opisany parametrem tłumienia amplitudy , parametrem tłumienia fazy oraz termiczną populacją wzbudzonego stanu . |
amplitude_damping_error | jednoQubitowy uogólniony kanał błędu tłumienia amplitudy, opisany parametrem tłumienia amplitudy oraz termiczną populacją wzbudzonego stanu . |
phase_damping_error | jednoQubitowy kanał błędu tłumienia fazy, opisany parametrem tłumienia fazy . |
Łączenie błędów kwantowych
Instancje QuantumError można łączyć przy użyciu kompozycji, iloczynu tensorowego oraz rozwinięcia tensorowego (iloczyn tensorowy w odwrotnej kolejności), aby tworzyć nowe QuantumErrors:
- Kompozycja: jako
error = error1.compose(error2) - Iloczyn tensorowy: jako
error = error1.tensor(error2) - Rozwinięcie iloczynu: jako
error = error1.expand(error2)
Przykład
Aby skonstruować 5% jednoQubitowy błąd odwrócenia bitu:
# 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 ├
└───┘
Konwersja do i z operatorów QuantumChannel
Możemy też konwertować obiekty QuantumError w Qiskit Aer i obiekty QuantumChannel w Qiskit w obie strony.
# 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
Błąd odczytu
Klasyczne błędy odczytu są określane przez listę wektorów prawdopodobieństwa przypisania :
- to zarejestrowana klasyczna wartość bitu
- to prawdziwa wartość bitu zwrócona z pomiaru
Na przykład dla jednego Qubitu: .
# 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 ]])
Błędy odczytu można również łączyć przy użyciu compose, tensor i expand, podobnie jak w przypadku błędów kwantowych.
Dodawanie błędów do modelu szumu
Dodając błąd kwantowy do modelu szumu, należy podać typ instrukcji, na którą on działa, oraz Qubity, do których ma być zastosowany. Wyróżniamy dwa przypadki błędów kwantowych:
- Błąd kwantowy dla wszystkich Qubitów
- Błąd kwantowy dla konkretnych Qubitów
1. Błąd kwantowy dla wszystkich Qubitów
Powoduje zastosowanie tego samego błędu do każdego wystąpienia danej instrukcji, niezależnie od tego, na które Qubity ona działa.
Dodaje się go jako 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. Błąd kwantowy dla konkretnych Qubitów
Powoduje zastosowanie błędu do każdego wystąpienia instrukcji działającej na określonej liście Qubitów. Należy pamiętać, że kolejność Qubitów ma znaczenie: na przykład błąd zastosowany do Qubitów [0, 1] dla bramki dwuqubitowej różni się od błędu zastosowanego do Qubitów [1, 0].
Dodaje się go jako 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,))]
Uwaga dotycząca nielokalnych błędów kwantowych Qubitów
NoiseModel nie obsługuje dodawania nielokalnych błędów kwantowych Qubitów. Powinny być one obsługiwane poza NoiseModel. Oznacza to, że jeśli chcesz wstawiać błędy kwantowe do swojego Circuit w określonych przez siebie warunkach, powinieneś napisać własny pass Transpilera (TransformationPass) i uruchomić go tuż przed uruchomieniem symulatora.
Wykonywanie zaszumionej symulacji z modelem szumu
Polecenie AerSimulator(noise_model=noise_model) zwraca symulator skonfigurowany zgodnie z podanym modelem szumu. Oprócz ustawiania modelu szumu symulatora, nadpisuje też jego bramki bazowe zgodnie z bramkami modelu szumu.
Przykłady modeli szumu
Poniżej przedstawiamy kilka przykładów modeli szumu. Do demonstracji używamy prostego Circuit testowego generującego stan GHZ na n Qubitach:
# 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
Idealna symulacja
# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))
Przykład szumu 1: podstawowy model szumu z błędem bit-flip
Rozważmy prosty przykładowy model szumu powszechnie stosowany w badaniach z zakresu kwantowej teorii informacji:
- Podczas stosowania bramki jednoQubitowej stan Qubitu ulega odwróceniu z prawdopodobieństwem
p_gate1. - Podczas stosowania bramki dwuQubitowej do każdego Qubitu są stosowane błędy jednoQubitowe.
- Podczas resetowania Qubitu jest on resetowany do 1 zamiast do 0 z prawdopodobieństwem
p_reset. - Podczas pomiaru Qubitu jego stan ulega odwróceniu z prawdopodobieństwem
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']
Wykonywanie zaszumionej symulacji
# 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)
Przykład 2: relaksacja termiczna T1/T2
Rozważmy teraz bardziej realistyczny model błędów oparty na relaksacji termicznej z otoczeniem Qubitu:
- Każdy Qubit jest parametryzowany stałą czasu relaksacji termicznej oraz stałą czasu defazowania .
- Należy pamiętać, że musi zachodzić .
- Współczynniki błędów instrukcji są wyznaczane na podstawie czasów bramek oraz wartości , poszczególnych Qubitów.
# 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))]
Wykonywanie zaszumionej symulacji
# 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)
Następne kroki
- Aby symulować zaszumione Circuit, zapoznaj się z artykułem Dokładna i zaszumiona symulacja z użyciem prymitywów Qiskit Aer.
- Przejrzyj dokumentację referencyjną modułu szumu Qiskit Aer.