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

Виконання динамічних схем

Версії пакетів

Код на цій сторінці було розроблено з використанням таких вимог. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

Динамічні схеми — це потужні інструменти, за допомогою яких можна вимірювати кубіти в середині виконання квантової схеми, а потім виконувати класичні логічні операції в рамках схеми, базуючись на результатах цих вимірювань у середині схеми. Цей процес також відомий як класичне зворотне подавання. Хоча це ще ранні дні розуміння того, як найкраще скористатися динамічними схемами, квантова дослідницька спільнота вже визначила ряд варіантів використання, таких як:

Однак ці покращення, принесені динамічними схемами, пов'язані з компромісами. Вимірювання в середині схеми та класичні операції зазвичай мають більший час виконання, ніж двокубітні вентилі, і це збільшення часу може нівелювати переваги зменшеної глибини схеми. Тому скорочення тривалості вимірювання в середині схеми є пріоритетним напрямом вдосконалення у міру того, як IBM Quantum® випускає нову версію динамічних схем. Інші обмеження при використанні динамічних схем дивись у таблиці сумісності функцій Estimator або Sampler.

Специфікація OpenQASM 3 визначає ряд структур потоку керування, але Qiskit Runtime наразі підтримує лише умовне твердження if. У Qiskit SDK це відповідає методу if_test на QuantumCircuit. Цей метод повертає контекстний менеджер і зазвичай використовується у твердженні with. Цей посібник описує, як використовувати це умовне твердження.

примітка

Приклади коду в цьому посібнику використовують стандартну інструкцію вимірювання для вимірювань у середині схеми. Однак рекомендується замість цього використовувати інструкцію MidCircuitMeasure, якщо backend її підтримує. Дивись розділ Вимірювання в середині схеми для деталей.

Пошук backends, що підтримують динамічні схеми

Щоб знайти всі backends, до яких має доступ твій акаунт та які підтримують динамічні схеми, запусти код, подібний до такого. Цей приклад передбачає, що ти зберіг свої облікові дані для входу. Також можна явно вказати облікові дані при ініціалізації сервісного акаунта Qiskit Runtime. Це дозволить переглядати backends, доступні для конкретного екземпляра або типу плану, наприклад.

Примітки
  • Backends, доступні для акаунту, залежать від екземпляра, вказаного в облікових даних.
  • Нова версія динамічних схем тепер доступна всім користувачам на всіх backends. Дивись оголошення для отримання додаткової інформації.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# This cell is hidden from users. It hides all those "...instance was not set..." warnings.
import warnings

warnings.filterwarnings("ignore", message=".*Instance was not set*")
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_kingston')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_boston')>]

Вимірювання в середині схеми

До qiskit-ibm-runtime v0.43.0 measure була єдиною інструкцією вимірювання в Qiskit. Однак вимірювання в середині схеми мають різні вимоги до налаштування порівняно з кінцевими вимірюваннями (вимірюваннями, що відбуваються в кінці схеми). Наприклад, при налаштуванні вимірювання в середині схеми потрібно враховувати тривалість інструкції, оскільки довші інструкції спричиняють більш зашумлені схеми. Не потрібно враховувати тривалість інструкції для кінцевих вимірювань, оскільки після кінцевих вимірювань інструкцій немає.

примітка

Інструкція MidCircuitMeasure відображається на інструкцію measure_2, що повідомляється в supported_instructions backend. Однак measure_2 підтримується не на всіх backends. Використовуй service.backends(filters=lambda b: "measure_2" in b.supported_instructions) для пошуку backends, що підтримують її. Нові вимірювання можуть бути додані в майбутньому, але це не гарантовано.

Метод MidCircuitMeasure

У qiskit-ibm-runtime v0.43.0 була введена інструкція MidCircuitMeasure. Як випливає з назви, це нова інструкція вимірювання, оптимізована для вимірювань у середині схеми на IBM® QPU. Хоча можна використовувати QuantumCircuit.measure для вимірювання в середині схеми, через свою конструкцію MidCircuitMeasure зазвичай є кращим вибором. Наприклад, вона додає менше накладних витрат до схеми, ніж при використанні QuantumCircuit.measure.

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)

circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
Важливі примітки
  • Для використання вимірювань повинен бути принаймні один класичний регістр.
  • Примітив Sampler вимагає вимірювань схеми. Можна додати вимірювання схеми з примітивом Estimator, але вони ігноруються.

Store

З qiskit-ibm-runtime версії 0.47.0 або пізнішої ти можеш використовувати інструкцію store для збереження результату класичного виразу, якщо цей вираз буде використовуватися неодноразово. Операції автоматично паралелізуються, що робить твій код значно ефективнішим під час виконання.

