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

Міграція з серверних на клієнтські Sampler та Estimator

Цей посібник описує, як мігрувати з серверних реалізацій IBM Quantum® Sampler та Estimator до їхніх нових клієнтських реалізацій у qiskit-ibm-runtime. Інтерфейси та опції здебільшого не змінилися, тож більшість коду працює без змін, але є деякі поведінкові відмінності, які варто розуміти.

Передумови​

Sampler та Estimator — це примітивні інтерфейси, визначені в Qiskit. IBM Quantum Compute Service (раніше Qiskit Runtime) історично надавав реалізацію цих примітивів усередині свого середовища виконання. Коли ти викликаєш sampler.run() або estimator.run(), запит надсилається до сервісу, і всі обчислення — включно з придушенням та пом'якшенням помилок — відбуваються на стороні сервера.

Цей досвід «чорної скриньки» зручний: тобі не потрібно турбуватися про деталі реалізації. Але це також ускладнює налагодження, налаштування або навчання на прикладі примітивів, оскільки ти не бачиш, що відбувається під час обробки.

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

Починаючи з qiskit-ibm-runtime v0.50.0, Sampler та Estimator реалізовано заново на стороні клієнта поверх Executor. Вони надають ту саму зручність і абстракцію, що й раніше, і тепер ти можеш перевіряти деталі реалізації, коли це потрібно. Оскільки інтерфейси та опції залишаються здебільшого тими самими, міграція має бути безпроблемною.

Примітка: IBM Quantum підтримує лише версію 2 інтерфейсів Sampler та Estimator (BaseSamplerV2 та BaseEstimatorV2). Тому в цьому посібнику вони просто називаються Sampler та Estimator.

Оновлення імпортів​

Сьогодні тобі потрібно явно імпортувати нові реалізації з їхніх спеціальних модулів:

from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator

У найближчому майбутньому імпорти верхнього рівня будуть розв'язуватися до нових клієнтських реалізацій, і жодних змін коду не знадобиться:

# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator

Аналогічно, якщо ти створюєш типізовані об'єкти опцій, тобі потрібно імпортувати їх з qiskit_ibm_runtime.options_models, або ж просто передати звичайний вкладений словник:

from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions

Що залишається незмінним​

  • Створення примітиву з mode та options.

  • Сигнатура run() та формат PUB.

  • Дерево опцій (options.twirling, options.resilience, options.default_shots, і так далі).

  • Структура даних результату, що повертається job.result().

Несумісні зміни в новому Sampler​

ЗмінаДія міграції
Базовий примітив тепер Executor. Як інтерфейс користувача IBM Quantum Platform, так і job.primitive_id показуватимуть executor замість sampler.Онови будь-який код, що посилається на job.primitive_id.
Нова реалізація зіставляє вхідні дані Sampler із вхідними даними Executor, тож job.inputs повертає вхідні дані Executor.Онови будь-який код, що посилається на job.inputs. Див. Структура вхідних даних завдання.
Більше попередньої та наступної обробки тепер відбувається на стороні клієнта, тож sampler.run() та job.result() можуть виконуватися довше, ніж раніше.Увімкни логування рівня INFO, щоб відстежувати прогрес обробки на стороні клієнта. Див. Увімкнення логування INFO.
Метадані схеми копіюються в метадані результату. Типи даних, дозволені в метаданих результату, тепер обмежені str, float, int, bool та списками або словниками цих типів.Якщо тобі потрібні інші типи даних, спочатку закодуй їх як рядок (наприклад, за допомогою base64).
Класи опцій (options_models.SamplerOptions і так далі) тепер є моделями Pydantic замість dataclasses, тому їх більше не можна перетворити на словники Python за допомогою asdict().Використовуй натомість options.model_dump().
Класи опцій, які раніше мали суфікс V2 (ExecutionOptionsV2 і так далі), більше його не мають, оскільки примітиви V1 більше не підтримуються.Видали суфікс V2 цих класів опцій: заміни ExecutionOptionsV2 на ExecutionOptions, ResilienceOptionsV2 на ResilienceOptions, а SamplerExecutionOptionsV2 на SamplerExecutionOptions.
Якщо twirling увімкнено, і shots (у PUB або в run()), shots_per_randomization та num_randomizations вказано всі одночасно, то num_randomizations * shots_per_randomization має пріоритет над shots.Пропусти num_randomizations та shots_per_randomization, якщо хочеш, щоб використовувалося значення shots.
Деяка перевірка вхідних даних перенесена на сторону сервера і тепер викликає RuntimeError замість IBMInputValueError.Онови типи винятків, які обробляє твій код.
Змішані значення shots в одному завданні більше не підтримуються.Надсилай окреме завдання для кожного значення shots. Див. Розділення завдань щодо міркувань.

Несумісні зміни в новому Estimator​

