Перейти до основного вмісту

Перехід від Sampler до Executor

Цей посібник описує, як перенести робочі навантаження квантової вибірки з примітиву IBM Quantum® Sampler на примітив Executor.

Бета-випуск

Примітив Executor є частиною моделі спрямованого виконання. Усі компоненти в моделі спрямованого виконання наразі перебувають у бета-версії і можуть бути нестабільними. Запрошуємо тебе протестувати їх і надати відгук, відкривши issue в репозиторіях GitHub Samplomatic або qiskit-ibm-runtime.

Чи варто тобі переходити?

Не всім варто переходити з Sampler на Executor. Між цими примітивами багато відмінностей, але наступні поради можуть допомогти тобі вирішити, чи варто переходити:

Переходь на Executor, якщо ти науковець з квантової інформації, який виконує експерименти промислового масштабу і потребує детального, відтворюваного контролю над такими техніками, як Pauli twirling, навчання й впровадження моделі шуму та зміни базису — або кому потрібна одна з додаткових можливостей, які надає Executor.

Продовжуй використовувати Sampler, якщо тобі потрібен простий інтерфейс високого рівня і ти хочеш, щоб примітив сам керував придушенням і пом'якшенням помилок за тебе.

Обмеження та застереження

Оскільки Executor і модель спрямованого виконання перебувають у бета-версії, зверни увагу на таке перед тим, як вирішити перейти:

  • Ще немає підтримки симулятора: На відміну від Sampler, який має реалізацію AerSampler у qiskit-aer для локальної симуляції, наразі немає бекенду симулятора для Executor. Очікується, що підтримка симулятора з'явиться найближчим часом. Тим часом ти все ще можеш перевіряти і семплувати шаблонну схему локально, щоб перевірити свій робочий процес перед відправкою на апаратне забезпечення.

  • Цей посібник охоплює лише Sampler, а не Estimator. Перехід з Estimator на Executor значно складніший, ніж перехід з Sampler, оскільки Estimator обчислює очікувані значення, а не повертає сирі семпли. Відтворення поведінки Estimator за допомогою Executor вимагає додаткової постобробки. Утилітарні функції для допомоги з переходом з Estimator на Executor все ще розробляються, тому цей посібник навмисно описує лише робочий процес Sampler.

Ключові відмінності між Executor і Sampler

Sampler і Executor обидва семплують вихідні регістри квантових схем, але вони орієнтовані на різних користувачів:

  • Sampler — це абстракція високого рівня. Вона має такі характеристики:

    • Вона має вбудоване придушення помилок (динамічне розчеплення і twirling).

    • Вона приймає неявні рішення за тебе.

    • Вона розроблена так, щоб розробники алгоритмів могли зосередитися на інноваціях, а не на перетворенні даних.

  • Executor є частиною моделі спрямованого виконання. Він відрізняється від Sampler у багатьох аспектах і має такі характеристики:

    • Він не має вбудованого придушення чи пом'якшення помилок. Натомість ти фіксуєш свій намір проєктування на стороні клієнта (за допомогою анотацій схеми та семплекса), а витратне генерування варіантів схеми переноситься на сторону сервера.

    • Він не приймає неявних рішень. Він точно слідує твоїм директивам, надаючи повний контроль і прозорість.

    • Executor і Samplomatic разом надають додаткові можливості, яких немає у Sampler, зокрема (але не обмежуючись):

      • Більше груп twirling: Samplomatic дозволяє тобі вибирати, яку групу twirling застосувати для кожного box, замість обмеження єдиною стратегією, яку Sampler застосовує за тебе. Він також підтримує групи twirling, відмінні від Pauli, наприклад групу twirling "local_c1".
      • Кернельні та класифіковані вимірювання разом: Встановлення QuantumProgram.meas_level = "both" (додано в qiskit-ibm-runtime v0.48.0) вимагає, щоб у результатах були присутні як класифіковані, так і кернельні вимірювання, замість вибору одного типу вимірювання на завдання.
      • Twirling для схем з дробовими вентилями: Executor може застосовувати twirling до схем, які містять дробові вентилі.
      • Детальне, композиційне пом'якшення помилок: Наприклад, вибір, які шари схеми пом'якшувати, і налаштування рівнів шуму, що впроваджуються в схему.
      Примітки
      • Очікується, що майбутні нові можливості будуть випущені для Executor першими і можуть не бути перенесені в Sampler. Якщо тобі важливий доступ до найновіших функцій, Executor — більш перспективний вибір.
      • Базовий пакет Qiskit ще не надає базового класу для примітиву Executor (на відміну від SamplerV2).

