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

Міграція на примітиви Qiskit Runtime V2

попередження

Оригінальні примітиви (так звані примітиви V1) — V1 Sampler і V1 Estimator — оголошено застарілими у qiskit-ibm-runtime 0.23. Підтримку їх було припинено 15 серпня 2024 року.

У зв'язку з оголошенням примітивів V1 застарілими, весь код слід перенести на інтерфейси V2. Цей посібник описує, що змінилося у примітивах Qiskit Runtime V2 (доступних починаючи з qiskit-ibm-runtime 0.21.0) і чому, детально описує кожен новий примітив та надає приклади для міграції коду з застарілих примітивів на примітиви V2. Усі приклади в посібнику використовують примітиви Qiskit Runtime, але загалом ті самі зміни стосуються й інших реалізацій примітивів. Функції, унікальні для Qiskit Runtime, — такі як пом'якшення помилок — залишаються ексклюзивними для Qiskit Runtime.

Для отримання інформації про зміни в еталонних примітивах Qiskit (тепер відомих як statevector примітиви) перегляньте розділ qiskit.primitives на сторінці змін Qiskit 1.0. Дивись StatevectorSampler і StatevectorEstimator для довідкових реалізацій примітивів V2.

Огляд

Версія 2 примітивів вводиться з новим базовим класом для Sampler і Estimator (BaseSamplerV2 і BaseEstimatorV2), а також новими типами для їхніх вхідних і вихідних даних.

Новий інтерфейс дозволяє задавати один схему та кілька спостережуваних (якщо використовуєш Estimator) і наборів значень параметрів для цієї схеми, що дає змогу ефективно задавати перебір по наборах значень параметрів і спостережуваних. Раніше потрібно було кілька разів задавати одну й ту ж схему, щоб відповідати розміру даних для об'єднання. Крім того, хоча ти й надалі можеш використовувати resilience_level (якщо використовуєш Estimator) як простий перемикач, примітиви V2 дають тобі гнучкість для вмикання або вимикання окремих методів пом'якшення/пригнічення помилок відповідно до потреб.

Щоб скоротити загальний час виконання завдань, примітиви V2 приймають лише схеми та спостережувані, що використовують інструкції, підтримувані цільовим QPU (квантовим процесором). Такі схеми та спостережувані називаються ISA-схемами та ISA-спостережуваними (ISA — instruction set architecture, архітектура набору команд). Примітиви V2 не виконують операції розміщення, маршрутизації та трансляції. Перегляньте документацію з транспіляції для отримання інструкцій щодо перетворення схем.

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

Дивись довідку API EstimatorV2 і довідку API SamplerV2 для отримання повної інформації.

Основні зміни

Імпорт

Для зворотної сумісності потрібно явно імпортувати примітиви V2. Вказувати import <primitive>V2 as <primitive> не обов'язково, але це полегшує перехід коду на V2.

попередження

Після того, як підтримку примітивів V1 буде припинено, import <primitive> імпортуватиме версію V2 вказаного примітива.

from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler

Вхідні та вихідні дані

Вхідні дані

Обидва SamplerV2 і EstimatorV2 приймають один або кілька primitive unified blocs (PUB) як вхідні дані. Кожен PUB — це кортеж, що містить одну схему та дані, що транслюються на цю схему, — кілька спостережуваних і параметрів. Кожен PUB повертає результат.

  • Формат PUB для Sampler V2: (<circuit>, <parameter values>, <shots>), де <parameter values> і <shots> є необов'язковими.
  • Формат PUB для Estimator V2: (<circuit>, <observables>, <parameter values>, <precision>), де <parameter values> і <precision> є необов'язковими. При об'єднанні спостережуваних і значень параметрів застосовуються правила broadcasting Numpy.

Крім того, були внесені такі зміни:

  • Estimator V2 отримав аргумент precision у методі run(), що задає цільову точність оцінок математичного сподівання.
  • Sampler V2 має аргумент shots у методі run().
Приклади

Приклад Estimator V2 з використанням precision у run():

# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

Приклад Sampler V2 з використанням shots у run():

# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)

# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])

Вихідні дані

