Przejdź do głównej treści
Ten projekt jest w wersji beta. Znalazłeś błąd? Otwórz zgłoszenie na GitHub. Masz pytanie lub pomysł? Zapytaj w Discussions — chętnie poznamy Twoją opinię!
Jak oceniasz jakość tłumaczenia tej strony?

Dane wejściowe i wyjściowe prymitywu Executor

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu następujących wymagań. Zalecamy używanie tych lub nowszych wersji.

qiskit[all]~=2.6.1
qiskit-ibm-runtime~=0.50.0
samplomatic~=0.21.0

Prymityw Executor jest częścią modelu ukierunkowanego wykonania, który zapewnia większą elastyczność przy dostosowywaniu przepływu pracy łagodzenia błędów.

Dane wejściowe i wyjściowe prymitywu Executor znacznie różnią się od tych z prymitywów Sampler i Estimator. Na przykład zamiast przyjmowania listy PUB-ów jako danych wejściowych, Executor przyjmuje QuantumProgram, który zawiera listę obiektów QuantumProgramItem. Te klasy kontenerowe dają większą elastyczność niż PUB, który jest prostą strukturą danych w postaci krotki.

Wyjście Executor to QuantumProgramResult, który jest iterowalny i zawiera jeden element dla każdego wejściowego QuantumProgramItem.

Dane wejściowe: Programy kwantowe​

Jak wspomniano wcześniej, dane wejściowe do prymitywu Executor to QuantumProgram, który jest iterowalnym zbiorem obiektów QuantumProgramItem. Obiekty te mogą być dwojakiego rodzaju:

  • CircuitItem, który zazwyczaj przechowuje obwód i jego wartości parametrów (jeśli są).
  • SamplexItem, który zazwyczaj przechowuje:
    • Obwód szablonowy
    • Obiekt samplex, używany do generowania losowych zestawów parametrów w czasie wykonania (na przykład do wykonywania twirlingów lub wstrzykiwania szumu)
    • Argumenty dla samplex, które mogą zawierać wartości parametrów dla oryginalnego obwodu

Każdy z tych elementów reprezentuje inne zadanie do wykonania przez Executor.

Przed rozpoczęciem​

Niektóre przykłady kodu na tej stronie używają samplex, który jest częścią pakietu Samplomatic. Dlatego przed uruchomieniem tych bloków kodu musisz zainstalować Samplomatic, jak pokazano w poniższym bloku kodu. Aby uzyskać więcej informacji, zobacz dokumentację Samplomatic.

pip install samplomatic

# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]

Przykład: Tworzenie QuantumProgram z dwoma różnymi zadaniami​

Najpierw zainicjuj swój program kwantowy, a następnie dodaj do niego elementy programu, używając append_circuit_item lub append_samplex_item (jeśli jest obecny samplex), jak pokazano w poniższych przykładach.

Poniższa komórka inicjuje QuantumProgram i określa, że powinien wykonać 1024 pomiary dla każdej konfiguracji każdego elementu w programie.

uwaga

W przeciwieństwie do Sampler, QuantumProgram przyjmuje tylko jedną wartość liczby pomiarów. Jeśli chcesz inną wartość liczby pomiarów, potrzebujesz osobnego QuantumProgram, co oznacza osobne zadanie.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "samplomatic": "samplomatic"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit_ibm_runtime import Executor, QiskitRuntimeService
from qiskit.circuit import Parameter, QuantumCircuit
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# Initialize an empty program
program = QuantumProgram(shots=1024)

# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)

# `measure_all` adds a 3-bit classical register named "meas"
circuit.measure_all()

# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an
# equivalent Instruction Set Architecture (ISA) circuit.
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)

# Transpile the circuit
isa_circuit = preset_pass_manager.run(circuit)

Dołączanie CircuitItem​

Następnie dołącz docelowy obwód, który został transpilowany zgodnie z architekturą zestawu instrukcji (ISA) backendu, do QuantumProgram. Ponieważ ten obwód ma dwa parametry, musimy również podać wartości parametrów (10 zestawów w tym przykładzie). Uruchomienie tego CircuitItem jest pierwszym zadaniem, które program wykona.

# Append the transpiled circuit and an array
# containing 10 sets of parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(
10, 2
), # 10 sets of parameter values and 2 parameters
)

Dołączanie SamplexItem​

Elementy obwodów są wykonywane bez żadnej randomizacji. Natomiast elementy samplex pozwalają określić, jak randomizować ich zawartość. Następna komórka używa funkcji generate_boxing_pass_manager() do grupowania bramek i pomiarów obwodu w bloki oraz dodawania adnotacji twirlingowej do każdego bloku. Następnie generuje parę obwodu szablonowego i samplex za pomocą funkcji build().

Uruchomienie tego SamplexItem jest drugim zadaniem, które program wykona.

