Skip to main content
IBM Quantum Platform

Ajudante de aprendizagem de ruídos

  • 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.4.1
    qiskit-ibm-runtime~=0.47.0
    samplomatic~=0.18.0
    

As técnicas de mitigação de erros PEA e PEC utilizam, ambas, um componente de aprendizagem de ruído baseado no modelo de ruído de Pauli-Lindblad, que é normalmente gerenciado durante a execução após o envio de um ou mais trabalhos por meio de qiskit-ibm-runtime sem qualquer acesso local ao modelo de ruído ajustado. No entanto, conforme disponível em qiskit-ibm-runtimev0.27.1, foram criadas uma NoiseLearner classe e uma classe NoiseLearnerOptions associada para obter os resultados dessas experiências de aprendizado com ruído. Esses resultados podem então ser armazenados localmente como um NoiseLearnerResult e utilizados como dados de entrada em experimentos posteriores. Esta página oferece uma visão geral sobre como utilizá-la e as opções disponíveis.

Além disso, a partir da versão qiskit-ibm-runtimev0.47.0, há uma nova NoiseLearnerV3 classe compatível com a primitiva Executor. Esta nova versão, que também faz parte do modelo de execução direcionada, permite especificar explicitamente as camadas que você deseja treinar.

Note

NoiseLearner funciona apenas com o EstimatorV2 e NoiseLearnerV3 funciona apenas com o Executor.


NoiseLearner

Visão geral

A NoiseLearner aula realiza experimentos que caracterizam processos de ruído com base no modelo de ruído de Pauli-Lindblad para um (ou mais) circuitos. Ele possui um run() método que executa os experimentos de aprendizado e recebe como entrada uma lista de circuitos ou um objeto PUB, retornando um objeto NoiseLearnerResult que contém os canais de ruído aprendidos e metadados sobre o(s) trabalho(s) enviado(s). Abaixo está um trecho de código que demonstra como usar o programa auxiliar.

from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import (
    NoiseLearnerOptions,
    ResilienceOptionsV2,
    EstimatorOptions,
)

# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]

circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
    circuit.cx(pair[0], pair[1])
for pair in odd_edges:
    circuit.cx(pair[0], pair[1])

# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_learn = pm.run(circuit)

# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend)
job = learner.run([circuit_to_learn])
noise_model = job.result()

O resultado NoiseLearnerResult.data é uma lista de LayerError objetos que contém o modelo de ruído para cada camada de entrelaçamento individual pertencente ao(s) circuito(s) alvo. Cada uma delas LayerError armazena as informações da camada, na forma de um circuito e de um conjunto de rótulos de qubits, juntamente com o PauliLindbladError modelo de ruído que foi aprendido para a camada em questão.

import numpy

print(
    f"Noise learner result contains {len(noise_model.data)} entries"
    f" and has the following type:\n {type(noise_model)}\n"
)
print(
    f"Each element of `NoiseLearnerResult` then contains"
    f" an object of type:\n {type(noise_model.data[0])}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
    print(
        f"And each of these `LayerError` objects possess"
        f" data on the generators for the error channel: \n"
        f"{noise_model.data[0].error.generators}\n"
    )
# Results are truncated
with numpy.printoptions(threshold=200):
    print(
        f"Along with the error rates: \n{noise_model.data[0].error.rates}\n"
    )

Output:

Noise learner result contains 2 entries and has the following type:
 <class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>

Each element of `NoiseLearnerResult` then contains an object of type:
 <class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>

And each of these `LayerError` objects possess data on the generators for the error channel: 
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
 'IIIIIIIIIIIIIIIIIIIIIIIIXIX', 'IIIIIIIIIIIIIIIIIIIIIIIIXIY',
 'IIIIIIIIIIIIIIIIIIIIIIIIXIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
 'IIIIIIIIIIIIIIIIIIIIIIIIYIX', 'IIIIIIIIIIIIIIIIIIIIIIIIYIY',
 'IIIIIIIIIIIIIIIIIIIIIIIIYIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
 'IIIIIIIIIIIIIIIIIIIIIIIIZIX', 'IIIIIIIIIIIIIIIIIIIIIIIIZIY',
 'IIIIIIIIIIIIIIIIIIIIIIIIZIZ', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
 'IIIIIIIIIIIIIIIIIIIIIIIYIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
 'IIIIIIIIIIIIIIIIIIIIIIXIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIII',
 'IIIIIIIIIIIIIIIIIIIIIIXYIII', 'IIIIIIIIIIIIIIIIIIIIIIXZIII',
 'IIIIIIIIIIIIIIIIIIIIIIYIIII', 'IIIIIIIIIIIIIIIIIIIIIIYXIII',
 'IIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIYZIII',
 'IIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIZXIII',
 'IIIIIIIIIIIIIIIIIIIIIIZYIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIII',
 'IIIIIIIIIIIIIIIIIIIIIXIIIII', 'IIIIIIIIIIIIIIIIIIIIIXXIIII',
 'IIIIIIIIIIIIIIIIIIIIIXYIIII', 'IIIIIIIIIIIIIIIIIIIIIXZIIII',
 'IIIIIIIIIIIIIIIIIIIIIYIIIII', 'IIIIIIIIIIIIIIIIIIIIIYXIIII',
 'IIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIYZIIII',
 'IIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIZXIIII',
 'IIIIIIIIIIIIIIIIIIIIIZYIIII', 'IIIIIIIIIIIIIIIIIIIIIZZIIII',
 'IIIIIIIIIIIIIIIIIIIIXIIIIII', 'IIIIIIIIIIIIIIIIIIIIXXIIIII',
 'IIIIIIIIIIIIIIIIIIIIXYIIIII', 'IIIIIIIIIIIIIIIIIIIIXZIIIII',
 'IIIIIIIIIIIIIIIIIIIIYIIIIII', 'IIIIIIIIIIIIIIIIIIIIYXIIIII',
 'IIIIIIIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIIIIIIYZIIIII',
 'IIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIZXIIIII',
 'IIIIIIIIIIIIIIIIIIIIZYIIIII', 'IIIIIIIIIIIIIIIIIIIIZZIIIII',
 'IIIIIIIIIIIIIIIIIIIXIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIII',
 'IIIIIIIIIIIIIIIIIIIXYIIIIII', 'IIIIIIIIIIIIIIIIIIIXZIIIIII',
 'IIIIIIIIIIIIIIIIIIIYIIIIIII', 'IIIIIIIIIIIIIIIIIIIYXIIIIII',
 'IIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIYZIIIIII', ...]

