Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Розрізання вентилів для зменшення глибини схеми

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

Ось кроки, які ми виконаємо в цьому шаблоні Qiskit:

  • Крок 1: Відображення задачі на квантові схеми та оператори:

    • Відобразити гамільтоніан на квантову схему.
  • Крок 2: Оптимізація для цільового апаратного забезпечення [Використовує аддон cutting]:

    • Розрізати схему та спостережуваний оператор.
    • Транспілювати підексперименти для апаратного забезпечення.

  • Крок 3: Виконання на цільовому апаратному забезпеченні:

    • Запустити підексперименти, отримані на кроці 2, використовуючи примітив Sampler.
  • Крок 4: Постобробка результатів [Використовує аддон cutting]:

    • Об'єднати результати кроку 3 для відновлення математичного сподівання відповідного спостережуваного оператора.

Крок 1: Відображення​

Створення схеми для запуску на бекенді​

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_cutting": "qiskit-addon-cutting", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit.circuit.library import efficient_su2

circuit = efficient_su2(num_qubits=4, entanglement="circular")
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
circuit.draw("mpl", scale=0.8)

Quantum circuit diagram

Задання спостережуваного оператора​

from qiskit.quantum_info import SparsePauliOp

observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])

Крок 2: Оптимізація​

Задання бекенду​

Ти можеш надати або фейковий бекенд, або апаратний бекенд із Qiskit Runtime.

from qiskit_ibm_runtime.fake_provider import FakeManilaV2

backend = FakeManilaV2()

Транспіляція схеми, візуалізація swap-вентилів та визначення глибини​

Ми обираємо розміщення, яке вимагає двох swap-вентилів для виконання вентилів між кубітами 3 і 0 та ще двох swap-вентилів для повернення кубітів до початкових позицій.

from qiskit.transpiler import generate_preset_pass_manager

pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, initial_layout=[0, 1, 2, 3]
)

transpiled_qc = pass_manager.run(circuit)
print(f"Transpiled circuit depth: {transpiled_qc.depth(lambda x: len(x.qubits) >= 2)}")
Transpiled circuit depth: 30
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, fold=-1)

Quantum circuit diagram

Заміна віддалених вентилів на TwoQubitQPDGate шляхом задання їх індексів​

cut_gates замінить вентилі за вказаними індексами на TwoQubitQPDGate і поверне список екземплярів QPDBasis — по одному для кожного розкладу вентиля.

from qiskit_addon_cutting import cut_gates

# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(circuit.data)
if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]

# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)

qpd_circuit.draw("mpl", scale=0.8)

Quantum circuit diagram

Генерація підекспериментів для запуску на бекенді​

generate_cutting_experiments приймає схему, що містить екземпляри TwoQubitQPDGate, та спостережувані оператори у вигляді PauliList.

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

Примітка: Аргумент observables функції generate_cutting_experiments має тип PauliList. Коефіцієнти та фази доданків спостережуваного оператора ігноруються під час розкладання задачі та виконання підекспериментів. Їх можна повторно застосувати під час відновлення математичного сподівання.

import numpy as np
from qiskit_addon_cutting import generate_cutting_experiments

# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observable.paulis, num_samples=np.inf
)

Обчислення накладних витрат на вибірку для обраних розрізів​

Тут ми розрізаємо три вентилі CNOT, що дає накладні витрати на вибірку 939^3.

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

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0

Демонстрація того, що QPD-підексперименти будуть менш глибокими після розрізання віддалених вентилів​

Ось приклад довільно обраного підексперименту, згенерованого з QPD-схеми. Його глибина зменшилась більш ніж удвічі. Для відновлення математичного сподівання глибшої схеми необхідно згенерувати та оцінити велику кількість таких імовірнісних підекспериментів.

# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pass_manager.run(subexperiments[100])

print(
f"Original circuit depth after transpile: {transpiled_qc.depth(lambda x: len(x.qubits) >= 2)}"
)
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth(lambda x: len(x.qubits) >= 2)}"
)
transpiled_qpd_circuit.draw("mpl", scale=0.8, idle_wires=False, fold=-1)
Original circuit depth after transpile: 30
QPD subexperiment depth after transpile: 7

Quantum circuit diagram

Підготовка підекспериментів для бекенду​

# Transpile the subeperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pass_manager.run(subexperiments)

Крок 3: Виконання​

Запуск підекспериментів за допомогою примітиву Qiskit Runtime Sampler​

from qiskit_ibm_runtime import SamplerV2

# Set up the Qiskit Runtime Sampler primitive. For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)

# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()

Крок 4: Постобробка​

Відновлення математичного сподівання​

Відновлюємо математичні сподівання для кожного доданку спостережуваного оператора та об'єднуємо їх для відновлення математичного сподівання вихідного спостережуваного оператора.

from qiskit_addon_cutting import reconstruct_expectation_values

reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
observable.paulis,
)
# Reconstruct final expectation value
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

Порівняння відновленого математичного сподівання з точним математичним сподіванням для вихідної схеми та спостережуваного оператора​

from qiskit_aer.primitives import EstimatorV2

estimator = EstimatorV2()
exact_expval = estimator.run([(circuit, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 0.44018555
Exact expectation value: 0.50497603
Error in estimation: -0.06479049
Relative error in estimation: -0.12830408