Skip to main content
IBM Quantum Platform

Migrar do Sampler para o Executor

Este guia descreve como migrar cargas de trabalho de amostragem quântica da primitiva “ IBM Quantum® ” do Sampler para a primitiva do Executor.

Liberação beta

A primitiva Executor faz parte do modelo de execução direcionada. Todos os componentes do modelo de execução direcionada estão atualmente em fase beta e podem não estar estáveis. Você está convidado a testá-los e enviar seus comentários, abrindo uma solicitação nos repositórios do Samplomatic ou do qiskit-ibm-runtime GitHub.


Você deveria migrar?

Nem todos devem migrar do Sampler para o Executor. Existem muitas diferenças entre as primitivas, mas as orientações a seguir podem ajudá-lo a decidir se deve migrar:

Migre para o Executor se você for um cientista da informação quântica que realiza experimentos em escala de utilidade e precisa de um controle detalhado e reproduzível sobre técnicas como o “Pauli twirling”, o aprendizado e a injeção de modelos de ruído e as mudanças de base — ou se precisar de um dos recursos adicionais oferecidos pelo Executor.

Continue usando o Sampler se desejar uma interface simples e de alto nível e quiser que a primitiva cuide da supressão e mitigação de erros para você.

Limitações e ressalvas

Como o Executor e o modelo de execução direcionada estão em fase beta, observe o seguinte antes de decidir migrar:

  • Ainda não há suporte a simulador : ao contrário do Sampler, que possui uma AerSamplerimplementação qiskit-aer para simulação local, atualmente não há um backend de simulador para o Executor. Simulador A previsão é que o suporte seja disponibilizado em breve. Enquanto isso, você ainda pode examinar e testar o circuito modelo localmente para validar seu fluxo de trabalho antes de enviá-lo para a produção de hardware.
  • Este guia aborda apenas o Sampler, e não o Estimator. A migração do Estimator para o Executor é consideravelmente mais complexa do que a migração do Sampler, pois o Estimator calcula valores esperados, em vez de retornar amostras brutas. Reproduzir o comportamento do Estimator com o Executor requer um pós-processamento adicional. As funções utilitárias para auxiliar na migração do Estimator para o Executor ainda estão em desenvolvimento; portanto, este guia descreve intencionalmente apenas o fluxo de trabalho do Sampler.

Principais diferenças entre o Executor e o Sampler

Tanto o Sampler quanto o Executor fazem a amostragem dos registros de saída dos circuitos quânticos, mas se destinam a usuários diferentes:

  • O Sampler é uma abstração de alto nível. Possui as seguintes características:
    • Possui supressão de erros integrada (desacoplamento dinâmico e twirling).
    • Ele toma decisões implícitas por você.
    • Ele foi projetado para que os desenvolvedores de algoritmos possam se concentrar na inovação, em vez de nos dados. conversão.
  • O Executor faz parte do modelo de execução direcionada. Ele difere do Sampler em muitos aspectos e apresenta as seguintes características:
    • Não possui nenhum mecanismo integrado de supressão ou mitigação de erros. Em vez disso, você define sua intenção de projeto no lado do cliente (usando anotações de circuito e um samplex ), e a geração, que exige muitos recursos, de variantes de circuito é transferida para o lado do servidor.

    • Ele não toma nenhuma decisão implícita. Ele segue suas instruções à risca, proporcionando total controle e transparência.

    • O Executor e o Samplomatic, em conjunto, oferecem recursos adicionais que o Sampler não oferece, incluindo (mas não se limitando a) os seguintes:

      • Mais grupos de rotação: o Samplomatic permite que você escolha qual grupo de rotação aplicar por caixa, em vez de ficar limitado à única estratégia que o Sampler aplica automaticamente. Ele também oferece suporte a grupos de giro que não sejam o de Pauli, como o grupo de giro "local_c1" .
      • Medidas com kernel e classificadas em conjunto: A configuração QuantumProgram.meas_level = "both" (adicionada em v0.48.0qiskit-ibm-runtime ) solicita que tanto as medidas classificadas quanto as com kernel estejam presentes nos resultados, em vez de selecionar um único tipo de medida por tarefa.
      • Twirling para circuitos com portas fracionárias: o Executor pode aplicar o twirling a circuitos que contenham portas fracionárias.
      • Mitigação de erros com granularidade fina e combinável: por exemplo, escolher quais camadas do circuito devem ser mitigadas e ajustar as taxas de ruído injetadas no circuito.
      Notas
      • Espera-se que os novos recursos futuros sejam lançados primeiro no Executor e talvez não sejam portados para o Sampler. Se você depende do acesso aos recursos mais recentes, o Executor é a opção mais preparada para o futuro.
      • O pacote básico do Qiskit ainda não oferece uma classe base para a primitiva Executor (mas oferece paraSamplerV2).

Mapeamento conceitual

A tabela a seguir mostra como os conceitos do Sampler se correspondem aos do Executor.

Conceito
Amostra
Executor
Importarfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
EntradaLista de PUBs (tuplas)Um conjunto de QuantumProgram objetos QuantumProgramItem
Circuito e parâmetros(circuit, params, shots) tuplaprogram.append_circuit_item(circuit, circuit_arguments=...)
GiroTwirlingOptionsExplicado por meio de caixas anotadas e um samplex (append_samplex_item)
Executar chamadasampler.run([pub, ...])executor.run(program)
Tipo de resultadoPrimitiveResult de SamplerPubResultQuantumProgramResult (iterável)
Dados do Acessoresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Gerenciar o ruídoOpções integradasDeve ser composto manualmente (anotações, samplex, NoiseLearnerV3)

Visão geral das etapas da migração

  1. Instale o Samplomatic.
  2. Altere as importações.
  3. Substituir as tuplas de PUB.
  4. Altere a forma como as jogadas são descritas.
  5. Atualize as demais opções conforme necessário.
  6. Atualize o comando run .
  7. Atualizar a análise dos resultados.
  8. Desfazer o giro.

Etapa 1. Instale os pacotes necessários

O Executor e o modelo de execução direcionada exigem o pacote samplomatic :

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Notas da versão
  • qiskit-ibm-runtime v0.48.0 é recomendado porque adiciona a opção meas_level = "both" e o grupo de giros local_c1 .
  • qiskit >= 2.3.0 é necessário.
  • samplomatic >= 0.18.0 é necessário.

Etapa 2. Altere as importações

Amostra:

from qiskit_ibm_runtime import SamplerV2 as Sampler

Executor:

from qiskit_ibm_runtime import Executor, QuantumProgram

Etapa 3. Substitua as tuplas PUB por um QuantumProgram

Em vez de passar uma lista de tuplas (PUBs), ao usar o Executor, você cria uma e QuantumProgram acrescenta itens a ela.

A QuantumProgram aceita itens de circuito e itens Samplex :

  • append_circuit_item: Acrescenta um CircuitItem, que é um circuito e (opcionalmente) seus valores de parâmetros. É executado tal como está, sem qualquer aleatorização.

    Use isso quando quiser apenas fazer uma amostragem de um circuito, exatamente como o Sampler faria com um “ PUB ” sem twirling; por exemplo, ao enviar um trabalho de amostragem simples ou quando você já tiver incluído manualmente quaisquer variantes desejadas.

  • append_samplex_item: Acrescenta um samplexItem, que é um circuito modelo mais um samplex que gera conjuntos de parâmetros aleatórios no lado do servidor.

    Use isso quando quiser que o conteúdo do circuito seja aleatorizado. O caso principal envolve giros (de porta ou de medição) ou injeção de ruído. Esse recurso substitui a função de rotação integrada do Sampler.

Um único QuantumProgram pode aceitar ambos os tipos de item; cada item anexado é executado como uma tarefa independente e gera sua própria entrada nos resultados. Em geral, use append_circuit_item quando seu circuito não precisar ser aleatorizado. Caso contrário, use append_samplex_item.

As próximas seções mostram, uma a uma: circuitos parametrizados que utilizam append_circuit_iteme a migração do twirling por meio de append_samplex_item.

Nos exemplos de código a seguir, isa_circuit refere-se ao circuito que foi transpilado para se adequar à Arquitetura de Conjunto de Instruções (ISA) do backend de destino. Isso contém isa_circuit dois parâmetros.

Etapa 3a e. Migrar circuitos parametrizados

No Sampler, os valores dos parâmetros são o segundo elemento da tupla PUB. Com o Executor, passe-os como circuit_arguments para append_circuit_item.

Amostra:

params = np.random.rand(10, circuit.num_parameters)  # 10 parameter sets
pubs = (isa_circuit, params)

Executor

program = QuantumProgram(shots=1024)
program.append_circuit_item(
    isa_circuit,
    circuit_arguments=np.random.rand(10, circuit.num_parameters),  # 10 sets
)

# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]

Etapa 3b e. Migrar o “twirling” integrado para anotações explícitas

Essa é a mudança mais significativa. O Sampler aplica o efeito de giro para você por meio de opções. Com o Executor, você declara essa intenção explicitamente usando caixas anotadas e um samplex (do Samplomatic ).

Exemplos (girando com as opções):

sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True

Executor (giros com caixas e um samplex):

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
    enable_gates=True,     # gate twirling
    enable_measures=True,  # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)

# 2. Build the (template circuit, samplex) pair.
#    The template circuit's single-qubit gates are replaced by parameterized gates;
#    the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)

# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # original circuit params
    },
    shape=(28, 10),  # 28 randomizations x 10 parameter sets
)

Como o circuito modelo e o samplex são criados no lado do cliente, você pode inspecioná-los e testá-los localmente para verificar o resultado antes de enviar qualquer coisa para o hardware.

Verificação: Teste o circuito modelo localmente

