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

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

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

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

  • Крок 1: Відображення задачі на квантові схеми та оператори:
    • Відобразити гамільтоніан на квантову схему.
  • Крок 2: Оптимізація для цільового апаратного забезпечення [Використовує аддон cutting]:
    • Розрізати схему та спостережуваний оператор.
    • Транспілювати підексперименти для апаратного забезпечення.
  • Крок 3: Виконання на цільовому апаратному забезпеченні:
    • Запустити підексперименти, отримані на кроці 2, використовуючи примітив Sampler.
  • Крок 4: Постобробка результатів [Використовує аддон cutting]:
    • Об'єднати результати кроку 3 для відновлення математичного сподівання відповідного спостережуваного оператора.

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

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

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
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