Construir modelos de ruído
Versões dos pacotes
O código nesta página foi desenvolvido com os seguintes requisitos. Recomendamos utilizar essas versões ou versões mais recentes.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17
Esta página mostra como usar o módulo noise do Qiskit Aer para construir modelos de ruído para simular circuitos quânticos na presença de erros. Isso é útil para emular processadores quânticos com ruído e para estudar os efeitos do ruído na execução de algoritmos quânticos.
# 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,
)
Módulo noise do Qiskit Aer
O módulo noise do Qiskit Aer contém classes Python para construir modelos de ruído personalizados para simulação. Há três classes principais:
-
A classe
NoiseModel, que armazena um modelo de ruído usado para simulação com ruído. -
A classe
QuantumError, que descreve erros de gate do tipo CPTP. Esses erros podem ser aplicados:- Após instruções de gate ou reset
- Antes de instruções de measure.
-
A classe
ReadoutError, que descreve erros clássicos de leitura.
Inicializando um modelo de ruído a partir de um backend
Você pode inicializar um modelo de ruído com parâmetros definidos a partir dos dados de calibração mais recentes de um backend físico:
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
noise_model = NoiseModel.from_backend(backend)
Isso produzirá um modelo de ruído que aproxima, de forma grosseira, os erros que seriam encontrados ao usar esse backend. Se você quiser ter um controle mais detalhado sobre os parâmetros do modelo de ruído, precisará criar seu próprio modelo, conforme descrito no restante desta página.
Erros quânticos
Em vez de lidar diretamente com o objeto QuantumError, existem muitas funções auxiliares para gerar automaticamente um tipo específico de erro quântico parametrizado. Essas funções estão contidas no módulo noise e incluem funções para muitos tipos de erro comuns usados na pesquisa em computação quântica. Os nomes das funções e o tipo de erro que retornam são:
| Função de erro padrão | Detalhes |
|---|---|
kraus_error | um canal de erro CPTP de n-qubits em geral, dado como uma lista de matrizes de Kraus . |
mixed_unitary_error | um erro unitário misto de n-qubits dado como uma lista de matrizes unitárias e probabilidades . |
coherent_unitary_error | um erro unitário coerente de n-qubits dado como uma única matriz unitária . |
pauli_error | um canal de erro de Pauli de n-qubits (unitário misto) dado como uma lista de Paulis e probabilidades |
depolarizing_error | um canal de erro de despolarização de n-qubits parametrizado por uma probabilidade de despolarização . |
reset_error | um erro de reset de um único qubit parametrizado pelas probabilidades de reinicializar para o estado , . |
thermal_relaxation_error | um canal de relaxamento térmico de um único qubit parametrizado pelas constantes de tempo de relaxamento , , tempo de gate e população térmica do estado excitado . |
phase_amplitude_damping_error | Um canal de erro de amortecimento combinado de fase e amplitude generalizado para um único qubit, dado por um parâmetro de amortecimento de amplitude , um parâmetro de amortecimento de fase e uma população térmica do estado excitado . |
amplitude_damping_error | Um canal de erro de amortecimento de amplitude generalizado para um único qubit, dado por um parâmetro de amortecimento de amplitude e uma população térmica do estado excitado . |
phase_damping_error | Um canal de erro de amortecimento de fase para um único qubit, dado por um parâmetro de amortecimento de fase . |
Combinando erros quânticos
Instâncias de QuantumError podem ser combinadas usando composição, produto tensorial e expansão tensorial (produto tensorial em ordem inversa) para produzir novos QuantumErrors da seguinte forma:
- Composição: como
error = error1.compose(error2) - Produto tensorial: como
error = error1.tensor(error2) - Produto de expansão: como
error = error1.expand(error2)
Exemplo
Para construir um erro de bit-flip de 5% em um único qubit:
# 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 ├
└───┘
Convertendo para e a partir de operadores QuantumChannel
Também podemos converter entre objetos QuantumError no Qiskit Aer e objetos QuantumChannel no 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
Erro de leitura
Os erros clássicos de leitura são especificados por uma lista de vetores de probabilidade de atribuição :
- é o valor do bit clássico registrado
- é o valor do bit verdadeiro retornado pela medição
Por exemplo, para um qubit: .
# 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 ]])
Os erros de leitura também podem ser combinados usando compose, tensor e expand, assim como os erros quânticos.
Adicionando erros a um modelo de ruído
Ao adicionar um erro quântico a um modelo de ruído, devemos especificar o tipo de instrução sobre a qual ele age e quais qubits ele deve ser aplicado. Há dois casos de erros quânticos:
- Erro quântico para todos os qubits
- Erro quântico para qubits específicos
1. Erro quântico para todos os qubits
Isso aplica o mesmo erro a qualquer ocorrência de uma instrução, independentemente de quais qubits ela age.
É adicionado como 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. Erro quântico para qubits específicos
Isso aplica o erro a qualquer ocorrência de uma instrução que age sobre uma lista especificada de qubits. Note que a ordem dos qubits importa: por exemplo, um erro aplicado aos qubits [0, 1] para um gate de dois qubits é diferente de um aplicado aos qubits [1, 0].
É adicionado como 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 sobre erro quântico de qubit não local
NoiseModel não suporta a adição de erros quânticos de qubit não local. Esses erros devem ser tratados fora do NoiseModel. Isso sugere que você deve escrever sua própria transpiler pass (TransformationPass) e executá-la logo antes de rodar o simulador, caso precise inserir seus erros quânticos no circuito sob suas próprias condições.
Executar uma simulação com ruído usando um modelo de ruído
O comando AerSimulator(noise_model=noise_model) retorna um simulador configurado com o modelo de ruído especificado. Além de definir o modelo de ruído do simulador, ele também substitui os gates de base do simulador, de acordo com os gates do modelo de ruído.
Exemplos de modelos de ruído
Apresentaremos agora alguns exemplos de modelos de ruído. Para as demonstrações, utilizamos um circuito de teste simples que gera um estado GHZ de n-qubits:
# 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
Simulação ideal
# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))
Exemplo de ruído 1: Modelo de ruído com erro de bit-flip básico
Vamos considerar um exemplo simples de modelo de ruído, comum na pesquisa em teoria da informação quântica:
- Ao aplicar um gate de um único qubit, inverta o estado do qubit com probabilidade
p_gate1. - Ao aplicar um gate de dois qubits, aplique erros de um único qubit a cada qubit.
- Ao reinicializar um qubit, reinicialize para 1 em vez de 0 com probabilidade
p_reset. - Ao medir um qubit, inverta o estado do qubit com probabilidade
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']
Executar a simulação com ruído
# 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)
Exemplo 2: Relaxamento térmico T1/T2
Agora considere um modelo de erro mais realista baseado no relaxamento térmico com o ambiente do qubit:
- Cada qubit é parametrizado por uma constante de tempo de relaxamento térmico e uma constante de tempo de defasagem .
- Note que devemos ter .
- As taxas de erro nas instruções são determinadas pelos tempos de gate e pelos valores , dos qubits.
# 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))]
Executar a simulação com ruído
# 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)
Próximos passos
- Para simular circuitos com ruído, veja Simulação exata e com ruído usando as primitivas do Qiskit Aer.
- Consulte a referência do módulo noise do Qiskit Aer.