Концептуальне зіставлення

Наступна таблиця демонструє, як концепції Sampler зіставляються з Executor.

КонцепціяSamplerExecutor
Імпортfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
Вхідні даніСписок PUB (кортежів)QuantumProgram з об'єктів QuantumProgramItem
Схема та параметриКортеж (circuit, params, shots)program.append_circuit_item(circuit, circuit_arguments=...)
TwirlingTwirlingOptionsЯвно через анотовані box і семплекс (append_samplex_item)
Виклик запускуsampler.run([pub, ...])executor.run(program)
Тип результатуPrimitiveResult з SamplerPubResultQuantumProgramResult (ітерований)
Доступ до данихresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Керування шумомВбудовані параметриПотрібно компонувати вручну (анотації, семплекс, NoiseLearnerV3)

Огляд кроків міграції

  1. Встанови Samplomatic.

  2. Зміни імпорти.

  3. Заміни кортежі PUB.

  4. Зміни, як виражаються shots.

  5. Онови інші параметри за потреби.

  6. Онови команду run.

  7. Онови розбір результатів.

  8. Скасуй twirling.

Крок 1. Встанови необхідні пакети

Executor і модель спрямованого виконання вимагають пакет samplomatic:

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Примітки щодо версій
  • Рекомендується qiskit-ibm-runtime v0.48.0, оскільки він додає параметр meas_level = "both" і групу twirling local_c1.
  • Потрібен qiskit >= 2.3.0.
  • Потрібен samplomatic >= 0.18.0.

Крок 2. Зміни імпорти

Sampler:

from qiskit_ibm_runtime import SamplerV2 as Sampler

Executor:

from qiskit_ibm_runtime import Executor, QuantumProgram

Крок 3. Заміни кортежі PUB на QuantumProgram

Замість передачі списку кортежів (PUB), під час використання Executor ти будуєш QuantumProgram і додаєш до нього елементи.

QuantumProgram приймає елементи типу circuit і samplex:

  • append_circuit_item: Додає CircuitItem, який є схемою та (опційно) її значеннями параметрів. Він виконується як є, без будь-якої рандомізації.

    Використовуй це, коли хочеш просто семплувати схему точно так, як це робив би Sampler з PUB без twirling; наприклад, при відправці простого завдання вибірки, або коли ти вже вручну включив будь-які варіанти, які хотів.

  • append_samplex_item: Додає samplexItem, який є шаблонною схемою плюс семплекс, що генерує рандомізовані набори параметрів на стороні сервера.

    Використовуй це, коли хочеш, щоб вміст схеми був рандомізований. Основний випадок — це twirling (вентилів або вимірювання) чи впровадження шуму. Ця можливість замінює вбудований twirling Sampler.

Один QuantumProgram може приймати обидва типи елементів; кожен доданий елемент виконується як незалежне завдання і створює власний запис у результатах. Загалом, використовуй append_circuit_item, коли твоя схема не потребує рандомізації. В іншому випадку використовуй append_samplex_item.

Наступні розділи показують кожен з них по черзі: параметризовані схеми, що використовують append_circuit_item, і перехід twirling за допомогою append_samplex_item.

У наступних прикладах коду isa_circuit позначає схему, транспільовану відповідно до архітектури набору інструкцій (ISA) цільового бекенду. Ця isa_circuit містить два параметри.

Крок 3a. Перехід параметризованих схем

У Sampler значення параметрів є другим елементом кортежу PUB. В Executor передай їх як circuit_arguments у append_circuit_item.

Sampler:

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"]

Крок 3b. Перехід вбудованого twirling до явних анотацій

Це найзначніша зміна. Sampler застосовує twirling за тебе за допомогою параметрів. З Executor ти явно оголошуєш цей намір за допомогою анотованих box і семплекса (з Samplomatic).

Sampler (twirling за допомогою параметрів):

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

Executor (twirling за допомогою box та семплекса):

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
)

Оскільки шаблонна схема і семплекс будуються на стороні клієнта, ти можеш перевіряти і семплувати їх локально, щоб перевірити вивід перед надсиланням чогось на апаратне забезпечення.

