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

Вступ до примітивів

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Нова модель виконання, зараз у бета-версії

Бета-версія нової моделі виконання вже доступна. Направлена модель виконання забезпечує більшу гнучкість при налаштуванні робочого процесу пом'якшення помилок. Дивись посібник Directed execution model для отримання додаткової інформації.

Чому Qiskit запровадив примітиви?

Подібно до перших днів класичних комп'ютерів, коли розробникам доводилося безпосередньо маніпулювати регістрами CPU, ранній інтерфейс до QPU просто повертав необроблені дані з керуючої електроніки. Це не було великою проблемою, поки QPU знаходилися в лабораторіях і дозволяли прямий доступ лише дослідникам. Усвідомлюючи, що більшість розробників не зобов'язана і не повинна розбиратися в перетворенні таких сирих даних на нулі та одиниці, Qiskit запровадив backend.run — першу абстракцію для доступу до QPU в хмарі. Це дозволило розробникам працювати зі звичним форматом даних і зосередитися на загальній картині.

Коли доступ до QPU став більш поширеним і розроблялося дедалі більше квантових алгоритмів, знову виникла потреба в абстракції вищого рівня. У відповідь Qiskit запровадив інтерфейс примітивів, оптимізований для двох основних завдань у розробці квантових алгоритмів: оцінки очікуваних значень (Estimator) і вибірки схем (Sampler). Мета знову полягає в тому, щоб допомогти розробникам більше зосереджуватися на інноваціях і менше на перетворенні даних. Інтерфейс примітивів замінює інтерфейс backend.run, оскільки Sampler забезпечує той самий прямий доступ до апаратного забезпечення, який пропонував backend.run.

Що таке примітив?

Обчислювальні системи побудовані на кількох рівнях абстракції. Абстракції дозволяють тобі зосередитися на певному рівні деталей, необхідному для виконання поточного завдання. Чим ближче до апаратного забезпечення, тим нижчий рівень абстракції потрібен (наприклад, може знадобитися переміщувати або маніпулювати даними на рівні інструкцій CPU). Чим складніше завдання, яке ти хочеш виконати, тим вищим буде рівень абстракцій (наприклад, ти можеш використовувати програмну бібліотеку для виконання алгебраїчних обчислень).

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

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

Два найпоширеніші завдання для квантових комп'ютерів — це вибірка квантових станів і обчислення очікуваних значень. Ці завдання мотивували розробку примітивів Qiskit: Estimator та Sampler.

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

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

Визначення та реалізації примітивів

Існує два типи примітивів Qiskit: базові класи та їхні реалізації. Примітиви Qiskit визначені відкритими базовими класами примітивів, що знаходяться в Qiskit SDK (у модулі qiskit.primitives). Провайдери (наприклад, Qiskit Runtime) можуть використовувати ці базові класи для створення власних реалізацій Sampler і Estimator. Більшість користувачів взаємодіятимуть із реалізаціями провайдерів, а не з базовими примітивами.

Базові класи

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

Реалізації

