Nota
Esta página foi gerada, a partir do tutorials/simulators/3_building_noise_models.ipynb.
Execute interativamente no IBM Quantum lab.
Construindo Modelos de Ruído¶
Introdução¶
Este notebook apresenta como utilizar o módulo noise
do Qiskit Aer, para construir modelos de ruído personalizados para simulações ruidosas.
[1]:
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit.quantum_info import Kraus, SuperOp
from qiskit.providers.aer import AerSimulator
from qiskit.tools.visualization import plot_histogram
# Import from Qiskit Aer noise module
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise import QuantumError, ReadoutError
from qiskit.providers.aer.noise import pauli_error
from qiskit.providers.aer.noise import depolarizing_error
from qiskit.providers.aer.noise import thermal_relaxation_error
Módulo de Ruído do Qiskit Aer¶
O módulo noise
do Qiskit Aer contém classes Python para a construção de 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 ruidosa.A classe
QuantumError
, que descreve os erros de porta CPTP. Esses podem ser aplicados:Depois de instruções gate ou reset
Antes de instruções measure.
A classe
ReadoutError
que descreve erros de leitura clássicos.
Erros Quânticos¶
Em vez de lidar com o objeto QuantumError
, diretamente, muitas funções auxiliares existem para gerar, automaticamente, um tipo específico de erro quântico parametrizado. Estes estão contidos no módulo noise
e incluem funções para muitos tipos de erros comuns utilizados na pesquisa de computação quântica. Os nomes das funções e o tipo de erro que elas retornam são:
Função de erro padrão |
Detalhes |
---|---|
|
um canal de erro CPTP de n qubits geral dado como uma lista de matrizes de Kraus \([K_0, ...]\). |
|
um erro unitário misto de n qubits dado como uma lista de matrizes unitárias e probabilidades \([(U_0, p_0),...]\). |
|
um erro unitário coerente de n qubits dado como uma única matriz unitária \(U\). |
|
um canal de erro Pauli de n qubits (unitária mista) dado como uma lista de Pauli’s e probabilidades \([(P_0, p_0),...]\) |
|
um canal de erro de despolarização de n qubits parametrizado por uma probabilidade de despolarização \(p\). |
|
Erro de reset de um qubit-único (um estado) parametrizado por probabilidades \(p_0, p_1\) de resetar para o estado \(|0\rangle\), \(|1\rangle\). |
|
um canal de relaxação térmica de um único qubit parametrizado por constantes de tempo de relaxação \(T_1\), \(T_2\), tempo de porta \(t\), e população térmica do estado excitado \(p_1\). |
|
Um canal de erro de amortecimento de fase combinada e amplitude generalizada de um único qubit dado por um parâmetro de amortecimento de amplitude \(\lambda\), um parâmetro de amortecimento de fase \(\gamma\), e uma população térmica do estado excitado \(p_1\). |
|
Um canal de erro de amortecimento de amplitude generalizada de um único qubit dado por um parâmetro de amortecimento de amplitude \(\lambda\), e uma população térmica de estado excitado \(p_1\). |
|
Um canal de erro de amortecimento de fase de um único qubit, dado por um parâmetro de amortecimento de fase \(\gamma\) |
Combinando erros quânticos¶
As instâncias de QuantumError
podem ser combinadas usando a composição, o produto tensorial e a expansão tensorial (produto tensorial de ordem inversa) para produzir o novos QuantumErrors
como:
Composição: \(\cal{E}(\rho)=\cal{E_2}(\cal{E_1}(\rho))\) como
error = error1.compose(error2)
Produto tensorial: \(\cal{E}(\rho) =(\cal{E_1}\otimes\cal{E_2})(\rho)\) como
error error1.tensor(error2)
Produto de expansão: \(\cal{E}(\rho) =(\cal{E_2}\otimes\cal{E_1})(\rho)\) as
error error1.expand(error2)
Exemplo¶
Por exemplo para construir um erro de inversão de bit de um único qubit de 5%:
[2]:
# 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, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}]
P(1) = 0.95, QasmQobjInstructions = [[{'name': 'id', 'qubits': [0]}]
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.05, QasmQobjInstructions = [[{'name': 'z', 'qubits': [0]}]
P(1) = 0.95, QasmQobjInstructions = [[{'name': 'id', 'qubits': [0]}]
[3]:
# 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, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}, {'name': 'z', 'qubits': [0]}]
P(1) = 0.0475, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}]
P(2) = 0.0475, QasmQobjInstructions = [[{'name': 'z', 'qubits': [0]}]
P(3) = 0.9025, QasmQobjInstructions = [[{'name': 'id', 'qubits': [0]}]
[4]:
# 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, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}, {'name': 'z', 'qubits': [1]}]
P(1) = 0.0475, QasmQobjInstructions = [[{'name': 'z', 'qubits': [1]}]
P(2) = 0.0475, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}]
P(3) = 0.9025, QasmQobjInstructions = [[{'name': 'id', 'qubits': [1]}]
Convertendo para e, a partir de operadores QuantumChannel¶
Podemos, também, converter em ambas as direções entre objetos QuantumError
no Qiskit Aer e objetos QuantumChannel
no Qiskit Terra.
[5]:
# 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,))
[6]:
# 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,))
[7]:
# 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) = 0.04999999999999995, QasmQobjInstructions = [[{'name': 'x', 'qubits': [0]}]
P(1) = 0.9500000000000002, QasmQobjInstructions = [[{'name': 'id', 'qubits': [0]}]
[7]:
True
Erro de Leitura¶
Erros de leitura clássicos são especificados por uma lista de vetores de probabilidade de atribuição \(P(A|B)\):
\(A\) é o valor do bit clássico gravado
\(B\) é o valor verdadeiro do bit retornado da medição
Por exemplo, para 1 qubit: $ P(A|B) = [P(A|0), P(A|1)]$.
[8]:
# Measurement miss-assignement probabilities
p0given1 = 0.1
p1given0 = 0.05
ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])
[8]:
ReadoutError([[0.95 0.05]
[0.1 0.9 ]])
Erros de leitura também podem ser combinados usando: compose
, tensor
e expand
, como com 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 que ele atua sobre, e em quais qubits aplicá-lo. Há três casos para erros quânticos:
Erro quântico em todos qubits
Erro quântico de qubit específico
Erro quântico não local
Erro quântico em todos qubits¶
Isto aplica o mesmo erro, para qualquer ocorrência de uma instrução, independentemente, de quais qubits ele atua.
Ele é adicionado como noise_model.add_all_qubit_quantum_error(error, instructions)
:
[9]:
# 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', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
All-qubits errors: ['u1', 'u2', 'u3']
Erro quântico de qubit específico¶
Isto aplica o erro, a qualquer ocorrência de uma instrução atuando em uma lista especificada de qubits. Observe que a ordem dos qubits importa: Para uma porta de 2 qubits um erro aplicado aos qubits [0, 1] é diferente de um aplicado aos qubits [1 0] por exemplo.
É adicionado como noise_model.add_quantum_error(error, instructions, qubits)
:
[10]:
# 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', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
Qubits with noise: [0]
Specific qubit errors: [('u1', [0]), ('u2', [0]), ('u3', [0])]
Erro quântico de qubit não local¶
Isto aplica um erro a um conjunto específico de qubits de ruído, após qualquer ocorrência de uma instrução, agindo sobre um conjunto específico de qubits de porta.
É adicionado como noise_model.add_nonlocal_quantum_error(error, instructions, instr_qubits, error_qubits)
:
[11]:
# Create an empty noise model
noise_model = NoiseModel()
# Add depolarizing error on qubit 2 forall single qubit u1, u2, u3 gates on qubit 0
error = depolarizing_error(0.05, 1)
noise_model.add_nonlocal_quantum_error(error, ['u1', 'u2', 'u3'], [0], [2])
# Print noise model info
print(noise_model)
NoiseModel:
Basis gates: ['cx', 'id', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
Qubits with noise: [0, 2]
Non-local specific qubit errors: [('u1', [0], [2]), ('u2', [0], [2]), ('u3', [0], [2])]
Executando uma simulação ruidosa com um modelo de ruído¶
O comando AerSimulator(noise_model=noise_model)
retorna um simulador configurado para o modelo de ruído dado. Além de configurar o modelo de ruído do simulador, ele também sobrepõe suas portas base, de acordo com as portas do modelo de ruído.
Exemplos de Modelo de Ruído¶
Vamos, agora, dar alguns exemplos de modelos de ruído. Para as nossas demonstrações, utilizaremos um circuito de teste simples, gerando um estado GHZ de n qubits:
[12]:
# 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¶
[13]:
# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))
[13]:

Exemplo de Ruído 1: Modelo básico de ruído de erro de inversão de bit¶
Vamos considerar um simples exemplo de modelo de ruído de brinquedo comum na pesquisa de teoria da informação quântica:
Ao aplicar uma porta de um único qubit, inverte o estado do qubit com probabilidade
p_gate1
.Ao aplicar uma porta de 2 qubits, aplica erros de um único qubit para cada qubit.
Ao resetar um qubit, faça-o para 1 em vez de 0 com probabilidade
p_reset
.Ao medir um qubit, inverte o estado do qubit com probabilidade
p_meas
.
[14]:
# 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', 'u1', 'u2', 'u3']
Instructions with noise: ['reset', 'u3', 'cx', 'u2', 'u1', 'measure']
All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']
Executando a simulação de ruído¶
[15]:
# Create noisy simulator backend
sim_noise = AerSimulator(noise_model=noise_bit_flip)
# Transpile circuit for noisy basis gates
circ_tnoise = transpile(circ, sim_noise)
# 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)
[15]:

Exemplo 2: relaxamento térmico T1/T2¶
Agora, considere um modelo de erro mais realista, baseado na relaxação térmica com o ambiente do qubit: * Cada qubit é parametrizado por uma constante de tempo de relaxação térmica \(T_1\) e uma constante de tempo de desfasagem \(T_2\). * Observe que devemos ter \(T_2 \le 2 T_1\). * As taxas de erro nas instruções são determinadas pelos tempos de porta e pelos valores \(T_1\), \(T_2\) do qubit.
[16]:
# 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', 'u2', 'u3']
Instructions with noise: ['reset', 'cx', 'u3', 'u2', 'measure']
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])]
Executando a simulação de ruído¶
[17]:
# Run the noisy simulation
sim_thermal = AerSimulator(noise_model=noise_thermal)
# Transpile circuit for noisy basis gates
circ_tthermal = transpile(circ, sim_thermal)
# 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)
[17]:

[18]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | 0.25.0 |
Terra | 0.17.0 |
Aer | 0.8.0 |
Ignis | 0.6.0 |
Aqua | 0.9.0 |
IBM Q Provider | 0.12.2 |
System information | |
Python | 3.7.7 (default, May 6 2020, 04:59:01) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 6 |
Memory (Gb) | 32.0 |
Fri Apr 02 12:05:34 2021 EDT |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
[ ]: