Skip to main content
IBM Quantum Platform

Transmissão do executor

Os dados fornecidos à primitiva Executor podem ser organizados em diversos formatos para oferecer flexibilidade a uma carga de trabalho por meio da difusão. Este guia explica como o Executor lida com entradas e saídas de matrizes utilizando a semântica de difusão. Compreender esses conceitos ajudará você a percorrer com eficiência os valores dos parâmetros, combinar várias configurações e interpretar a forma dos dados retornados.

Note

Os exemplos deste tópico não podem ser executados isoladamente. Eles presumem que você já definiu os circuitos adequados, utilizou o gerenciador de passagens do Samplomatic para adicionar caixas e anotações e utilizou o método do Samplomatic build para obter um circuito modelo e um samplex para cada bloco de código, conforme necessário.


Exemplo de introdução rápida

Este exemplo ilustra a ideia central. Ele cria um circuito paramétrico e cinco configurações de parâmetros diferentes. O executor executa todas as cinco configurações e retorna os dados organizados por configuração, com um resultado por registro clássico em cada item do programa quântico.

O restante deste guia se baseia neste exemplo para explicar como isso funciona e como criar varreduras mais complexas, incluindo randomização e entradas baseadas no Samplomatic.

import numpy as np
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

# A circuit with 2 parameters
# This circuit is used throughout the rest of this guide.
circuit = QuantumCircuit(4)
circuit.rx(Parameter("a"), 0)
circuit.rx(Parameter("b"), 1)
circuit.h(2)
circuit.cx(2, 3)
circuit.measure_all()

# 5 different parameter configurations (shape: 5 configurations × 2 parameters)
parameter_values = np.linspace(0, np.pi, 10).reshape(5, 2)

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

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

# This program is used throughout the rest of this guide.
program = QuantumProgram(shots=1024)
program.append_circuit_item(isa_circuit, circuit_arguments=parameter_values)

# initialize an Executor with default options
executor = Executor(mode=backend)

# Run and get results
result = executor.run(program).result()

# result is a list with one entry per program item
# result[0] is a dict mapping classical register names to data arrays
# Output bool arrays have shape (5, 1024, 4)
#   5 = number of parameter configurations
#   1024 = number of shots
#   4 = bits in the classical register
result[0]["meas"]

Eixos intrínsecos e extrínsecos

A transmissão se aplica apenas aos eixos extrínsecos. Os eixos intrínsecos são sempre preservados conforme especificado.

  • Eixos intrínsecos (à direita): determinados pelo tipo de dados. Por exemplo, se o seu circuito tiver três parâmetros, os valores desses parâmetros exigirão três números, resultando em uma forma intrínseca de (3,).

  • Eixos extrínsecos (mais à esquerda): suas dimensões de varredura. Isso define quantas configurações você deseja executar.

Tipo de entrada
Forma intrínseca
Exemplo de forma completa
Valores dos parâmetros (n parâmetros)(n,)(5, 3) para cinco configurações e três parâmetros
Entradas escalares (por exemplo, escala de ruído)()(4,) para quatro configurações
Observáveis (se aplicável)variaDepende do tipo do observável

Exemplo

Considere um circuito com dois parâmetros que você deseja varrer por uma grade de configurações de tipo “ 4x3 ”, variando os valores dos parâmetros e um fator de escala de ruído:

import numpy as np

# Parameter values: 4 configurations along axis 0, intrinsic shape (2,)
# Full shape: (4, 1, 2) - the "1" allows broadcasting with noise_scale
parameter_values = np.array([
    [[0.1, 0.2]],
    [[0.3, 0.4]],
    [[0.5, 0.6]],
    [[0.7, 0.8]],
])  # shape (4, 1, 2)

# Noise scale: 3 configurations, intrinsic shape () (scalar)
# Full shape: (3,)
noise_scale = np.array([0.8, 1.0, 1.2])  # shape (3,)

# Extrinsic shapes: (4, 1) and (3,) → broadcast to (4, 3)
# Result: 12 total configurations in a 4×3 grid
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": parameter_values,
        "noise_scales.mod_ref1": noise_scale,
    },
)