Zobacz dokumentację API Samplomatic, aby uzyskać pełne szczegóły dotyczące samplex i jego argumentów. Zobacz Przewodnik po Transpilerze Samplomatic, aby uzyskać informacje o używaniu funkcji generate_boxing_pass_manager().

# Transpile the circuit, additionally grouping gates and measurements into annotated boxes
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)

# Use the boxing pass manager to group gates
# and measurements into boxes and add
# a`Twirl` annotation.
preset_pass_manager.post_scheduling = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = preset_pass_manager.run(circuit)

# Build the template circuit and the samplex. The template circuit has parametric gates
# without fixed values and the samplex randomly generates the parameter
# values on the server side at runtime to perform twirling.
template_circuit, samplex = build(boxed_circuit)

# Determine what arguments are required by the samplex.
# Input the arguments in samplex_arguments.
print(samplex.inputs())
TensorInterface(<
- 'parameter_values' <float64[2]>: Input parameter values to use during sampling.
>)
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
# the arguments required by the samplex.sample method
"parameter_values": np.random.rand(10, 2),
},
shape=(28, 10), # 28 randomizations and 10 sets of parameter values
)
# Initialize an Executor with the default options
executor = Executor(mode=backend)

# Submit the job
job = executor.run(program)

# Retrieve the result
result = job.result()

Dane wyjściowe​

Wyjście Executor to QuantumProgramResult, który jest iterowalny. Zawiera jeden wpis na wejściowy QuantumProgramItem w tej samej kolejności co elementy wejściowe. Każdy z tych elementów wyjściowych jest słownikiem, gdzie klucze to ciągi znaków odpowiadające nazwom klasycznych rejestrów w obwodach wejściowych (między innymi), więc nie musisz już zapamiętywać tych nazw tak jak w przypadku wyjść Sampler. Wartości słownika są typu np.ndarray.

Wynik dla poprzedniego przykładu zawiera następujące elementy:

Wynik CircuitItem​

Pierwszy element zawiera wyniki uruchomienia pierwszego zadania (CircuitItem) w programie. Zawiera pojedynczy klucz, meas, który jest nazwą rejestru klasycznego w obwodzie wejściowym. Wartością tego klucza jest np.ndarray o kształcie (parameter sets, shots, register bits), co w powyższym przykładzie wynosi (10, 1024, 3).

Poniższy kod ilustruje, jak uzyskać dostęp do tych informacji:

# Access the results of the classical register of task #0, a CircuitItem
result_0 = result[0]["meas"]
print(f"Result shape: {result_0.shape}")
Result shape: (10, 1024, 3)

Skrajnie prawa oś result_0 przebiega po bitach klasycznych w kolejności little-endian. Zatem indeks 0 zawiera wynik bitu klasycznego 0, czyli bitu najmniej znaczącego.

Wynik SamplexItem​

Drugi element zawiera wyniki uruchomienia drugiego zadania (SamplexItem) w programie. Ten element zawiera wiele kluczy. Klucz meas, który jest nazwą rejestru klasycznego obwodu wejściowego, wskazuje na tablicę wyników tego rejestru. Ta tablica ma kształt (randomizations, parameter sets, shots, classical bits), czyli (28, 10, 1024, 3) w tym przykładzie.

Dodatkowo wynik zawiera klucz measurement_flips.meas, który zawiera korekty odwrócenia bitów cofające twirling pomiaru dla rejestru meas. Ten kształt wyniku będzie wynosił (28, 10, 1, 3) w naszym przykładzie, ponieważ do wykonania odwrócenia bitu wystarczy tylko jeden pomiar.

Zwróć uwagę, że skrajnie prawa oś result_1 przebiega po bitach klasycznych w kolejności little-endian.

# Access the results of the classical register of task #1
result_1 = result[1]["meas"]
print(f"Result shape: {result_1.shape}")

# Access the bit-flip corrections
flips_1 = result[1]["measurement_flips.meas"]
print(f"Bit-flip corrections shape: {flips_1.shape}")

# Undo the bit flips via classical XOR
unflipped_result_1 = result_1 ^ flips_1
Result shape: (28, 10, 1024, 3)
Bit-flip corrections shape: (28, 10, 1, 3)

Wyświetl zakresy wykonania​

Pojedyncze wywołanie Executor.run() przesyła pojedyncze zadanie. Po stronie serwera usługa nie musi jednak uruchamiać wszystkich konfiguracji programu kwantowego jednocześnie. Zamiast tego grupuje związane obwody elementów programu we fragmenty (chunks) i uruchamia na QPU jeden fragment naraz. Nie musisz dzielić programu ani przesyłać dodatkowych zadań; podział na fragmenty odbywa się automatycznie w ramach jednego zadania. Atrybut metadata wyniku zawiera informacje o czasie tych fragmentów w result.metadata.chunk_timing. Te znaczniki czasu to rzeczywiste czasy rozpoczęcia i zakończenia każdego fragmentu na QPU. Możesz użyć tych informacji, aby zobaczyć, kiedy konkretne dane zostały zebrane na QPU, zrozumieć, jak Executor rozdzielił pracę, lub diagnozować problemy z wydajnością.