Along with the error rates: 
[5.9e-04 5.3e-04 5.7e-04 ... 0.0e+00 1.0e-05 0.0e+00]

O atributo LayerError.error do resultado do aprendizado de ruído contém os geradores e as taxas de erro do modelo Pauli Lindblad ajustado, que tem a forma

Λ(ρ)=expjrj(PjρPjρ),\Lambda(\rho) = \exp{\sum_j r_j \left(P_j \rho P_j^\dagger - \rho\right)},

onde rjr_j são os operadores LayerError.rates e PjP_j são os operadores Pauli especificados em LayerError.generators.

Opções de aprendizagem de ruído

Você pode escolher entre várias opções para inserir ao instanciar um NoiseLearner objeto. Essas opções estão encapsuladas na qiskit_ibm_runtime.options.NoiseLearnerOptions classe e incluem a possibilidade de especificar o número máximo de camadas a serem aprendidas, o número de randomizações e a estratégia de twirling, entre outras. Consulte a documentação da NoiseLearnerOptions API para obter informações detalhadas.

A seguir, apresentamos um exemplo simples que mostra como usar o NoiseLearnerOptions em um NoiseLearner experimento:

# Build a GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_run = pm.run(circuit_to_learn)

# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
    max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)

# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend, options=learner_options)
job = learner.run([circuit_to_run])
noise_model = job.result()

Modelo de ruído de entrada para uma primitiva

O modelo de ruído aprendido no circuito também pode ser usado como entrada para a primitiva IBM QuantumEstimatorV2 . Isso pode ser passado para a primitiva de várias maneiras diferentes. Os três exemplos a seguir mostram como é possível passar o modelo de ruído diretamente para o estimator.options atributo, utilizando um ResilienceOptionsV2 objeto antes de instanciar uma primitiva Estimator e passando um dicionário formatado adequadamente.

# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model
# Specify options through a ResilienceOptionsV2 object
resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)
estimator_options = EstimatorOptions(resilience=resilience_options)
estimator = EstimatorV2(mode=backend, options=estimator_options)
# Specify options by using a dictionary
options_dict = {
    "resilience_level": 2,
    "resilience": {"layer_noise_model": noise_model},
}

estimator = EstimatorV2(mode=backend, options=options_dict)

Depois que o modelo de ruído for passado para o EstimatorV2 objeto, ele poderá ser usado para executar cargas de trabalho e realizar a mitigação de erros normalmente.


NoiseLearnerV3

Visão geral

Assim como NoiseLearner, a NoiseLearnerV3 classe realiza experimentos que caracterizam processos de ruído com base em um modelo de ruído de Pauli-Lindblad para um ou mais circuitos. Seu run() método recebe uma lista de instruções, cada uma das quais deve ser uma estrutura com anotação Twirled BoxOp contendo operações ISA.

O resultado de uma NoiseLearnerV3 tarefa contém uma lista de NoiseLearnerV3Result objetos, um para cada instrução de entrada. O código a seguir mostra como usar o programa auxiliar.

from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions


# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]

circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
    circuit.cx(pair[0], pair[1])