As formas são as seguintes:

Entrada
Em plena forma
Forma extrínseca
Forma intrínseca
parameter_values(4, 1, 2)(4, 1)(2,)
noise_scale(3,)(3,)()
TransmissãoNenhum(4, 3)Nenhum

Formatos das matrizes de saída

As matrizes de saída seguem o mesmo padrão extrínseco/intrínseco:

  • Forma extrínseca: corresponde à forma de transmissão de todas as entradas
  • Formato intrínseco: determinado pelo tipo de saída

O resultado mais comum são dados em forma de sequência de bits provenientes das medições, que são formatados como uma matriz de valores booleanos:

Tipo de saída
Forma intrínseca
Descrição
Dados do registro clássico(número_de_tiros, tamanho_do_círculo)Dados em cadeia de bits provenientes de medições

Exemplo

Se você fornecer entradas com formas (4, 1) extrínsecas e (3,), a forma extrínseca de difusão é (4, 3). O código a seguir utiliza um circuito com 1024 disparos e um registro clássico de 4 bits (conforme definido no exemplo do Guia Rápido ):

# Input extrinsic shapes: (4, 1) and (3,) → (4, 3)
# Output for classical register "meas":
#   extrinsic: (4, 3)
#   intrinsic: (1024, 4)  - shots × bits
#   full shape: (4, 3, 1024, 4)

result = executor.run(program).result()
meas_data = result[0]["meas"]  # result[0] for first program item
print(meas_data.shape)  # (4, 3, 1024, 4)

# Access a specific configuration
config_2_1 = meas_data[2, 1, :, :]  # shape (1024, 4)
Note

Cada configuração executa o número total de disparos especificado no programa quântico. As doses não são distribuídas entre as configurações. Por exemplo, se você solicitar 1.024 testes e tiver 10 configurações, cada configuração executará 1.024 testes (total de 10.240 testes executados).


Randomização e o shape parâmetro

Ao utilizar um samplex, cada elemento da forma extrínseca corresponde a uma execução de circuito independente. O samplex normalmente introduz aleatoriedade (por exemplo, rotação de portas) em cada execução; assim, mesmo sem solicitar explicitamente múltiplas aleatorizações, cada elemento recebe uma realização aleatória.

Você pode usar o shape parâmetro para ampliar a forma extrínseca do item, efetivamente adicionando eixos que correspondem especificamente à repetição aleatória da mesma configuração várias vezes. Deve ser transmitível a partir do formato implícito no seu samplex_arguments. Os eixos em que shape excede a forma implícita enumeram aleatorizações independentes adicionais.

Não há eixos de randomização explícitos

Se você omitir shape (ou definir de modo a corresponder às suas formas de entrada), terá uma execução por configuração de entrada. Cada execução ainda é aleatorizada pelo samplex, mas com apenas uma única realização aleatória, você não se beneficia da média obtida a partir de várias aleatorizações.

Note

Se você está acostumado a ativar o twirling com um simples sinalizador como twirling=True, observe que o Executor exige que você solicite explicitamente múltiplas randomizações com o shape argumento para permitir que suas rotinas de pós-processamento obtenham os benefícios da média calculada sobre múltiplas randomizações. Uma única randomização (a configuração padrão quando shape é omitida) aplica portas aleatórias, mas normalmente não oferece nenhuma vantagem em relação à execução do circuito básico sem randomização.

O exemplo a seguir ilustra o comportamento padrão:

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    # shape defaults to (10,) - one randomized execution per config
)
# Output shape for "meas": (10, num_shots, creg_size)

Eixo único de randomização

Para executar várias randomizações por configuração, amplie a figura com eixos adicionais. Por exemplo, o código a seguir executa 20 randomizações para cada uma das 10 configurações de parâmetros:

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    shape=(20, 10),  # 20 randomizations × 10 configurations
)
# Output shape for "meas": (20, 10, num_shots, creg_size)

Vários eixos de randomização

