Exemplos de executores
O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos o uso dessas versões ou versões mais recentes.
qiskit[all]~=2.4.0 qiskit-ibm-runtime~=0.46.1 samplomatic~=0.18.0
Os exemplos desta seção ilustram algumas formas comuns de utilizar a primitiva Executor. Antes de executar estes exemplos, siga as instruções do guia rápido “Instalar o Qiskit e o Executor”.
Antes de iniciar
Alguns dos exemplos de código nesta página utilizam samplex, que faz parte do pacote Samplomatic. Portanto, antes de executar esses blocos de código, é necessário instalar o Samplomatic, conforme mostrado no bloco de código a seguir. Para mais informações, consulte a documentação do Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]Exemplo: Circuito parametrizado
Este exemplo mostra como adicionar itens de circuito com parâmetros, bem como itens Samplex. Consiste nas seguintes etapas:
- Configurar o circuito: Gerar e compilar o circuito de destino.
- Prepare um samplex: agrupe portas e medições em caixas anotadas e gere o modelo de circuito e o par de samplex.
- Executar: Adicione um item de circuito e um item Samplex a um
QuantumPrograme execute ambos em um único trabalho.
Monte o circuito
Prepare um estado GHZ de três qubits, gire os qubits em torno do eixo de Pauli-Z e meça os qubits na base computacional.
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()Especifique o backend e compile o circuito para que ele utilize apenas instruções compatíveis com a QPU (conhecido como circuito de arquitetura de conjunto de instruções, ou ISA).
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)Prepare o samplex
Use a generate_boxing_pass_manager função de conveniência e seus parâmetros de rotação para agrupar portas de dois qubits e medições em caixas e aplicar anotações de rotação.
boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = boxing_pm.run(isa_circuit)Use o build método para gerar o circuito modelo e o samplex.
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)Execute os circuitos
O Executor executa QuantumProgram objetos. Cada um QuantumProgram pode conter vários itens. Este exemplo adiciona um item de circuito e um item samplex para execução. Para obter mais detalhes, consulte Entrada e saída do Executor.
O primeiro passo é inicializar um programa vazio, solicitando 1024 tomas para cada configuração de cada item.
# Generate a quantum program
program = QuantumProgram(shots=1024)Adicione o item do circuito ao QuantumProgram. Este item de circuito é composto por duas partes: o circuito ISA e 10 conjuntos de valores de parâmetros.
# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)Adicione o item samplex ao QuantumProgram com estes argumentos:
- O circuito modelo e o samplex gerado pela
buildfunção - Dez conjuntos de valores de parâmetros para o circuito original
- O número de randomizações a serem realizadas
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)Executar a tarefa do Executor
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()Recupere o resultado de cada tarefa.
# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]
# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]Exemplo: Executar o PEC
Este exemplo mostra como usar um item Samplex para realizar o cancelamento probabilístico de erros ( PEC ) com o objetivo de mitigar erros.
Considere uma versão espelhada de um circuito com dez qubits e duas camadas distintas de portas CX. Estas são as principais tarefas:
- Execute o circuito com giros.
- Execute o circuito com mitigação PEC, conforme descrito no artigo “Cancelamento probabilístico de erros com modelos Pauli-Lindblad esparsos em processadores quânticos sujeitos a ruído ”.
O fluxo de trabalho consiste nas seguintes etapas:
- Configuração: Gere o circuito de destino e agrupe suas operações em caixas.
- Aprenda: Identifique o ruído das instruções que queremos atenuar com o PEC.
- Executar: Executar o circuito em um backend.
- Analisar: Realizar o pós-processamento e analisar os resultados.
Para fins de comparação, vamos executar esse circuito espelhado duas vezes. Uma vez apenas com o efeito de Pauli aplicado e outra com a mitigação PEC aplicada.
A duração deste exemplo é de aproximadamente 10 minutos em um processador Heron r2.
Monte o circuito
Escolha um backend e prepare um circuito de 10 qubits.
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Prepare a circuit
num_qubits = 10
num_layers = 10
qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)
for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])
circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)
circuit.draw("mpl", scale=0.35, fold=100)Output:
Combine o circuito com seu inverso para criar um circuito espelho.
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)Output:
Defina alguns valores de parâmetros:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)Use o gerenciador de passagens para compilar o circuito, transformando-o em um circuito ISA.
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)Em seguida, agrupe os portões e as medições em caixas anotadas. Você pode fazer isso manualmente ou usar a generate_boxing_pass_manager função do Samplomatic para facilitar. O primeiro circuito terá apenas o efeito giratório aplicado e, portanto, precisa apenas da Twirl anotação. A segunda volta será realizada com mitigação PEC total e requer as Twirl anotações e InjectNoise .
# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
mirror_circuit_twirl = boxing_pm.run(isa_circuit)
# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
mirror_circuit_pec = boxing_pm.run(isa_circuit)Aprenda a identificar o ruído
Para minimizar o número de experimentos de aprendizado com ruído, identifique as instruções únicas no segundo circuito (aquele com caixas marcadas com InjectNoise). Ao definir a exclusividade, duas instruções de caixa são consideradas iguais se ambas as condições a seguir forem verdadeiras:
- O conteúdo é idêntico, até as portas de um único qubit.
- A anotação deles
Twirlé igual (todas as outras anotações são desconsideradas).
Isso resulta em três instruções distintas: as caixas de portas ímpares e pares e a caixa de medição final.
from samplomatic.utils import find_unique_box_instructions
unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3Inicialize um NoiseLearnerV3, selecione os parâmetros de aprendizado definindo suas opções e execute um trabalho de aprendizado de ruído.
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]
learner_job = learner.run(unique_box_instructions)
learner_job.job_id()
learner_result = learner_job.result()Converta result para o objeto exigido pelo samplex utilizando o result.to_dict método.
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)Execute os circuitos
Executor executa QuantumProgram objetos. Cada um deles QuantumProgram pode conter vários itens, que são anexados ao programa. Cada item é uma tarefa a ser executada pelo programa.
Inicie um programa vazio, solicitando 1000 imagens para cada configuração de cada item.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)Em seguida, crie o circuito modelo e o samplex para mirror_circuit_twirl e acrescente-os ao programa. Solicite 900 também randomizações do Samplex. Isso significa que o samplex irá gerar 900 conjuntos de parâmetros, e cada conjunto será executado 1000 várias vezes (o número de iterações) na QPU.
Esta é a primeira tarefa do programa (resultado 0).
template_twirl, samplex_twirl = build(mirror_circuit_twirl)
program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)Da mesma forma, acrescente o circuito modelo e o samplex criados para mirror_circuit_pec, solicitando 900 randomizações. Esta é a segunda tarefa do programa (resultado 1).
template_pec, samplex_pec = build(mirror_circuit_pec)
program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)Importar Executor e enviar um trabalho.
from qiskit_ibm_runtime.executor import Executor
executor = Executor(backend)
executor_job = executor.run(program)
executor_job.job_id()
executor_results = executor_job.result()
executor_results
twirl_result = executor_results[0]
print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")
pec_result = executor_results[1]
print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")Output:
Twirl result keys:
['meas', 'measurement_flips.meas']
Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']
Shape of results: (900, 1000, 10)
Analisar os resultados
Por fim, processe os resultados para estimar os valores esperados dos operadores de Pauli-Z de um único qubit que atuam sobre cada um dos dez qubits ativos (valor esperado: 1.0).
# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)
# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")Output:
Qubit 0 -> 0.77
Qubit 1 -> 0.76
Qubit 2 -> 0.66
Qubit 3 -> 0.71
Qubit 4 -> 0.69
Qubit 5 -> 0.67
Qubit 6 -> 0.62
Qubit 7 -> 0.59
Qubit 8 -> 0.62
Qubit 9 -> 0.68
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)
# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))
# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")Output:
Qubit 0 -> 0.98
Qubit 1 -> 0.99
Qubit 2 -> 0.96
Qubit 3 -> 0.98
Qubit 4 -> 0.98
Qubit 5 -> 0.98
Qubit 6 -> 0.98
Qubit 7 -> 0.95
Qubit 8 -> 0.95
Qubit 9 -> 0.94
Próximas etapas
- Consulte a visão geral da transmissão.
- Saiba como usar as opções do Executor.
- Compreender o modelo de execução direcionada.
- Consulte a documentação do Samplomatic.
- Saiba como combinar diferentes técnicas de mitigação de erros ao usar o modelo de execução direcionada no tutorial “Cancelamento probabilístico de erros com cones de luz sombreados ”.