for pair in odd_edges:
    circuit.cx(pair[0], pair[1])

# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit = pm.run(circuit)

# Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
    inject_noise_targets="gates",  # no measurement mitigation
    inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)
print(f"Found {len(unique_box_instructions)} unique layers")
print(
    f"Each instruction is of type {type(unique_box_instructions[0].operation)}"
)
print(
    f"And has annotations: {unique_box_instructions[0].operation.annotations}"
)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()

Output:

Found 3 unique layers
Each instruction is of type <class 'qiskit.circuit.controlflow.box.BoxOp'>
And has annotations: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='', site='before')]

O resultado da função é uma lista de NoiseLearnerV3Result objetos, um para cada conjunto de instruções entre colchetes. NoiseLearnerV3Result possui um to_pauli_lindblad_map() método que retorna um PauliLindbladMap objeto, o qual possui métodos para extrair os geradores, as taxas de erro e muito mais.

print(
    f"The Noise learner V3 result contains {len(learner_result)} entries"
    f" and each has the following type:\n {type(learner_result[0])}\n"
)
noise_map = learner_result[0].to_pauli_lindblad_map()
print(
    f"After converting to PauliLindbladMap, you can extract data "
    f" on the generators for the error channel "
    f"(truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
    print(
        f"Along with the error rates "
        f"(truncated to 3): \n{noise_map.rates[:3]}\n"
    )

Output:

The Noise learner V3 result contains 3 entries and each has the following type:
 <class 'qiskit_ibm_runtime.results.noise_learner_v3.NoiseLearnerV3Result'>

After converting to PauliLindbladMap, you can extract data  on the generators for the error channel (truncated to 3): 
<QubitSparsePauliList with 3 elements on 27 qubits: [X_0, Y_0, Z_0]>

Along with the error rates (truncated to 3): 
[0.00026 0.00032 0.00023]

Opções de aprendizagem de ruído

NoiseLearnerV3 oferece várias opções, incluindo o número de randomizações e a profundidade do par de camadas, entre outras. Assim como nas primitivas, você pode definir as opções durante ou após a instanciação do NoiseLearnerV3 objeto. O exemplo de código anterior mostrou como definir as opções shots_per_randomization e num_randomizations . Consulte a documentação da NoiseLearnerV3Options API para obter informações detalhadas.

Insira um modelo de ruído no Executor

O Executor segue as intenções de projeto especificadas nas anotações do circuito (na forma de um samplex) e nas opções. InjectNoise é a anotação para especificar onde injetar ruído, e o pauli_lindblad_maps argumento samplex especifica qual mapa de ruído deve ser usado.

O circuito do exemplo anterior passa pelo gerenciador de passagens de encaixotamento, que agrupa instruções em caixas anotadas. O código relevante foi incluído aqui para facilitar a compreensão.

  • inject_noise_targets=”gates” especifica que as InjectNoise anotações devem ser adicionadas às caixas que contêm elementos de entrelaçamento.
  • inject_noise_strategy="uniform_modification" especifica que se atribua o mesmo ref e modifier_ref a todas as caixas equivalentes com InjectNoise anotações.
    • InjectNoise.ref é um identificador único usado para atribuir um modelo de ruído a essa caixa.
    • InjectNoise.modifier_ref permite ajustar o modelo de ruído atribuído a uma caixa por meio de fatores multiplicativos.
boxing_pm = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
    inject_noise_targets="gates",  # no measurement mitigation
    inject_noise_strategy="uniform_modification",
)

O circuito do exemplo anterior contém três caixas, duas das quais contêm InjectNoise anotações com atributos diferentes ref (uma vez que não são equivalentes).

# box_circuit comes from the example above
for idx, instruction in enumerate(boxed_circuit):
    # The `InjectNoise` annotation defines which boxes to inject noise.
    print(f"Annotations of box #{idx}: {instruction.operation.annotations}\n")

Output:

Annotations of box #0: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='r789B', site='before')]

Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r054B', modifier_ref='r054B', site='before')]

Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]

O resultado da NoiseLearnerV3 tarefa deve ser convertido em um dicionário antes de ser passado para o Executor. As chaves deste dicionário são os InjectNoise.ref atributos, e os valores são os mapas de ruído correspondentes. Este mapeamento indica ao Executor quais modelos de ruído devem ser injetados e em que pontos.

O código a seguir mostra como pegar o circuito e o NoiseLearnerV3 resultado do exemplo anterior e passá-los para o Executor, que irá gerar as variantes do circuito com os modelos de ruído injetados e executá-las no hardware.

from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic import build

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
    instructions=unique_box_instructions, require_refs=False
)

# Append the samplex item and execute
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "pauli_lindblad_maps": noise_maps,
    },
)

executor = Executor(backend)
executor_job = executor.run(program)

Próximas etapas

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