Skip to main content
IBM Quantum Platform

Criar modelos de ruído

  • O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos o uso dessas versões ou de versões mais recentes.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    qiskit-aer~=0.17
    

Esta página mostra como usar o módulo Qiskit Aer noise para criar 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.

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 Qiskit Aer

O módulo Qiskit Aer noise contém classes Python para criar modelos de ruído personalizados para simulação. Há três classes principais:

  1. A classe NoiseModel que armazena um modelo de ruído usado para simulação com ruído.

  2. A classe QuantumError que descreve os erros de porta do CPTP. Eles podem ser aplicados:

    • Após o portão ou instruções de reinicialização
    • Instruções antes da medição.
  3. A classe ReadoutError que descreve os erros clássicos de leitura.


Inicializar um modelo de ruído a partir de um backend

É possível inicializar um modelo de ruído com parâmetros definidos a partir dos dados de calibração mais recentes de um backend físico.

Note

Nestes exemplos, é utilizado o backend FakeSherbrooke``qiskit_ibm_runtime simulado do, mas você pode testá-lo em qualquer backend real ou simulado compatível com o Qiskit.

from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()
noise_model = NoiseModel.from_backend(backend)

Isso produzirá um modelo de ruído que se aproxima aproximadamente dos erros que seriam encontrados ao usar esse backend. Se quiser ter um controle mais detalhado sobre os parâmetros do modelo de ruído, será necessário criar seu próprio modelo de ruído, conforme descrito no restante desta página.


Erros quânticos

Em vez de lidar diretamente com o QuantumError objeto, existem muitas funções auxiliares para gerar automaticamente um tipo específico de erro quântico parametrizado. Elas estão contidas no noise módulo e incluem funções para muitos tipos de erros comuns usados na pesquisa em 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
kraus_errorum canal de erro CPTP geral de n qubits dado como uma lista de matrizes Kraus [K0,...][K_0, ...].
mixed_unitary_errorum erro unitário misto de n-qubit dado como uma lista de matrizes unitárias e probabilidades [(U0,p0),...][(U_0, p_0),...].
coherent_unitary_errorum erro unitário coerente de n qubits dado como uma única matriz unitária UU.
pauli_errorum canal de erro Pauli de n-qubit (unitário misto) fornecido como uma lista de Pauli's e probabilidades [(P0,p0),...][(P_0, p_0),...]
depolarizing_errorum canal de erro de despolarização de n-qubit parametrizado por uma probabilidade de despolarização pp.
reset_errorum erro de reinicialização de um único qubit parametrizado pelas probabilidades p0,p1p_0, p_1 de reinicialização para o estado 0\vert0\rangle 1\vert1\rangle.
thermal_relaxation_errorum canal de relaxamento térmico de um único qubit parametrizado por constantes de tempo de relaxamento T1T_1, T2T_2, tempo de porta tt e população térmica de estado excitado p1p_1.
phase_amplitude_damping_errorUm canal de erro de amortecimento de amplitude e fase combinada 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 de estado excitado p1p_1.
amplitude_damping_errorUm canal de erro de amortecimento de amplitude generalizado de um único qubit dado por um parâmetro de amortecimento de amplitude λ\lambda e uma população térmica de estado excitado p1p_1.
phase_damping_errorUm canal de erro de amortecimento de fase de um único qubit dado por um parâmetro de amortecimento γ\gamma de fase.

Combinar erros quânticos

QuantumError as instâncias podem ser combinadas usando composição, produto tensorial e expansão tensorial (produto tensorial de ordem inversa) para produzir novos QuantumErrors como:

  • Composição: E(ρ)=E2(E1(ρ))\cal{E}(\rho)=\cal{E_2}(\cal{E_1}(\rho)) como error = error1.compose(error2)
  • Produto tensorial: E(ρ)=(E1E2)(ρ)\cal{E}(\rho) =(\cal{E_1}\otimes\cal{E_2})(\rho) como error = error1.tensor(error2)
  • Expandir produto: E(ρ)=(E2E1)(ρ)\cal{E}(\rho) =(\cal{E_2}\otimes\cal{E_1})(\rho) como error = error1.expand(error2)