Перевірка: семплування шаблонної схеми локально

Ти можеш витягувати рандомізації з семплекса і зв'язувати їх із шаблонною схемою, щоб підтвердити, що семплекс видає очікувані значення параметрів. Значення параметрів, що повертаються samplex.sample, безпосередньо сумісні з параметрами шаблонної схеми.

# 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)

Щоб піти далі, ти можеш перевірити, що кожна рандомізація логічно еквівалентна оригінальній схемі, наприклад, перетворивши обидві на об'єкти Operator і порівнявши їхні унітарні реалізації (з урахуванням поправок outputs["measurement_flips.<register>"], які скасовують twirling вимірювання), або порівнявши очікувані значення з локального запуску StatevectorSampler чи StatevectorEstimator. Дивись посібник Samplomatic Samplex inputs and outputs для повного покрокового опису.

Крок 4. Зміни, як запитуються shots

Перенеси shots з PUB у QuantumProgram(shots=...). В Executor shots застосовується до всього завдання. Відправляй кілька завдань, якщо тобі потрібна різна кількість shots.

Sampler:

# 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)

Крок 5. Онови параметри за потреби

Executor має менше доступних параметрів, ніж Sampler, оскільки вибір щодо пом'якшення помилок тепер знаходиться в твоїх анотаціях і семплексі, а не в параметрах.

Також є структурна відмінність у тому, де зберігаються налаштування.

  • У Sampler усе, включно з вибором, що впливає на постобробку результатів, налаштовується в параметрах примітиву або в PUB.

  • В Executor вибір, що впливає на те, як формуються та постобробляються результати завдання, встановлюється в QuantumProgram, а не в ExecutorOptions.

Examples:

SamplerExecutor
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

ExecutorOptions містить лише налаштування виконання й середовища нижчого рівня, які не змінюють структуру повернутих даних. Він має три групи верхнього рівня:

Варто зазначити, що параметри twirling і dynamical_decoupling існують у Sampler, але не в Executor. Натомість ці значення параметрів виражаються через модель спрямованого виконання.

Example:

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)

Крок 6. Онови команду run

Вхідними даними для завдання Executor є програма, а не PUB.

Sampler:

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

Executor:

# Submit a job
executor.run(program)

Крок 7. Зміни спосіб доступу до результатів

У Executor результати — це масиви NumPy, а не об'єкти BitArray. Використовуй рядок з іменем як індекс (result[0]["meas"]), щоб отримати назад np.ndarray. Немає потреби пам'ятати шлях атрибута .data.<register>.

Щоб оновитися з Sampler на Executor, зміни result[i].data.<reg> (BitArray) на result[i]["<reg>"] (np.ndarray), а потім перепиши постобробку на основі get_counts як операції NumPy.

ЗавданняSamplerВиконавець
Отримати дані регіструresult[0].data.measresult[0]["meas"]
Тип данихBitArraynp.ndarray
Словник підрахунківresult[0].data.meas.get_counts()Обробити масив вручну
Кілька регістрівresult[0].data.<name> для кожного регіструresult[0]["<name>"] для кожного регістру
Форма масиву CircuitItem-(parameter_sets, shots, register_bits)
Форма масиву SamplexItem-(randomizations, parameter_sets, shots, register_bits)
Скасувати twirling вимірюваньАвтоматичноresult[i]["measurement_flips.<name>"] + XOR
примітка

BitArray від Sampler пропонує допоміжні функції (get_counts, slice_bits, slice_shots, expectation_values та маски пост-селекції). Executor повертає необроблені масиви NumPy, тож ти можеш виконати цю постобробку за допомогою стандартних операцій NumPy.

Крок 8. Обробка результатів твірлінгу (виправлення бітових інверсій)

Коли ти застосовуєш твірлінг вимірювань через SamplexItem, Executor повертає сирі (твірловані) вимірювання разом із виправленнями бітових інверсій, необхідними для скасування твірлінгу. Тобі потрібно застосувати їх вручну; нічого не виправляється неявно.

Під час використання Executor скасовуй твірлінг явно за допомогою виправлень measurement_flips.<reg> та операції XOR, як показано в наступному прикладі:

# 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

У Sampler немає еквівалентного кроку, оскільки він скасовує твірлінг за тебе.

Повний приклад: міграція базового завдання семплування

Sampler

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"]

Наступні кроки