ЗмінаДія міграції
Базовий примітив тепер Executor. Як інтерфейс користувача IBM Quantum Platform, так і job.primitive_id показуватимуть executor замість estimator.Онови будь-який код, що посилається на job.primitive_id.
Нова реалізація зіставляє вхідні дані Estimator із вхідними даними Executor, тож job.inputs повертає вхідні дані Executor.Онови будь-який код, що посилається на job.inputs. Див. Структура вхідних даних завдання.
Більше попередньої та наступної обробки тепер відбувається на стороні клієнта, тож estimator.run() та job.result() можуть виконуватися довше, ніж раніше.Увімкни логування рівня INFO, щоб відстежувати прогрес обробки на стороні клієнта. Див. Увімкнення логування INFO.
Метадані схеми копіюються в метадані результату. Типи даних, дозволені в метаданих результату, тепер обмежені str, float, int, bool та списками або словниками цих типів.Якщо тобі потрібні інші типи даних, спочатку закодуй їх як рядок (наприклад, за допомогою base64).
Класи опцій (options_models.EstimatorOptions і так далі) тепер є моделями Pydantic замість dataclasses, тому їх більше не можна перетворити на словники Python за допомогою asdict().Використовуй натомість options.model_dump().
Класи опцій, які раніше мали суфікс V2 (ExecutionOptionsV2 і так далі), більше його не мають, оскільки примітиви V1 більше не підтримуються.Видали суфікс V2 цих класів опцій: заміни ExecutionOptionsV2 на ExecutionOptions, а ResilienceOptionsV2 на ResilienceOptions.
Усі вхідні опції повертаються в метаданих результату, а не обраний підмножина.Немає — це лише інформаційно.
Деяка перевірка вхідних даних перенесена на сторону сервера і тепер викликає RuntimeError замість IBMInputValueError.Онови типи винятків, які обробляє твій код.
Більше немає неявного вивчення шуму для PEA та PEC. Вивчення шуму вимірювання для TREX усе ще підтримується.Вивчай моделі шуму окремо та передавай їх до Estimator. Див. Явне вивчення шуму для PEA та PEC.
Тип вхідних даних ResilienceOptions.layer_noise_model інший і може бути побудований з результатів NoiseLearnerV3.Див. Явне вивчення шуму для PEA та PEC щодо того, як вивчати моделі шуму за допомогою NoiseLearnerV3 та передавати їх до Estimator.
MeasureNoiseLearningOptions.shots_per_randomization більше не підтримується.Для всіх схем у завданні використовується єдине значення shots, включно зі схемами вивчення шуму вимірювання. Якщо тобі потрібно використати інше значення shots, застосуй TREX за допомогою qiskit-mitigation поза Estimator.
Змішані значення точності в одному завданні більше не підтримуються.Надсилай окреме завдання для кожної бажаної точності. Див. Розділення завдань щодо міркувань.
Опція seed_estimator більше не підтримується.Видали будь-яке присвоєння options.seed_estimator (його встановлення викликає ValidationError). Немає клієнтського еквівалента, тож результати більше не є відтворюваними за допомогою цього seed.

Увімкнення логування INFO​

Оскільки тепер на стороні клієнта відбувається більше роботи, корисно бачити прогрес цієї обробки. Увімкни логування рівня INFO для логера qiskit_ibm_runtime:

import logging

logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)

Явне вивчення шуму для PEA та PEC​

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

Важливо

PEA та PEC тепер вимагають цей явний шаблон. Не пропускай крок вивчення шуму, інакше твій код не спрацює. Вивчення шуму вимірювання для TREX не зачіпається і продовжує працювати як раніше.

Аналогічно, якщо твій код використовує NoiseLearner і передає отриману модель шуму серверному Estimator, тобі потрібно мігрувати до NoiseLearnerV3. НЕ використовуй старіший NoiseLearner, який несумісний з новим Estimator.

Усі опції вивчення шуму в серверному Estimator (LayerNoiseLearningOptions) напряму зіставляються з опцією NoiseLearnerV3 (NoiseLearnerV3Options), за винятком max_layers_to_learn. Кількість шарів для вивчення натомість базується на кількості шарів, переданих до NoiseLearnerV3.

Наприклад:

Серверний Estimator (з увімкненим PEC):

from qiskit_ibm_runtime import Estimator

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64

job = estimator.run(pubs)

Клієнтський Estimator (з увімкненим PEC):

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()

# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# Now execute the target PUBs.
job = estimator.run(pubs)

Міграція з NoiseLearner до NoiseLearnerV3​

NoiseLearner працює лише з серверною реалізацією Estimator. Тому, якщо твій код використовує NoiseLearner для вивчення моделі шуму та передачі її до Estimator, тобі потрібно оновити свій код для використання NoiseLearnerV3.

