Руководство по Лаборатории
Лаборатория даёт вам Python со всеми квантовыми библиотеками прямо в браузере, симуляторы всех видов, двойники настоящих чипов, само настоящее железо и арендованные машины для счёта, который никуда больше не помещается. Это руководство рассказывает, какие задачи здесь решаются и как именно: с кодом, с ценами и с указанием, в каком блокноте что разобрано.
Вход и первые шаги
Войдите на странице входа тем же мессенджером, которым пользуетесь в курсе: откроется чат с ботом, нажмите там «Старт» и вернитесь на страницу. Через полминуты у вас открыта среда с блокнотами. Ставить на свой компьютер ничего не нужно, среда целиком живёт в браузере и говорит по-русски.
Первым делом откройте блокнот 00-start: он за десять минут проводит
по среде и показывает главный вызов, с которого начинается любая работа:
import qqlab
lab = qqlab.connect() # адрес и ключ уже в окружении, аргументы не нужны
lab.backends() # весь парк: симуляторы, двойники, железо, машины в аренду
Объект lab это единая точка входа. Через него уходят прогоны на
симуляторы и железо, через него же доступны классические решатели, фотоника, отжиг
и арендованные машины. Полный список вызовов с примерами собран в блокноте
12-sdk-reference.
Как устроено рабочее место
Ваш дом это каталог со своими файлами: блокноты, данные, результаты. Он переживает выход, перезапуск среды и обновления; ночами с него снимается резервная копия. Примеры при первом входе копируются к вам домой, это уже ваши файлы: правьте их как угодно, оригиналы не пострадают, а новые примеры досыпаются при обновлениях, не трогая ваших правок.
Недостающие пакеты ставятся обычным способом прямо из блокнота:
%pip install networkx
Три вещи, которые полезно знать заранее:
- Контейнеру среды отведено 2 ГБ памяти. Для схем до 25 кубитов, фотоники, отжига и всей учебной работы этого достаточно; что не помещается, уходит на арендованные машины.
- Среда, простоявшая час без дела, засыпает. Файлы при этом целы: вход обратно возвращает всё как было.
- Перезапустить среду можно через меню «Файл → Панель управления Hub»: кнопка остановки, затем кнопка запуска. Так же подтягиваются свежие примеры.
Карта примеров
Шестнадцать блокнотов лежат в каталоге examples и устроены как
подробные разборы: каждый шаг объяснён до того, как случится. Ходить по ним
подряд не обязательно, выбирайте по задаче.
| Блокнот | О чём |
|---|---|
00-start | путеводитель по среде и первый прогон |
01-hello-bell | первая запутанная пара и чтение результатов |
02-backends-tour | экскурсия по парку: чем симулятор отличается от двойника и железа |
03-vqe-vs-dmrg | вариационный расчёт против классического эталона на одной задаче |
04-photonics-hom | фотоника: эффект Хонга, Оу и Манделя своими руками |
05-qubo-annealing | постановка задачи оптимизации и отжиг |
06-photonics-server | фотонные схемы на серверных движках |
07-anneal-server | отжиг на серверных решателях с бюджетом |
08-noise-and-state | модели шума и что они делают с состоянием |
09-workshop-layout | сквозная задача: расстановка оборудования в цехе от постановки до ответа |
10-chip-twin | паспорт настоящего чипа и сборка его двойника |
11-maintenance-schedule | вторая сквозная задача: график ремонтов при ограниченных бригадах |
12-sdk-reference | справочник по всем вызовам с примерами |
13-heavy-machine | одна арендованная машина: когда нужна и сколько стоит |
14-heavy-batch | пачка работ на арендованных машинах: все виды, отмена, счёт |
15-heavy-session | сессия: машина держится, вариационный цикл идёт шагами |
Схемы и симуляторы
Схемы пишутся на привычном Qiskit, среда его уже знает. Небольшую схему считает идеальный симулятор:
from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
result = lab.simulate(qc, shots=2000)
result.counts # {'00': 1004, '11': 996}
Идеальный ответ полезен как ориентир, но настоящие устройства шумят. Модель шума подключается тем же вызовом, и по разнице с идеальным ответом видно, что именно шум делает с вашей схемой:
noisy = lab.simulate(qc, shots=2000, noise={"error_1q": 0.002, "error_2q": 0.02})
Как шум устроен внутри и как читать его следы в результатах, разобрано в
блокноте 08-noise-and-state. Что делать, когда кубитов становится
больше, чем помещается в среде, рассказано в разделе про
арендованные машины.
Двойники чипов и настоящее железо
Список всего, на чём можно считать, живой и запрашивается одной строкой:
for b in lab.backends():
print(b.address, b.kind, b.num_qubits, "кубит",
"платно" if b.paid else "бесплатно")
Каждая точка исполнения бывает одного из трёх видов. Симулятор считает идеально. Двойник это эмулятор конкретного настоящего чипа, собранный из его топологии и свежей калибровки: он отвечает на вопрос «что получится именно на этом устройстве», не тратя очередь и деньги. Железо это сам чип: прогон встаёт в очередь производителя, а результат забирается по готовности.
Прогон на выбранной точке выглядит одинаково для всех трёх видов:
result = lab.run(qc, "iqm/emerald::emulator", shots=1000)
Двойника можно собрать и своими руками из паспорта устройства. Паспорт это топология, времена когерентности, ошибки гейтов и считывания, всё с последней калибровки:
props = lab.properties("iqm/emerald")
twin = props.twin() # обычный симулятор Qiskit с шумом именно этого чипа
Разбор паспорта и сборка двойника показаны в блокноте 10-chip-twin,
а экскурсия по всему парку в 02-backends-tour. Прогоны на настоящем
железе платные, цена видна в профиле после каждого прогона.
Фотоника
Фотонные вычисления устроены иначе, чем гейтовые: состояние живёт в модах
света, а схема собирается из светоделителей и фазовых пластин. В среде стоят
профильные библиотеки, а у lab есть фотонное крыло:
ph = lab.photonic
hom = ph.hom(shots=2000) # эффект Хонга, Оу и Манделя
hom.probabilities # два фотона выходят вместе, а не порознь
bs = ph.beamsplitter(0, 1, theta=0.785)
res = ph.sample(modes=2, photons=[1, 1], elements=[bs], shots=1000)
Для задач побольше есть выборка по графу: она лежит в основе задачи о плотном
подграфе и разобрана вместе с ней. Начните с 04-photonics-hom, где
эффект собирается руками, затем 06-photonics-server про серверные
движки и выбор платформы через ph.platforms().
Оптимизация и отжиг
Большой класс практических задач сводится к выбору наилучшей комбинации из конечного числа вариантов: что куда поставить, кого когда назначить, что включить в набор. Такие задачи записываются матрицей QUBO и решаются отжигом:
an = lab.anneal
Q = an.build("max_cut", edges=[(0, 1), (1, 2), (2, 0), (2, 3)])
answer = an.solve(Q["Q"], method="sa", budget=2000)
answer.chosen # какие вершины попали в разрез
Сборщик an.build знает типовые постановки и избавляет от ручного
вывода матрицы, а решать можно и своим QUBO. Как записать в ту же форму житейскую
задачу целиком, со штрафами и проверкой кодирования, показывают два сквозных
разбора: 09-workshop-layout про расстановку оборудования в цехе и
11-maintenance-schedule про график ремонтов, где появляются
ограничения-неравенства. Основы в 05-qubo-annealing, серверные
решатели с бюджетом в 07-anneal-server.
Классические эталоны
Ответ квантового способа ничего не значит, пока его не с чем сравнить. Поэтому рядом с симуляторами стоят классические решатели, и сравнение делается в том же блокноте, без переключения инструментов:
cl = lab.classical
exact = cl.tfim_ground(n_sites=16, h=1.0, method="exact") # точная диагонализация
dmrg = cl.tfim_ground(n_sites=64, h=1.0, method="dmrg") # тензорная сеть
dmrg["energy"]
Образец рабочего процесса: посчитать вариационным алгоритмом, посчитать
эталоном, честь по чести положить оба ответа рядом и сделать вывод. Так построен
блокнот 03-vqe-vs-dmrg, и так же устроены оба сквозных разбора: в
задаче про цех квантовый ответ сверяется с точным переборщиком, и по этой сверке
видно, где выигрыш настоящий, а где показалось.
Квантовая химия
В среде стоит полный химический стек: интегралы считает химический пакет, из них строится гамильтониан молекулы, дальше он переводится в кубиты и решается вариационно. Всё от начала до конца, без подставленных чисел:
from openfermion.chem import MolecularData
from openfermion.transforms import get_fermion_operator, jordan_wigner
from openfermionpyscf import run_pyscf
geometry = [("H", (0, 0, 0)), ("H", (0, 0, 0.74))]
molecule = run_pyscf(MolecularData(geometry, "sto-3g", multiplicity=1),
run_scf=True, run_fci=True)
molecule.hf_energy # грубое приближение, с которого начинают
molecule.fci_energy # точное значение, эталон
qubit_op = jordan_wigner(get_fermion_operator(molecule.get_molecular_hamiltonian()))
Одна точка на четырёх кубитах считается в среде за секунды. Целая кривая
диссоциации, где расстояние между атомами пробегает десятки значений, уходит одной
командой на арендованные машины, об этом ниже. Вариационный расчёт против эталона
разобран в 03-vqe-vs-dmrg.
Коррекция ошибок
Логический кубит собирается из многих физических, и главный вопрос здесь звучит так: начиная с какого уровня физического шума избыточность начинает помогать, а не вредить. Чтобы это увидеть, нужны миллионы запусков, и в среде для этого стоят профильные инструменты:
import stim
circuit = stim.Circuit.generated(
"surface_code:rotated_memory_z",
distance=3, rounds=3, after_clifford_depolarization=0.01)
sampler = circuit.compile_detector_sampler()
shots = sampler.sample(10_000)
Декодирование берёт на себя pymatching. Десятки тысяч запусков
среда считает сама; миллионы, нужные для порога кода, отправляются на арендованные
машины работой qec_shots, пример есть в 14-heavy-batch.
Машины в аренду
Рано или поздно задача перестаёт помещаться: вектор состояния на тридцати кубитах требует шестнадцать гигабайт, кривая из десятков точек считается часами, порогу кода нужны миллионы запусков. Для этого есть арендованные машины: под вашу работу поднимаются машины нужного размера, считают её по частям сразу на нескольких, отдают результат и гасятся. Вы платите за время машин по факту, и короткая работа стоит один токен, потому что столько времени и занимает.
Пачка работ
Форма у всей тяжёлой работы одна: она режется на независимые единицы. Виды работ такие:
| Работа | Что считает | Единица |
|---|---|---|
circuit_run | одну большую схему, до 31 кубита | вся схема целиком |
qaoa_scan | сетку параметров QAOA | точка сетки |
noise_trajectories | траектории схемы под шумом | траектория |
sa_restarts | рестарты отжига на одной задаче | рестарт |
gbs_samples | выборки бозонного сэмплера | пачка выборок |
qec_shots | запуски кода коррекции с декодированием | пачка запусков |
dmrg_ground | основное состояние цепочки вдоль сетки поля | точка сетки |
molecule_curve | энергию молекулы вдоль межатомного расстояния | точка кривой |
Вызов одинаковый для всех, меняются имя и параметры. Вот кривая диссоциации водорода на двадцати четырёх расстояниях:
job = lab.heavy("molecule_curve", units=24,
params={"molecule": "H2", "r_from": 0.35, "r_to": 2.5, "vqe": True})
job["points"] # по каждой точке: приближение, эталон и вариационный ответ
job["cost_tokens"] # сколько списано со счёта, в токенах
Такой прогон занимает пару минут на двух машинах и обходится примерно в пять
токенов. Не хочется ждать в блокноте: поставьте работу с wait=False,
заберите позже через lab.scan_result(job_id), а передумали, так
lab.cancel(job_id) погасит машины и выставит счёт только за
потраченное время. Все виды работ с примерами собраны в
14-heavy-batch.
Одна большая схема
Схема, которой не хватает памяти среды, отправляется целиком: под неё подбирается машина с нужной памятью по числу кубитов в самой схеме.
result = lab.simulate_heavy(qc, shots=4096)
Сессия для вариационных циклов
Вариационный алгоритм живёт итерациями: оптимизатор у вас в блокноте предлагает параметры, схема считается, ответ возвращается оптимизатору. Поднимать машину под каждый шаг дольше, чем считать, поэтому есть сессия: машина держится между итерациями, и шаги идут один за другим.
with lab.heavy_session(n_qubits=26) as s:
for params in optimizer:
counts = s.iterate(circuit_for(params))["counts"]
Время идёт, пока сессия открыта, поэтому её надо закрывать; менеджер контекста
делает это сам, а забытая сессия закрывается по простою через четверть часа.
Разбор в 15-heavy-session.
Очередь: работа принимается всегда
Если свободных машин нет, постановка не падает: работа встаёт в живую
очередь и стартует сама, когда место освободится. В её статусе видно место в
очереди, занятость ядер и верхнюю границу ожидания; стояние бесплатно, деньги
списываются только со старта машин. Передумали: lab.cancel снимает
работу из очереди без единого токена.
Где всё это видно, не заглядывая в код: значок в шапке среды
рядом с вашим аватаром. Красный «считается» горит, пока идёт платный счёт,
жёлтый «в очереди» висит, пока работа ждёт машин, а если значка нет, ничего не
тратится. Подробности в профиле: блок «Сейчас в работе» стоит первым, у каждой
работы там виден её номер для lab.scan_result и
lab.cancel.
Машины прерываемые, поэтому дешёвые: облако вправе отнять их посреди счёта. Недосчитанная часть пересдаётся сама, за отнятое вы не платите, снаружи это выглядит как «работа идёт дольше обычного».
Арендованные машины видны в общем списке как heavy-par вместе с
ценой часа машины и перечнем работ: lab.backends() покажет и это.
Отказ случается до денег: работа, которая заведомо не поместится в память или
для которой не хватает средств, отклоняется сразу, а не после получаса
оплаченного счёта.
Деньги и профиль
Бесплатно всё, из чего состоит повседневная работа: среда, симуляторы, модели шума, двойники чипов, классические решатели, фотоника, отжиг и все примеры. Платных вещей две: прогон на настоящем квантовом железе и счёт на арендованных машинах.
Деньги списываются со счёта по факту: за арендованные машины по времени их жизни от подъёма до гашения, за железо по прейскуранту производителя. Подписки нет, минимального чека нет. Цена каждого прогона видна в профиле сразу после его завершения, строкой с тем, что считалось, сколько времени заняло и во что обошлось. Работа, потерянная по вине площадки, не оплачивается: за отозванную машину часть пересчитывается, а несостоявшийся прогон возвращает залог.
Профиль открывается из среды: меню «Файл → Панель управления Hub», кнопка «Открыть профиль». Там баланс, история прогонов с ценами, движение средств и пополнение.
Какую дорогу выбрать
| Задача | Инструмент | Где разобрано |
|---|---|---|
| Проверить идею на маленькой схеме | lab.simulate | 01, 08 |
| Узнать, что получится на конкретном чипе | двойник: lab.properties(...).twin() | 10 |
| Запустить на настоящем железе | lab.run на бэкенд вида hardware | 02 |
| Оптимизационная задача из жизни | QUBO и отжиг: lab.anneal | 05, 09, 11 |
| Сравнить квантовый ответ с классикой | lab.classical | 03, 09 |
| Фотонные схемы и выборки | lab.photonic | 04, 06 |
| Молекула: энергия и кривая связи | химический стек, затем molecule_curve | 03, 14 |
| Порог кода коррекции | stim в среде, затем qec_shots | 14 |
| Схема больше 25 кубитов | lab.simulate_heavy | 13 |
| Сетка параметров, траектории, рестарты | lab.heavy | 14 |
| Вариационный цикл на большой схеме | lab.heavy_session | 15 |
Если что-то пошло не так
- Среда не отвечает или зависла. «Файл → Панель управления Hub», остановите и запустите среду заново. Файлы не пострадают.
- Вызов
lab.*говорит про вход. Ключ среды живёт двенадцать часов; перезапуск среды выдаёт свежий. - Не хватает памяти. Ядро блокнота падает молча, когда задача упирается в 2 ГБ. Уменьшите задачу или отправьте её на арендованные машины.
- Поставленная работа считается дольше ожидаемого. Сначала
взгляните на значок в шапке: жёлтый значит, что работа ещё ждёт машин в
очереди, и это бесплатно. Состояние с причиной видно через
lab.scan_result(job_id), номер работы есть в профиле, аlab.cancel(job_id)останавливает её и берёт деньги только за прошедшее время. - Вопрос, на который здесь нет ответа. Напишите в тот же чат бота, через который входите: это и есть дверь к нам.
Лаборатория это часть проекта Учи Кванты!. Руководство обновляется вместе с самой Лабораторией.