Exemplo

Para construir um erro de inversão de bit 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)

Output:

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)

Output:

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)

Output:

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

Converter de e para operadores do tipo QuantumChannel

Também podemos converter para frente e para trás entre os objetos QuantumError no Qiskit Aer e os objetos QuantumChannel no Qiskit.

# Convert to Kraus operator
bit_flip_kraus = Kraus(bit_flip)
print(bit_flip_kraus)

Output:

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)

Output:

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

Output:

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 P(AB)P(A|B) de atribuição:

  • AA é o valor do bit clássico registrado
  • BB é o valor de bit verdadeiro retornado da medição

Por exemplo, para um 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]])

Output:

ReadoutError([[0.95 0.05]
 [0.1  0.9 ]])

Os erros de leitura também podem ser combinados usando compose, tensor e expand, como nos erros quânticos.


Adicionar 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 em que ele atua e a quais qubits ele deve ser aplicado. Há dois casos de erros quânticos:

  1. Erro quântico de todos os qubits
  2. Erro quântico de qubit específico

1. Erro quântico em todos os qubits

Isso aplica o mesmo erro a qualquer ocorrência de uma instrução, independentemente de em quais qubits ela atua.

Ele é 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)

Output:

NoiseModel:
  Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
  Instructions with noise: ['u1', 'u2', 'u3']
  All-qubits errors: ['u1', 'u2', 'u3']

2. Erro quântico específico do qubit

Isso aplica o erro a qualquer ocorrência de uma instrução que atue em uma lista especificada de qubits. Observe que a ordem do qubit é importante: por exemplo, um erro aplicado aos qubits [0, 1] para uma porta de dois qubits é diferente de um erro aplicado aos qubits [1, 0].

Ele é 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)

Output:

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

Nota sobre erros quânticos em qubits não locais

NoiseModel não suporta a adição de erros quânticos em qubits não locais. Devem ser tratados fora do NoiseModel. Isso sugere que você deve escrever sua própria etapa de transpilador (TransformationPass) e executá-la imediatamente antes de iniciar o simulador, caso precise inserir seus erros quânticos no circuito de acordo com suas próprias condições.

Execute 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 fornecido. Além de definir o modelo de ruído do simulador, ele também substitui as portas de base do simulador, de acordo com as portas do modelo de ruído.


Exemplos de modelos de ruído

A seguir, apresentaremos alguns exemplos de modelos de ruído. Para nossas demonstrações, usamos 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)

Output:

        ┌───┐                ░ ┌─┐         
   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))

Output:

Output of the previous code cell

Exemplo de ruído 1: Modelo básico de ruído de erro de inversão de bits

Vamos considerar um exemplo simples de modelo de ruído de brinquedo comum na pesquisa da teoria da informação quântica:

  • Ao aplicar uma porta de um único qubit, inverta o estado do qubit com probabilidade p_gate1.
  • Ao aplicar uma porta de dois qubits, aplique erros de um qubit a cada qubit.
  • Ao redefinir um qubit, redefina 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)

Output:

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

Execute a simulação ruidosa

# 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:

Output of the previous code cell

Exemplo 2: relaxamento térmico de 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 T1T_1 e uma constante de tempo de defasagem T2T_2.
  • Observe que devemos ter T22T1T_2 \le 2 T_1.
  • As taxas de erro nas instruções são determinadas por tempos de porta e valores de qubit T1T_1, T2T_2.
# 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)

Output:

NoiseModel:
  Basis gates: ['cx', 'id', 'rz', 'sx', 'u2', 'u3']
  Instructions with noise: ['u2', 'measure', 'cx', 'u3', '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))]

Execute a simulação ruidosa

# 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:

Output of the previous code cell

Próximas etapas

Recomendações
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.