Вихідні дані тепер у форматі PubResult. PubResult — це дані та метадані, що є результатом виконання одного PUB.

  • Estimator V2 продовжує повертати математичні сподівання.

  • Частина data у PubResult Estimator V2 містить як математичні сподівання, так і стандартні похибки (stds). V1 повертав дисперсію в метаданих.

  • Sampler V2 повертає поshot-вимірювання у вигляді бітових рядків, замість квазійовірнісних розподілів з інтерфейсу V1. Бітові рядки показують результати вимірювань, зберігаючи порядок shots, у якому вони були виміряні.

  • Sampler V2 має зручні методи, такі як get_counts(), для полегшення міграції.

  • Об'єкти результатів Sampler V2 організують дані у термінах імен класичних регістрів вхідних схем, для сумісності з динамічними схемами. За замовчуванням ім'я класичного регістра — meas, як показано в наступному прикладі. При визначенні схеми, якщо ти створюєш один або кілька класичних регістрів з нестандартним іменем, використовуй це ім'я для отримання результатів. Ім'я класичного регістра можна дізнатись, запустивши <circuit_name>.cregs. Наприклад, qc.cregs.

    # Define a quantum circuit with 2 qubits
    circuit = QuantumCircuit(2)
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure_all()
    circuit.draw()
            ┌───┐      ░ ┌─┐
    q_0: ┤ H ├──■───░─┤M├───
    └───┘┌─┴─┐ ░ └╥┘┌─┐
    q_1: ─────┤ X ├─░──╫─┤M├
    └───┘ ░ ║ └╥┘
    meas: 2/══════════════╩══╩═
    0 1

Приклади Estimator (вхідні та вихідні дані)

# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values

# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs

Приклади Sampler (вхідні та вихідні дані)

  # Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists

# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()

Приклад з використанням різних вихідних регістрів

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)

circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")

Опції

Опції у примітивах V2 задаються інакше, ніж раніше:

  • SamplerV2 і EstimatorV2 тепер мають окремі класи опцій. Ти можеш переглядати доступні опції та оновлювати їхні значення під час або після ініціалізації примітива.
  • Замість методу set_options(), опції примітивів V2 мають метод update(), який застосовує зміни до атрибута options.
  • Якщо ти не вказуєш значення опції, їй присвоюється спеціальне значення Unset, і використовуються серверні значення за замовчуванням.
  • Для примітивів V2 атрибут options є типом dataclass у Python. Можна використовувати вбудований метод asdict, щоб перетворити його на словник.

Дивись довідку API для переліку доступних опцій.

from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})

# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler

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

# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})

# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True

# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))

Пом'якшення та пригнічення помилок

  • Оскільки Sampler V2 повертає зразки без постобробки, він не підтримує рівні стійкості (resilience levels).

  • Sampler V2 не підтримує optimization_level.

  • Estimator V2 припинить підтримку optimization_level приблизно 30 вересня 2024 року.

  • Estimator V2 не підтримує рівень стійкості 3. Це пов'язано з тим, що рівень 3 у V1 Estimator використовує Probabilistic Error Cancellation (PEC), що, як доведено, дає незміщені результати ціною експоненційного часу обробки. Рівень 3 було прибрано, щоб звернути увагу на цей компроміс. Однак ти все ще можеш використовувати PEC як метод пом'якшення помилок, задавши опцію pec_mitigation.

  • Estimator V2 підтримує resilience_level 0-2, як описано в наступній таблиці. Ці опції є більш просунутими порівняно з їхніми аналогами у V1. Ти також можеш явно вмикати або вимикати окремі методи пом'якшення/пригнічення помилок.

    Рівень 1Рівень 2
    Measurement twirlingMeasurement twirling
    Пом'якшення помилок зчитуванняПом'якшення помилок зчитування
    ZNE
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

# Setting options during primitive initialization
estimator = Estimator(backend)

# Set resilience_level to 0
estimator.options.resilience_level = 0

# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")

Транспіляція

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

Статус завдання

Примітиви V2 мають новий клас RuntimeJobV2, що успадковується від BasePrimitiveJob. Метод status() цього нового класу повертає рядок замість переліку JobStatus з Qiskit. Дивись довідку API RuntimeJobV2 для деталей.

job = estimator.run(...)

# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")

Кроки міграції на Estimator V2

  1. Заміни from qiskit_ibm_runtime import Estimator на from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. Видали всі оператори from qiskit_ibm_runtime import Options, оскільки клас Options не використовується примітивами V2. Натомість можна передавати опції у вигляді словника при ініціалізації класу EstimatorV2 (наприклад, estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), або задавати їх після ініціалізації:

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. Перегляньте всі підтримувані опції та внеси відповідні зміни.

  4. Згрупуй кожну схему, яку хочеш запустити, зі спостережуваними та значеннями параметрів, які хочеш застосувати до схеми, у кортеж (PUB). Наприклад, використовуй (circuit1, observable1, parameter_set1), якщо хочеш запустити circuit1 з observable1 і parameter_set1.

  5. Можливо, доведеться перетворити форму масивів спостережуваних або наборів параметрів, якщо хочеш застосувати їхній зовнішній добуток. Наприклад, масив спостережуваних форми (4, 1) і масив наборів параметрів форми (1, 6) дадуть результат (4, 6) математичних сподівань. Дивись правила broadcasting Numpy для деталей.

  6. Можна за бажанням вказати точність для конкретного PUB.

  7. Оновіть метод run() estimator для передачі списку PUB. Наприклад, run([(circuit1, observable1, parameter_set1)]). Можна за бажанням вказати precision, яка буде застосована до всіх PUB.

  8. Результати завдань Estimator V2 згруповані за PUB. Ти можеш переглянути математичне сподівання та стандартну похибку для кожного PUB, звертаючись до нього за індексом. Наприклад:

pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")

Повні приклади Estimator

Запуск одного експерименту

Використовуй Estimator для визначення математичного сподівання однієї пари схема-спостережувана.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
estimator = Estimator(backend)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

Запуск кількох експериментів в одному завданні

Використовуй Estimator для визначення математичних сподівань кількох пар схема-спостережувана.

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

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)

n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]

isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]

estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")

Запуск параметризованих схем

Використовуй Estimator для запуску кількох експериментів в одному завданні, використовуючи значення параметрів для підвищення повторного використання схем. У наступному прикладі зверни увагу, що кроки 1 і 2 однакові для V1 і V2.

import numpy as np

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem

theta = Parameter("θ")

chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)

number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]

ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]

# Step 2: Optimize problem for quantum execution.

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

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]

from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Step 3: Execute using Qiskit primitives.

# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))

estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")

Використання сесій і розширених опцій

Досліджуй сесії та розширені опції для оптимізації продуктивності схем на QPU.

обережно

Наступний блок коду повернеться з помилкою для користувачів плану Open, оскільки він використовує сесії. Завдання на плані Open можуть виконуватися лише в режимі job або режимі batch.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
estimator = Estimator()

estimator.options.resilience_level = 1

job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

# second job
print(f" > Another Expectation value: {another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")

Кроки міграції на Sampler V2

  1. Заміни from qiskit_ibm_runtime import Sampler на from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. Видали всі оператори from qiskit_ibm_runtime import Options, оскільки клас Options не використовується примітивами V2. Натомість можна передавати опції у вигляді словника при ініціалізації класу SamplerV2 (наприклад, sampler = Sampler(backend, options={"default_shots": 1024})), або задавати їх після ініціалізації:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. Перегляньте всі підтримувані опції та внеси відповідні зміни.
  4. Згрупуй кожну схему, яку хочеш запустити, зі спостережуваними та значеннями параметрів, які хочеш застосувати до схеми, у кортеж (PUB). Наприклад, використовуй (circuit1, parameter_set1), якщо хочеш запустити circuit1 з parameter_set1. За бажанням можна вказати кількість shots для конкретного PUB.
  5. Оновіть метод run() sampler для передачі списку PUB. Наприклад, run([(circuit1, parameter_set1)]). За бажанням можна вказати shots, які будуть застосовані до всіх PUB.
  6. Результати завдань Sampler V2 згруповані за PUB. Ти можеш переглянути вихідні дані для кожного PUB, звертаючись до нього за індексом. Хоча Sampler V2 повертає незважені зразки, клас результатів має зручний метод для отримання кількості відліків. Наприклад:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
примітка

Для отримання результатів потрібно знати ім'я класичного регістра. За замовчуванням він називається meas, коли використовуєш measure_all(). При визначенні схеми, якщо ти створюєш один або кілька класичних регістрів з нестандартним іменем, використовуй це ім'я для отримання результатів. Ім'я класичного регістра можна дізнатись, запустивши <circuit_name>.cregs. Наприклад, qc.cregs.

Повні приклади Sampler

Запуск одного експерименту

Використовуй Sampler для визначення кількості відліків або квазійовірнісного розподілу однієї схеми.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()

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

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()

Запуск кількох експериментів в одному завданні

Використовуй Sampler для визначення кількості відліків або квазійовірнісних розподілів кількох схем в одному завданні.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)

sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()

for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")

Запуск параметризованих схем

Запуск кількох експериментів в одному завданні, використовуючи значення параметрів для підвищення повторного використання схем.

import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()

# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]

# Step 2: Optimize problem for quantum execution.

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

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

# Step 3: Execute using Qiskit primitives.

from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")

Використання сесій і розширених опцій

Досліджуй сесії та розширені опції для оптимізації продуктивності схем на QPU.

обережно

Наступний блок коду повернеться з помилкою для користувачів плану Open, оскільки він використовує сесії. Завдання на плані Open можуть виконуватися лише в режимі job або режимі batch.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)

service = QiskitRuntimeService()

# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")

# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")

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

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