Для отримання додаткової інформації дивись посібник Класичне зворотне подавання та потік керування.

примітка

Коли ти використовуєш store для збереження значення в класичний регістр на реальному backend, це значення зберігається лише в пам'яті під час виконання і не копіюється та не повертається в результаті завдання.

Наприклад, у наступному коді temp має те саме значення, що й creg під час виконання, і if_test працює як очікується. Але після завершення завдання BitArray temp, повернений у результаті завдання, не містить значення creg. Тобто job.result()[0].data.temp дорівнює 0.

creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...

Повний приклад

Наступний код створює та запускає динамічну схему на апаратному забезпеченні IBM®.

from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)

# Create a dynamic circuit

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)

# Convert to an ISA circuit for the given backend

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

# Generate samplers for backend targets
sampler = SamplerV2(backend)

# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: d88cakp789is7391vq0g (DONE)

Обмеження Qiskit Runtime

Враховуй такі обмеження при запуску динамічних схем у Qiskit Runtime.

  • Через обмежену фізичну пам'ять керуючої електроніки також є обмеження на кількість тверджень if та розмір їхніх операндів. Це обмеження є функцією кількості мовлень та кількості трансльованих бітів у завданні (не у схемі).

    При обробці умови if дані вимірювань потрібно передати до керуючої логіки для здійснення цієї оцінки. Мовлення — це передача унікальних класичних даних, а трансльовані біти — це кількість класичних бітів, що передаються. Розглянемо таке:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    У попередньому прикладі коду перші два об'єкти if_test на c0 вважаються одним мовленням, оскільки вміст c0 не змінився і тому не потребує повторного мовлення. if_test на c1 є другим мовленням. Перше транслює всі три біти в c0, а друге транслює лише один біт, що в сумі складає чотири трансльованих біти.

    Наразі, якщо ти транслюєш 60 бітів щоразу, завдання може мати приблизно 300 мовлень. Якщо транслювати лише один біт щоразу, то завдання може мати 2400 мовлень.

  • Операнд, що використовується у твердженні if_test, має бути 32 або менше бітів. Таким чином, якщо ти порівнюєш весь ClassicalRegister, розмір цього ClassicalRegister має бути 32 або менше бітів. Однак якщо порівнюєш лише один біт з ClassicalRegister, то цей ClassicalRegister може бути будь-якого розміру (оскільки операнд — лише один біт).

    Наприклад, блок коду "Недійсно" не працює, оскільки cr має більше 32 бітів. Однак можна використовувати класичний регістр ширший за 32 біти, якщо тестується лише один біт, як показано в блоці коду "Дійсно".

    cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • Вкладені умовні вирази не дозволені. Наприклад, наступний блок коду не працюватиме, оскільки в ньому є if_test всередині іншого if_test:

    c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • Наявність reset або вимірювань всередині умовних виразів не підтримується.

  • Арифметичні операції не підтримуються.

  • Дивись таблицю функцій OpenQASM 3 для визначення того, які функції OpenQASM 3 підтримуються на Qiskit та Qiskit Runtime.

  • Коли OpenQASM 3 (замість QuantumCircuit) використовується як вхідний формат для передачі схем до примітивів Qiskit Runtime, підтримуються лише інструкції, що можуть бути завантажені в Qiskit. Класичні операції, наприклад, не підтримуються, оскільки не можуть бути завантажені в Qiskit. Дивись Імпорт програми OpenQASM 3 у Qiskit для отримання додаткової інформації.

  • Інструкції for, while та switch не підтримуються.

Використання динамічних схем з Estimator

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

Щоб відтворити поведінку Estimator, виконай такий процес:

  1. Групуй терміни всіх спостережуваних у розбиття. Це можна зробити за допомогою PauliList API, наприклад.
    примітка

    Можна використовувати атрибут примітива BitArray для обчислення очікуваних значень наданих спостережуваних.

  2. Виконуй одну схему зміни базису на розбиття (яка зміна базису потрібна для кожного розбиття). Дивись модуль measurement_bases утиліти доповнення для вимірювань Measurement bases для отримання додаткової інформації. Дивись документацію пакету утилітних доповнень Qiskit.
  3. Додавай результати для кожного розбиття разом.

Обмеження

Переглядай будь-яку таблицю сумісності функцій, щоб зрозуміти обмеження при використанні динамічних схем. Зверни увагу, що сумісність функцій не залежить від примітива.

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

Рекомендації