Ось реалізації базових класів примітивів:

  • Примітиви Qiskit Runtime (EstimatorV2 та SamplerV2) забезпечують більш досконалу реалізацію (наприклад, включаючи пом'якшення помилок) як хмарний сервіс. Ця реалізація базових примітивів використовується для доступу до апаратного забезпечення IBM Quantum®. Доступ до них здійснюється через IBM Qiskit Runtime.

  • StatevectorEstimator та StatevectorSampler — еталонні реалізації примітивів, що використовують симулятор, вбудований у Qiskit. Вони побудовані за допомогою модуля Qiskit quantum_info, що дає результати на основі ідеальних симуляцій вектора стану. Доступ до них здійснюється через Qiskit.

  • BackendEstimatorV2 та BackendSamplerV2 — ці класи дозволяють «загорнути» будь-який ресурс квантових обчислень у примітив. Це дає змогу писати код у стилі примітивів для провайдерів, які ще не мають інтерфейсу на основі примітивів. Ці класи можна використовувати так само, як звичайні Sampler і Estimator, за винятком того, що їх слід ініціалізувати з додатковим аргументом backend для вибору квантового комп'ютера для запуску. Доступ до них здійснюється через Qiskit.

Переваги примітивів Qiskit

Завдяки примітивам користувачі Qiskit можуть писати квантовий код для конкретного QPU без необхідності явно керувати кожною деталлю. Крім того, завдяки додатковому рівню абстракції ти можеш простіше отримати доступ до розширених можливостей апаратного забезпечення конкретного провайдера. Наприклад, із примітивами Qiskit Runtime ти можеш скористатися останніми досягненнями в пом'якшенні та придушенні помилок, перемикаючи такі параметри, як resilience_level примітиву, замість того щоб самостійно реалізовувати ці техніки.

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

Деталі примітивів

Як описано раніше, всі примітиви створюються з базових класів; тому вони мають однакову загальну структуру та використання. Наприклад, формат вхідних даних для всіх примітивів Estimator однаковий. Однак існують відмінності в реалізаціях, що робить їх унікальними.

примітка

Оскільки більшість користувачів мають доступ до примітивів Qiskit Runtime, приклади в решті цього розділу засновані на примітивах Qiskit Runtime.

Estimator

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

Вхідні дані — це масив PUB-ів. Кожен PUB має формат:

(<single circuit>, <one or more observables>, <optional one or more parameter values>, <optional precision>),

де необов'язкові parameter values можуть бути списком або одним параметром. Різні реалізації Estimator підтримують різні параметри конфігурації. Якщо вхідні дані містять вимірювання, вони ігноруються.

Вихідні дані — це PubResult, що містить обчислені очікувані значення для кожної пари та їх стандартні похибки у вигляді PubResult. Кожен PubResult містить як дані, так і метадані.

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

Приклад:

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# This cell is hidden from users, it creates the circuits and observables to run

from qiskit_ibm_runtime import EstimatorV2, SamplerV2, QiskitRuntimeService
from qiskit.circuit.random import random_circuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np

service = QiskitRuntimeService()
backend = service.least_busy()
phi = Parameter("phi")

circuit1 = random_circuit(10, 5, seed=12345)
circuit1.rzz(phi, 1, 2)
observable1 = SparsePauliOp.from_sparse_list(
[("ZXYZ", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values1 = np.random.uniform(size=5).T

circuit2 = random_circuit(10, 5, seed=12345)
circuit2.rzz(phi, 1, 2)
observable2 = SparsePauliOp.from_sparse_list(
[("XZYX", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values2 = np.random.uniform(size=5).T

shots1 = 164
shots2 = 1024

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
circuit1 = pm.run(circuit1)
circuit2 = pm.run(circuit2)
observable1 = observable1.apply_layout(circuit1.layout)
observable2 = observable2.apply_layout(circuit2.layout)
estimator = EstimatorV2(mode=backend)
estimator_job = estimator.run(
[
(circuit1, observable1, param_values1),
(circuit2, observable2, param_values2),
]
)

Sampler

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

Вхідні дані — це один або кілька PUB-ів, у форматі:

(<single circuit>, <one or more optional parameter value>, <optional shots>),

де може бути кілька елементів parameter values, і кожен елемент може бути або масивом, або одним параметром, залежно від вибраної схеми. Крім того, вхідні дані повинні містити вимірювання.

Вихідні дані — це лічильники або вимірювання на кожен знімок у вигляді об'єктів PubResult без ваг. Однак клас результатів має методи для повернення зважених зразків, наприклад лічильників. Повні деталі дивись у розділі Вхідні та вихідні дані примітивів.

Приклад:

# This cell is hidden from users, add measurement instructions to circuits
circuit1.measure_active()
circuit2.measure_active()
sampler = SamplerV2(mode=backend)
sampler_job = sampler.run(
[
(circuit1, param_values1, shots1),
(circuit2, param_values2, shots2),
]
)

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

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