chunk_timing to lista zawierająca jeden ChunkSpan na każdy wykonany fragment. Każdy ChunkSpan ma następujące atrybuty:

  • start i stop: Znaczniki czasu UTC rozpoczęcia i zakończenia wykonywania fragmentu na QPU.

  • parts: Lista obiektów ChunkPart, które opisują, które elementy programu wniosły wkład do fragmentu. Każdy ChunkPart zawiera następujące elementy:

    • idx_item: Indeks elementu programu, w kolejności, w jakiej elementy zostały dołączone do QuantumProgram.

    • size: Liczba konfiguracji (elementów kształtu elementu) z tego elementu, które zostały wykonane w tym fragmencie. Na przykład SamplexItem w poprzednim przykładzie ma kształt (28, 10), więc ma łącznie 280 konfiguracji.

Poniższy kod wypisuje czas trwania każdego fragmentu i zawartość każdego fragmentu dla zadania przesłanego wcześniej:

for idx, span in enumerate(result.metadata.chunk_timing):
duration = (span.stop - span.start).total_seconds()
print(f"Chunk {idx}: {span.start} to {span.stop} ({duration:.3f} s)")
for part in span.parts:
print(f" Item {part.idx_item}: {part.size} configurations")
Chunk 0: 2026-11-06 01:47:46.009718+00:00 to 2026-11-06 01:49:05.577720+00:00 (79.568 s)
Item 0: 10 configurations
Item 1: 280 configurations

Właściwość QuantumProgramResult.timing opakowuje tę samą listę w obiekt ChunkTiming, który raportuje także globalne informacje o zbiorze fragmentów:

timing = result.timing

print("Number of chunks:", len(timing))
print(" Start of the first chunk:", timing.start)
print(" End of the last chunk:", timing.stop)
print(" Total duration (s):", timing.duration)
Number of chunks: 1
Start of the first chunk: 2026-11-06 01:47:46.009718+00:00
End of the last chunk: 2026-11-06 01:49:05.577720+00:00
Total duration (s): 79.568002

Wizualizuj zakresy wykonania​

Użyj funkcji draw_chunk_timings, aby zwizualizować czasy fragmentów na interaktywnym wykresie słupkowym. Oś x to czas, a każdy fragment jest rysowany jako poziomy odcinek obejmujący jego czasy rozpoczęcia i zakończenia. Oś y pokazuje skumulowaną liczbę ukończonych konfiguracji (lub procent całego obciążenia, jeśli ustawisz normalize_y=True). Najedź na odcinek, aby zobaczyć znaczniki czasu, czas trwania i części tego fragmentu.

Funkcja zwraca figurę plotly, którą możesz wyświetlić bezpośrednio, zapisać do pliku lub jedno i drugie. Przyjmuje listę chunk_timing albo obiekt ChunkTiming. Możesz przekazać kilka z nich, na przykład z różnych zadań, aby narysować je na tym samym wykresie. Ustaw common_start=True, aby wyrównać je do wspólnego czasu rozpoczęcia w celu porównania.

Wymagany plotly

draw_chunk_timings wymaga plotly. Możesz go zainstalować, uruchamiając pip install plotly.

Przerwy między fragmentami

Wizualizacja może pokazywać przerwy między wykonaniami fragmentów. Wszystkie zadania przesyłane do prawdziwego QPU przechodzą przez pewne klasyczne przetwarzanie wstępne, takie jak tłumaczenie na impulsy, które można wysłać do sprzętu. Ponieważ to przetwarzanie wstępne może odbywać się równolegle, usługa jednocześnie zdejmuje z kolejki i uruchamia wiele zadań. QPU może jednak przetwarzać tylko jedną jednostkę pracy naraz. Dlatego jeśli zadanie generuje wiele fragmentów, jego fragmenty mogą być przeplatane z fragmentami zadań innych użytkowników, a każdy fragment może musieć czekać na swoją kolej na QPU. Dlatego między fragmentami mogą pojawiać się przerwy.

from qiskit_ibm_runtime.visualization import draw_chunk_timings

fig = draw_chunk_timings(result.timing, names="Executor job")

# Alternatively, call the `draw()` method of the ChunkTiming object
# fig = result.timing.draw()

# Uncomment the following line to display the figure
# fig.show(renderer="notebook")

# Save to a file
# fig.write_html("chunk_timings.html")

Następne kroki​

Zalecenia