Дивись посібник Міграція з NoiseLearner до NoiseLearnerV3 для отримання деталей.

Розділення завдань​

Коли тобі потрібно розділити одне завдання на кілька, оскільки змішані значення shots або точності в одному завданні більше не підтримуються, врахуй наступне:

  • Групуй PUB за їхнім цільовим значенням — одне завдання на кожне окреме значення, а не одне завдання на кожен PUB. Розділення — це перегрупування, тож загальна кількість PUB, які ти надсилаєш, не змінюється. Наприклад, маючи [A@0.01, B@0.05, C@0.01], надішли два завдання: [A, C] з precision=0.01 та [B] з precision=0.05. Надсилання A та C як окремих завдань менш ефективне, оскільки кожне завдання має фіксовані накладні витрати.

  • Вивчи один раз і використовуй моделі шуму в усіх розділених завданнях. Ефективніше запустити одне завдання NoiseLearnerV3 над об'єднанням усіх шарів. Результат завдання вивчення шуму містить список об'єктів NoiseLearnerV3Result, по одному для кожної вхідної інструкції, у тому ж порядку, що й вхідний список. Ти можеш використовувати вихід цього завдання вивчення шуму в усіх розділених (Estimator) завданнях, а моделі шуму для шарів, яких немає в PUB розділеного завдання, ігноруються.

  • Спочатку надішли всі розділені завдання в Batch, а потім збери їхні результати. Режим виконання Batch забезпечує ефективне паралельне виконання, коли є кілька завдань. Однак job.result() є блокувальним, тож виклик його всередині циклу надсилання серіалізує завдання та зводить нанівець переваги використання Batch. Переконайся, що використовуєш шаблон «надіслати все, потім зібрати» (показаний нижче).

У наступному прикладі pub1 та pub2 вимагають precision=0.5, тоді як pub3 вимагає precision=0.1:

group1_pubs = [pub1, pub2]
group2_pubs = [pub3]

with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True

# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)

# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))

# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]

Структура вхідних даних завдання​

Нова реалізація зіставляє вхідні дані Sampler або Estimator із вхідними даними Executor, тож job.inputs повертає словник, що містить вхідні дані Executor. Цей словник має такі ключі:

  • options: Вхідний ExecutorOption.

  • quantum_program: Вхідний QuantumProgram

  • schema_version: Використана версія схеми на стороні сервера.

Якщо твій код використовував job.inputs['options'] для пошуку опцій, вказаних для завдання, тепер ти можеш замість цього використати job.result().metadata['options'].

Локальне тестування з фейковим бекендом​

Перед надсиланням на апаратне забезпечення ти можеш перевірити мігрований код на бекенді Fake*, щоб виявити будь-які синтаксичні помилки заздалегідь. Зверни увагу на такі деталі щодо режиму локального тестування:

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

  • NoiseLearnerV3 не має режиму локального тестування: його mode приймає лише справжній Backend, Session або Batch, тож ти не можеш виконати крок вивчення шуму на фейковому бекенді. Перевір цю частину свого коду за допомогою довідника API NoiseLearnerV3. Переконайся, що конструктор, форма вхідних даних run(instructions) та будь-який помічник (наприклад, помічник унікальних шарів) використовуються, як задокументовано.

Перетворення схеми на Clifford для ефективної локальної симуляції​

Фейковий бекенд використовує симулятор станвектора (зашумлений), вартість якого зростає експоненційно з кількістю кубітів і глибиною. Тому реалістична робоча схема може зависнути або вичерпати пам'ять. Оскільки локальне тестування має лише перевіряти шляхи опцій (а не відтворювати фізичні результати), спочатку зведи схему до Clifford за допомогою ConvertISAToClifford, яка округлює кожен кут RZ/RZZ/RX до найближчого кратного π/2. Схеми Clifford симулюються ефективно (стабілізаторна симуляція) незалежно від розміру.

from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford

clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive

ConvertISAToClifford вимагає ISA-схему як вхідні дані (вихід generate_preset_pass_manager(...).run(...), спрямований на бекенд). Тобі потрібно врахувати такі наслідки при побудові локального PUB:

  • Атрибут .layout відкидається. Cliffordizована схема зберігає ту саму кількість кубітів, але clifford.layout дорівнює None, тож observable.apply_layout(clifford.layout) зазнає невдачі. Натомість розклади спостережуваний об'єкт з до-Clifford ISA схеми: isa_obs = observable.apply_layout(isa_circuit.layout), потім запусти (clifford, isa_obs).

  • Параметри зв'язуються. Округлення кутів обертання перетворює параметричну ISA-схему на конкретну Clifford-схему, тож clifford.num_parameters стає 0. PUB, що досі містить масив значень параметрів, не пройде приведення типів. Для локального запуску видали масив параметрів з PUB; апаратний запуск зберігає оригінальну параметричну схему та її значення.

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