Você pode organizar as randomizações em uma grade multidimensional. Isso é útil para análises estruturadas, por exemplo, para separar as randomizações por tipo ou agrupá-las para processamento estatístico.

(2, 14, 10)Aqui, a forma (10,) extrínseca de entrada é transmitida para a forma solicitada, com os eixos 0 e 1 preenchidos por aleatorizações independentes.

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    # 2×14=28 randomizations per configuration, 10 configurations
    # Or you could set shape=(28, 10) for the same effect
    shape=(2, 14, 10),  
)
# Output shape for "meas": (2, 14, 10, num_shots, creg_size)

Como shape as formas de entrada interagem

O shape parâmetro deve poder ser transmitido a partir das suas formas extrínsecas de entrada. Ou seja:

  • As formas inseridas com dimensões d size-1 e podem se expandir para se adequarem a shape.
  • As formas inseridas devem ser alinhadas à direita com shape.
  • Os shape eixos que excedem as dimensões de entrada enumeram as randomizações.

Observe que shape pode conter dimensões do tipo “ size-1 ” que se expandem para se adequarem às dimensões de entrada, conforme ilustrado na última linha da tabela a seguir.

Exemplos:

Entrada extrínseca
Forma
Resultado
(10,)(10,)10 configurações, com 1 randomização para cada uma
(10,)(5, 10)10 configurações, com 5 randomizações cada
(10,)(2, 3, 10)10 configurações, 2×3=6 randomizações cada
(4, 1)(4, 5)4 configurações, com 5 randomizações cada
(4, 3)(2, 4, 3)4×3=12 configurações, com 2 randomizações cada
(4, 3)(2, 1, 3)4×3=12 configurações, com 2 randomizações cada (o 1 se expande para 4)

Índice dos resultados

Com os eixos de aleatorização, é possível acessar combinações específicas de aleatorização/parâmetros:

# Using shape=(2, 14, 10) with input extrinsic shape (10,), and 
# 1024 shots and 4 classical registers.
result = executor.run(program).result()
meas_data = result[0]["meas"]  # shape (2, 14, 10, 1024, 4)

# Get all shots for randomization (0, 7) and parameter config 3
specific = meas_data[0, 7, 3, :, :]  # shape (1024, 4)

# Average over all randomizations for parameter config 5 on bit 2
averaged = meas_data[:, :, 5, :, 2].mean(axis=(0, 1))

Padrões comuns

Variação de um único parâmetro

Use um código como o seguinte para variar um parâmetro enquanto mantém os demais fixos:

# Circuit has 2 parameters, sweep first one over 20 values
sweep_values = np.linspace(0, 2*np.pi, 20)

parameter_values = np.column_stack([
    sweep_values,
    np.full(20, 0.5),
])  # shape (20, 2)

Criação de uma varredura de grade do tipo “ 2D ”

Para criar uma grade com base em três parâmetros:

# Sweep param 0 over 10 values, param 1 over 8 values, param 2 fixed
p0 = np.linspace(0, np.pi, 10)[:, np.newaxis, np.newaxis]  # (10, 1, 1)
p1 = np.linspace(0, np.pi, 8)[np.newaxis, :, np.newaxis]   # (1, 8, 1)
p2 = np.array([[[0.5]]])                                   # (1, 1, 1)

parameter_values = np.broadcast_arrays(p0, p1, p2)
parameter_values = np.stack(parameter_values, axis=-1).squeeze()  # (10, 8, 3)

# Extrinsic shape: (10, 8), intrinsic shape: (3,)

Combinação de várias entradas

Ao combinar entradas com formas intrínsecas diferentes, alinhe as dimensões extrínsecas utilizando os eixos d size-1 :

# 4 parameter configurations, 3 noise scales → 4×3 = 12 total configurations
parameter_values = np.random.rand(4, 1, 2)  # extrinsic (4, 1), intrinsic (2,)
noise_scale = np.array([0.8, 1.0, 1.2])     # extrinsic (3,), intrinsic ()

# Broadcasted extrinsic shape: (4, 3)

Próximas etapas

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