Você pode gerar amostras aleatórias do samplex e vinculá-las ao circuito modelo para confirmar se o samplex está produzindo os valores de parâmetros esperados. Os valores dos parâmetros retornados por samplex.sample são diretamente compatíveis com os parâmetros do circuito modelo.

# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())

# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
    parameter_values=np.random.rand(2),  # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)

# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)

Para aprofundar, é possível verificar se cada randomização é logicamente equivalente ao circuito original, por exemplo, convertendo ambos em objetos Operator e comparando suas implementações unitárias (após levar em conta as outputs["measurement_flips.<register>"] correções que revertem o efeito da medição), ou comparando os valores esperados de uma execução local StatevectorSampler ou de uma StatevectorEstimator execução. Consulte o guia de entradas e saídas do Samplomatic Samplex para obter um passo a passo completo.

Etapa 4. Alterar a forma como os pedidos de bebidas são feitos

Mova as fotos do PUB para QuantumProgram(shots=...). No Executor, isso se aplica shots a todo o trabalho. Envie vários trabalhos caso precise de quantidades diferentes de fotos.

Amostra:

# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])

Executor:

# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

Etapa 5. Atualize as opções conforme necessário

O Executor tem menos opções disponíveis do que o Sampler, pois as opções de mitigação de erros agora estão nas suas anotações e no samplex, em vez de nas opções.

Há também uma diferença estrutural quanto ao local onde as configurações estão armazenadas.

  • Com o Sampler, tudo, inclusive as opções que afetam o pós-processamento dos resultados, é configurado nas opções da primitiva ou no arquivo “ PUB ”.

  • Com o Executor, as opções que afetam a forma como os resultados do trabalho são moldados e pós-processados são definidas no QuantumProgram, e não no ExecutorOptions.

Exemplos:

Amostra
Executor
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

ExecutorOptions contém apenas configurações de execução e de ambiente de nível inferior que não alteram a estrutura dos dados retornados. Possui três grupos de nível superior:

Vale destacar que as opções dynamical_decoupling e twirling existem no Sampler, mas não no Executor. Em vez disso, esses valores de opção são expressos por meio do modelo de execução direcionada.

Exemplo:

from qiskit_ibm_runtime import Executor, ExecutorOptions

options = ExecutorOptions(
    environment={"log_level": "INFO"},
    execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True

executor = Executor(mode=backend, options=options)

Etapa 6. Atualize o comando run

A entrada para uma tarefa do Executor é o programa, em vez de PUBs.

Amostra:

# Submit a job
sampler.run([(isa_circuit, parameter_values)])

Executor:

# Submit a job
executor.run(program)

Etapa 7. Altere a forma como você acessa os resultados

No Executor, os resultados são matrizes do tipo NumPy, e não BitArray objetos. Use a string de nome como índice (result[0]["meas"]) e receba um de volta np.ndarray . Não é preciso lembrar o caminho do atributo .data.<register> .

Para atualizar do Sampler para o Executor, altere result[i].data.<reg> (BitArray) para result[i]["<reg>"] (np.ndarray), e, em seguida, reescreva o pós-processamento get_countsbaseado em como operações d NumPy.

Tarefa
Amostra
Executor
Obter dados do registroresult[0].data.measresult[0]["meas"]
Tipo de dadosBitArraynp.ndarray
Dicionário de contagensresult[0].data.meas.get_counts()Fazer o pós-processamento da matriz manualmente
Vários registrosresult[0].data.<name> por registroresult[0]["<name>"] por registro
CircuitItem forma da matriz-(parameter_sets, shots, register_bits)
SamplexItem forma da matriz-(randomizations, parameter_sets, shots, register_bits)
Desfazer a rotação da mediçãoAutomáticoresult[i]["measurement_flips.<name>"] + XOR
Note

O Sampler's BitArray oferece auxiliares (get_counts, slice_bits, slice_shots, expectation_values, e máscaras pós-seleção). O Executor retorna matrizes “ NumPy ” em formato bruto, para que você possa realizar esse pós-processamento com operações padrão de “ NumPy ”.

Etapa 8. Tratar resultados de rotação (correções de inversão de bits)

Quando você aplica a rotação de medição por meio de um SamplexItem, o Executor retorna as medições brutas (rotacionadas) mais as correções de inversão de bits necessárias para reverter a rotação. É preciso aplicá-las manualmente; nada é corrigido automaticamente.

Ao usar o Executor, desfaça o twirling explicitamente utilizando as correções measurement_flips.<reg> e uma operação XOR, conforme mostrado no exemplo a seguir:

# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"]                       # example: (28, 10, 1024, 2)

# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"]      # example: (28, 10, 1, 2)

# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1

Não há uma etapa equivalente no Sampler, pois ele desativa automaticamente o efeito de giro para você.


Exemplo completo: Migrar um trabalho básico de amostragem

Amostra

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()

# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()

Executor

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()

# 6. Access results: a plain np.ndarray keyed by register name
#    shape = (shots, register_bits)
meas = result[0]["meas"]

Próximas etapas

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