Розгортання та запуск шаблону Qiskit Function для динаміки Гамільтоніана AQC + Trotter
Огляд
Це незалежний від експерименту шаблон Qiskit Function для динаміки Гамільтоніана. Маючи 1D гамільтоніан Паулі найближчих сусідів, підготовлений початковий стан (необов'язково) та набір спостережуваних, він виконує еволюцію в часі Trotter, стиснення схеми за допомогою наближеного квантового компілювання (AQC) та пом'якшене виконання, а потім повертає часовий ряд кожної спостережуваної величини. Поміняй місцями налаштування (PRE) та аналіз (POST), і те саме ядро керує іншим експериментом:
| PRE (твоє налаштування) | FUNCTION (розгорнуто тут) | POST (твій аналіз) |
|---|---|---|
| Підготуй стан як схему або добутковий стан, з необов'язковим локальним поштовхом | Синтез Trotter → стиснення AQC → виконання на statevector, fake або runtime, повертаючи | для нейтронного розсіювання, або намагніченості, транспорту, динаміки гартування тощо |
Шаблон опубліковано в репозиторії шаблонів Qiskit Function, поряд з іншими прикладними шаблонами. Цей блокнот розгортає його у твій власний акаунт Qiskit Serverless. Запусти його один раз, і тоді будь-який блокнот зможе викликати функцію за допомогою serverless.load("aqc-dynamics-function").
Для практичного наукового прикладу дивись Симуляція нейтронного розсіювання за допомогою робочого процесу Serverless з динамікою AQC + Trotter, який викликає цю функцію для обчислення динамічного структурного фактора KCuF. Цей блокнот натомість охоплює розгортання та контракт вхідних даних.
Вимоги
Перш ніж почати, переконайся, що в середовищі ядра цього блокнота є наступне:
-
Qiskit SDK v2.0 або новіше (
pip install qiskit). -
Клієнт Qiskit IBM Catalog (
pip install qiskit-ibm-catalog), який розгортає та запускає робочі навантаження в Qiskit Serverless.
Власні наукові залежності функції (qiskit-addon-aqc-tensor, cotengrust, qiskit-aer) не потрібно встановлювати локально.
Отримання вихідних файлів шаблону
Функція — це невеликий пакет Python, який Qiskit Serverless запускає в хмарі, тож його вихідний код повинен існувати як локальні файли, які завантажуються під час розгортання. Пакет опубліковано в репозиторії шаблонів Qiskit Function.
Завантаж source_files
Завантаження — це один zip-файл, названий за повним шляхом каталогу в репозиторії:
qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip
-
Розпакуй його в каталог, що містить цей блокнот.
-
Перейменуй витягнуту папку з цієї довгої назви на
source_files.
Твій робочий каталог тоді виглядатиме так:
your-working-directory/
├── function-template-aqc-trotter.ipynb <- this notebook
└── source_files/ <- the renamed folder
├── __init__.py
├── program.py
└── source/
├── __init__.py
├── _serverless.py
├── app_function.py
├── aqc.py
├── build.py
├── execute.py
└── hamiltonian.py
Назва повинна бути точно source_files, тому що це working_dir, який завантажує Крок 3.
program.py — це точка входу, яку викликає шлюз. Все, що знаходиться під source/, є реалізацією, розділеною за етапами: синтез Гамільтоніана та Trotter, стиснення AQC та виконання. Нічого з цього не потрібно редагувати, щоб запустити наступні приклади. Крок 3 завантажує весь каталог, тож повторюй цей крок щоразу, коли змінюєш файл.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog
1. Автентифікація
Використай qiskit-ibm-catalog, щоб автентифікуватися в QiskitServerless за допомогою свого API-ключа (токена) та CRN (екземпляра), які можна знайти на панелі IBM Quantum® Platform. З цими обліковими даними ти можеш локально створити екземпляр клієнта serverless для завантаження або запуску обраної функції:
from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
Ти можеш опціонально використати save_account(), щоб зберегти свої облікові дані в локальному середовищі (дивись посібник Налаштування акаунту IBM Cloud®). Зверни увагу, що це записує твої облікові дані в той самий файл, що й QiskitRuntimeService.save_account():
QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
Якщо акаунт збережено, немає потреби надавати токен для автентифікації:
from qiskit_ibm_catalog import QiskitServerless
# Authenticate to the remote cluster
# In this case, loading a saved account
serverless = QiskitServerless()
# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT
# serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
2. Оголошення залежностей
Пакети, які потрібні функції на додаток до керованого базового образу serverless.
Шлюз встановлює лише назви зі свого дозволеного списку (requirements-dynamic-dependencies.txt), що збігаються за назвою пакета та закріплені на дозволеній версії за допомогою ==. Все інше має прийти транзитивно (як залежність пакета з дозволеного списку). Синтаксис [extras] враховується: qiskit-addon-aqc-tensor[quimb-jax] — це те, що встановлює quimb та jax. cotengrust потрібен для ефективності пам'яті під час симуляції тензорної мережі. qiskit-aer вказано окремо для бекенда fake (локальна шумова симуляція).
DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]
3. Визначення та завантаження функції
from qiskit_ibm_catalog import QiskitFunction
fn = QiskitFunction(
title="aqc-dynamics-function",
entrypoint="program.py",
working_dir="source_files/",
dependencies=DEPENDENCIES,
)
serverless.upload(fn)
QiskitFunction(aqc-dynamics-function)
4. Перевірка реєстрації
next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)
Довідник функції
Це короткий вступ. Кожне поле повністю задокументовано в README шаблону AQC Dynamics: повна таблиця вхідних даних з правилами валідації, вихідні поля, бекенди виконання та додаткові практичні приклади. Далі наведено коротку версію, достатню для читання наступних прикладів.
Вхідні дані
Кожен запуск — це один виклик fn.run(...). Обов'язковими є лише перші три вхідні дані в таблиці: hamiltonian, t_steps та aqc_segments. Все інше після них необов'язкове і повертається до показаного типового значення, тож мінімальний виклик — це три аргументи, а решта таблиці — це функціональність, яку можна обрати. num_qubits Гамільтоніана встановлює довжину ланцюга, тож окремого вхідного значення розміру немає.
| Вхідні дані | Типове значення | Опис |
|---|---|---|
hamiltonian | обов'язково | 1D гамільтоніан Паулі найближчих сусідів як SparsePauliOp. Рядки — це оператори Паулі, тож немає неявного множника одна друга. |
t_steps | обов'язково | Загальна кількість кроків Trotter. Еволюціонує до T = t_steps * dt і повідомляє кожну спостережувану на кожному t_k = k * dt. |
aqc_segments | обов'язково | План стиснення: список {"n_steps": k, "ansatz_steps": m}. sum(n_steps) кроків стискаються; решта виконуються як звичайний Trotter. |
dt | 0.2 | Фізичний час, на який просувається один крок Trotter. |
initial_state | |0...0> | Підготовлена QuantumCircuit для еволюції. Впечи будь-який локальний поштовх у цю схему. |
observables | Z на сайт | Все, що EstimatorV2 приймає як свій аргумент observables. Одна спостережувана на вихідний стовпець. |
trotter_options | Судзукі 2-го порядку | {"method": ..., "synthesis_settings": {...}}. reps та time належать функції. |
aqc_options | дивись опис | max_bond (32), cutoff (1e-8), autodiff_backend ("jax"), fidelity_target (None), optimizer_settings (L-BFGS-B, jac=True, maxiter=300). |
estimator_options | DD, твірлінг, TREX | EstimatorV2.options, передається без змін. Наданий словник повністю замінює типові значення, а не об'єднується з ними. |
transpiler_options | {"optimization_level": 3} | Аргументи ключових слів generate_preset_pass_manager. backend та target відхиляються, оскільки шлях виконання володіє ними. |
backend | "runtime" | "statevector", "fake" або "runtime". |
backend_name | найменш завантажений | Назва бекенда IBM® для runtime, або назва фейкового бекенда. |
batches | 1 | Розділяє схеми на N завдань runtime. Один пакет надсилає одне завдання і не створює сесії. |
parallel_sim | False | Розподіляє шляхи локального симулятора по всіх доступних ядрах за допомогою Ray. Не має ефекту на runtime. |
return_circuits | False | Повертає логічні схеми AQC + Trotter в результаті поряд з рядами спостережуваних. |
Бекенди виконання
Усі три шляхи використовують той самий код і ті самі налаштування пом'якшення. Вони відрізняються лише тим, де виконуються схеми.
backend | Що це таке | Облікові дані | Примітки |
|---|---|---|---|
"statevector" | Точний StatevectorEstimator | Лише акаунт Serverless | Точний еталонний шлях. Без часу QPU. |
"fake" | Шумова локальна симуляція на фейковому бекенді Qiskit | Лише акаунт Serverless | Достовірна репетиція пом'якшеного шляху runtime. Потребує qiskit-aer. За замовчуванням використовується 127-кубітний fake_sherbrooke. |
"runtime" (типовий) | Пом'якшений EstimatorV2 на реальному QPU | Акаунт Serverless та екземпляр з доступом до QPU | backend_name необов'язково; якщо не вказано, обирається найменш завантажений пристрій. |
Обидва шляхи симулятора все ще викликають розгорнуту функцію, тож їм потрібен збережений акаунт Serverless, навіть якщо вони не використовують час QPU. Два наступні приклади запускають те саме робоче навантаження спочатку на statevector, потім на runtime.
Вивід
job.result() повертає звичайний словник:
{
"times": [...], # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)
"expectation_values": [[...]], # shape (n_times, n_observables)
"observable_labels": [...], # for example: ["Z_0", "ZZ_0_1"]
"metadata": {
"n", "t_steps", "dt", "tier",
"aqc_compressed_steps": 5, # total compressed steps (= sum of segment n_steps)
"aqc_segments": [ # per segment: the plan plus its own results
{"n_steps": 3, "ansatz_steps": 1, "steps": [1, 2, 3], "n_params": 133,
"fidelities": {"1": ..., "2": ..., "3": ...}},
{"n_steps": 2, "ansatz_steps": 2, "steps": [4, 5], "n_params": 245,
"fidelities": {"4": ..., "5": ...}},
],
"execution_backend",
"aqc_fidelities": {"1": ..., "2": ...}, # flat per-step fidelity, all compressed steps
"circuit_stats": { # per-step 2q depth and gate count, full Trotter vs AQC
"1": {"full_trotter": {"depth_2q": ..., "num_2q_gates": ...},
"aqc_trotter": {"depth_2q": ..., "num_2q_gates": ...}},
"2": {...},
},
"warnings": [...], # non-fatal notices; for example, a cotengrust fallback
"resource_usage": { # per stage; QPU_TIME is the charged QPU time
"RUNNING: OPTIMIZING_FOR_HARDWARE": {"CPU_TIME": ...},
"RUNNING: WAITING_FOR_QPU": {"CPU_TIME": ...},
"RUNNING: EXECUTING_QPU": {"QPU_TIME": ...},
},
},
# present only when return_circuits=True
"circuits": [QuantumCircuit, ...], # one per evolved step; circuits[i] is at times[i + 1]
}
aqc_fidelities та circuit_stats — це два поля, які варто прочитати першими: разом вони показують, чи залишилося стиснення достовірним і чи справді воно зекономило глибину. На runtime, resource_usage повідомляє час очікування в черзі окремо від часу QPU, за який тебе стягують плату. Відхилений вхід швидко завершується як структурована ServerlessError (код 4615).
Приклад із симулятором
Спочатку запусти функцію на точному Backend statevector. Це не витрачає часу QPU і перевіряє розгортання наскрізно. Модель тут — восьмикубітний ланцюжок Ізінга в поперечному полі, а observables пропущено, тож функція вимірює типове для кожного вузла.
План стиснення — це вхідне значення, яке варто зрозуміти. Кожен сегмент {"n_steps": k, "ansatz_steps": m} стискає k послідовних кроків Trotter в анзац, побудований з m-крокової цілі Trotter, і будь-які кроки понад sum(n_steps) виконуються як звичайний Trotter. Ранні кроки з низькою заплутаністю добре стискаються в неглибокий однорівневий анзац; пізніші, більш заплутані кроки потребують глибшого.
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 8
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=8,
aqc_segments=[
{
"n_steps": 4,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 2,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="statevector",
)
print("job ID:", job.job_id)
job ID: ee1f3793-e995-427d-81d1-5924549beb38
Слідкування за виконанням і читання результату
status() повідомляє як грубий життєвий цикл завдання, так і субстатус кожного етапу, який публікує функція під час виконання. Ті самі етапи застосовуються до запуску на апаратному забезпеченні далі в цьому посібнику:
QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE
Значення status() | Етап |
|---|---|
RUNNING: OPTIMIZING_FOR_HARDWARE | підготовка стану, побудова Trotter, стиснення AQC |
RUNNING: WAITING_FOR_QPU | у черзі на QPU (лише бекенд runtime) |
RUNNING: EXECUTING_QPU | виконання схем (локальні симулятори позначають це напряму) |
RUNNING: POST_PROCESSING | збирання словника результату |
Кінцеві стани — це DONE, ERROR та CANCELED. Цей запуск statevector не має черги QPU, тож він пропускає RUNNING: WAITING_FOR_QPU. Використай job.logs() у будь-який момент, щоб побачити журнали кожного етапу, включаючи достовірність AQC, досягнуту на кожному кроці.
print(job.status()) # re-run until this reports DONE
DONE
import numpy as np
result = job.result()
ev = np.array(result["expectation_values"])
print("observables:", result["observable_labels"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("first row (t = 0, the prepared state):", np.round(ev[0], 4))
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']
shape: (9, 8) -> (n_times, n_observables)
first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]
last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}
2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)
Приклад з апаратним забезпеченням
Виклик функції з backend="runtime" транспілюється та виконується на реальному процесорі IBM Quantum, з вбудованим пом'якшенням помилок функції: динамічним розчепленням (XY4), твірлінгом гейтів та твірленим придушенням помилок зчитування (TREX). backend_name обирає пристрій; якщо пропустити, функція обирає найменш завантажений.
У науковому коді нічого не змінюється. Що відрізняється від прикладу з симулятором, так це довжина ланцюга, кількість кроків Trotter, план стиснення, бекенд та явні налаштування пом'якшення, розглянуті в наступному розділі.
Визначення розміру завдання для апаратного забезпечення керування
estimator_options — це вхідне значення, яке варто встановлювати навмисно. Твірлінг гейтів будує num_randomizations окремих рандомізованих схем для кожного PUB, і все завдання, кожен PUB з усіма його рандомізаціями, повинно вміститися в пам'яті інструкцій класичної системи керування QPU. Функція за замовчуванням використовує 1000 рандомізацій, тож еволюція з 10 кроками надсилає 11 PUB по 1000 схем кожен: приблизно 11 000 екземплярів схем в одному завданні.
Якщо перевищити те, що вміщує система керування, завдання завершується з помилкою 6073. Ліміти завдань дає порогові значення та спосіб їх обчислення, головним з яких є 26,8 мільйона інструкцій системи керування на кубіт, застосовується на завдання, а не на PUB. Динамічне розчеплення додає гейти, які враховуються в цьому.
Два вхідні значення керують розміром:
-
estimator_optionsвстановлює бюджет пострілів. Загальна кількість пострілів — цеnum_randomizations * shots_per_randomization, тож ти можеш обмінювати рандомізації на постріли на рандомізацію, зберігаючи статистику, і при цьому все ж зменшувати програму. Наступна комірка використовує 100 рандомізацій по 200 пострілів кожна, що становить 20 000 пострілів на спостережувану і приблизно десяту частину екземплярів схем, які надіслали б типові значення. Дивись TwirlingOptions та Параметри Estimator для повного набору полів. -
batchesрозділяє PUB на стільки окремих завдань runtime, що є засобом, який рекомендує сама помилка 6073, і чому важливе формулювання на рівні завдання. Встановленняbatches=4надсилає приблизно три PUB на завдання замість одинадцяти одразу, а завдання йдуть разом в одному пакеті, тож група стає в чергу один раз, а не кожне завдання окремо.
Пам'ятай, що наданий estimator_options повністю замінює типові значення функції, а не об'єднується з ними, тож динамічне розчеплення та TREX повторно вказуються в наступній комірці, щоб зберегти їх увімкненими.
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 10
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=10,
aqc_segments=[
{
"n_steps": 3,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 3,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="runtime",
backend_name="ibm_marrakesh",
# The function defaults to 1000 twirling randomizations, which was too large
# for this device. Total shots is num_randomizations *
# shots_per_randomization, so this is 20,000 shots per observable.
estimator_options={
"dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
"twirling": {
"enable_gates": True,
"num_randomizations": 100,
"shots_per_randomization": 200,
},
"resilience": {"measure_mitigation": True},
},
)
print("job ID (save this to reconnect later):", job.job_id)
job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9
Запуск на апаратному забезпеченні не швидкий, і більшість часу класична, а не на QPU. Стиснення AQC виконується всередині функції до того, як щось потрапляє на QPU, а черга QPU додається зверху. Тобі не потрібно тримати цей блокнот або ядро відкритими, поки він виконується.
Скопіюй ID завдання, надрукований попередньою коміркою, та збережи його. Наступні три комірки дозволяють тобі повернутися до нього пізніше:
-
Повторне підключення, потрібне лише в новій сесії ядра: повторно запусти комірку Автентифікація, щоб відтворити
serverless, потім відбудуй дескрипторjobз ID, який ти зберіг. Пропусти цю комірку, якщо ти все ще в сесії, де надіслав завдання, оскільки дескриптор уже активний. -
Перевірка статусу: повторно запускай, доки не повідомить
DONE. -
Отримання результату: запусти лише коли статус
DONE.
Встав свій збережений ID замість заповнювача в наступній комірці повторного підключення.
# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Re-run this until it reports DONE, then fetch the result in the following cell.
print(job.status())
DONE
import numpy as np
# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits.
result = job.result()
ev = np.array(result["expectation_values"])
print("backend:", result["metadata"]["execution_backend"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
backend: runtime
shape: (11, 10) -> (n_times, n_observables)
last row (t = t_steps * dt): [0.1504 0.1361 0.218 0.2144 0.2275 0.1783 0.1749 0.1599 0.0915 0.0922]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}
2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)
Наступні кроки
-
Пройди Симуляцію нейтронного розсіювання за допомогою робочого процесу Serverless з динамікою AQC + Trotter, супровідний приклад, який викликає цю розгорнуту функцію для обчислення динамічного структурного фактора KCuF.
-
Прочитай AQC Dynamics Template на GitHub для повного контракту вхідних та вихідних даних, додаткових прикладів і деталей цитування.
-
Переглянь репозиторій шаблонів Qiskit Function для інших прикладних шаблонів, побудованих таким же чином.
-
Прочитай посібник Qiskit Serverless для керування розгорнутими функціями.
-
Заглибся в етап стиснення AQC за допомогою документації Qiskit addon: AQC-Tensor.