Mulai dengan OBP
Versi paket
Kode di halaman ini dikembangkan menggunakan persyaratan berikut. Kami merekomendasikan menggunakan versi ini atau yang lebih baru.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-addon-utils~=0.3.0
qiskit-addon-obp~=0.3.0
Saat kamu mempersiapkan beban kerja kuantum dengan operator backpropagation (OBP), pertama kamu harus membuat pilihan "circuit slices", dan kedua, kamu perlu menentukan ambang batas pemotongan atau "error budget" untuk menghapus suku-suku dengan koefisien kecil dalam operator yang telah di-backpropagate sekaligus menetapkan batas atas untuk ukuran keseluruhan operator yang di-backpropagate. Selama backpropagation, jumlah suku dalam operator Circuit -Qubit akan mendekati dengan cepat pada skenario kasus terburuk. Panduan ini menunjukkan langkah-langkah yang terlibat dalam menerapkan OBP ke beban kerja kuantum.
Komponen utama dari paket qiskit-addons-obp adalah fungsi backpropagate(). Fungsi ini menerima argumen untuk observabel akhir yang akan direkonstruksi, sekumpulan circuit slices untuk dihitung secara klasik, dan secara opsional, TruncationErrorBudget atau OperatorBudget untuk memberikan batasan pada pemotongan yang dilakukan. Setelah semuanya ditentukan, operator yang di-backpropagate secara klasik dihitung secara iteratif dengan menerapkan gerbang dari setiap slice, , dengan cara berikut:
di mana adalah jumlah total slice dan merepresentasikan satu slice dari Circuit. Contoh ini menggunakan paket qiskit-addons-utils untuk mempersiapkan circuit slices serta menghasilkan circuit contoh.
Untuk memulai, pertimbangkan evolusi waktu dari rantai Heisenberg XYZ. Hamiltonian ini memiliki bentuk
dan nilai ekspektasi yang akan diukur adalah .
Cuplikan kode berikut menghasilkan Hamiltonian dalam bentuk SparsePauliOp dengan menggunakan modul qiskit_addons_utils.problem_generators dan sebuah CouplingMap. Atur konstanta kopling ke , , dan medan magnet eksternal ke , , , lalu buat Circuit yang memodelkan evolusi waktunya.
# Added by doQumentation β required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-obp qiskit-addon-utils qiskit-ibm-runtime
import numpy as np
from qiskit.transpiler import CouplingMap
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2
from qiskit.primitives import StatevectorEstimator
from qiskit.quantum_info import SparsePauliOp
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import slice_by_gate_types
from qiskit_addon_obp.utils.simplify import OperatorBudget
from qiskit_addon_obp.utils.truncating import setup_budget
from qiskit_addon_obp import backpropagate
from qiskit_addon_utils.slicing import combine_slices
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
# Choose a 10-qubit linear chain on this coupling map
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 5, 12, 8, 18]
)
# Get a qubit operator describing the Heisenberg XYZ model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),
ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),
)
# we evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=2), time=0.2
)
circuit.draw("mpl")
Siapkan input untuk backpropagateβ
Selanjutnya, buat circuit slices untuk backpropagation. Secara umum, pilihan cara memotong dapat berdampak pada seberapa baik backpropagation bekerja untuk masalah tertentu. Di sini, kelompokkan gerbang dengan tipe yang sama ke dalam slice menggunakan fungsi qiskit_addons_utils.slice_by_gate_types.
slices = slice_by_gate_types(circuit)
print(f"Separated the circuit into {len(slices)} slices.")
Separated the circuit into 18 slices.
Setelah slice dibuat, tentukan OperatorBudget untuk memberikan kondisi kepada fungsi backpropagate() kapan harus berhenti mem-backpropagate operator dan mencegah overhead klasik tumbuh lebih lanjut. Kamu juga bisa menentukan error budget pemotongan untuk setiap slice di mana suku-suku Pauli dengan koefisien kecil akan dipotong dari setiap slice sampai error budget habis. Sisa budget kemudian akan ditambahkan ke budget slice berikutnya.
Di sini, tentukan bahwa backpropagation harus berhenti ketika jumlah grup Pauli yang komuting secara qubit-wise dalam operator melebihi , dan alokasikan error budget sebesar untuk setiap slice.
op_budget = OperatorBudget(max_qwc_groups=8)
truncation_error_budget = setup_budget(max_error_per_slice=0.005)
Backpropagate sliceβ
Pada langkah ini kamu akan mendefinisikan observabel akhir yang akan diukur dan menjalankan backpropagation di setiap slice. Fungsi backpropagate() mengembalikan tiga output: observabel yang di-backpropagate, sisa circuit slices yang tidak di-backpropagate (dan yang harus dijalankan di hardware kuantum), serta metadata tentang backpropagation.
Perlu diperhatikan bahwa baik OperatorBudget maupun TruncationErrorBudget adalah parameter opsional untuk metode backpropagate(). Secara umum, pilihan terbaik untuk keduanya harus dipilih secara heuristik dan memerlukan sejumlah eksperimen. Dalam contoh ini kita akan mem-backpropagate dengan dan tanpa TruncationErrorBudget.
Secara default, backpropagate() menggunakan norma dari koefisien yang dipotong untuk membatasi total error yang ditimbulkan dari pemotongan, tetapi lainnya bisa digunakan jika kamu ingin mengubah cara error pemotongan dihitung.
# Specify a single-qubit observable
observable = SparsePauliOp("IIIIIIIIIZ")
# Backpropagate without the truncation error budget
backpropagated_observable, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=op_budget,
)
# Recombine the slices remaining after backpropagation
bp_circuit = combine_slices(remaining_slices, include_barriers=True)
print(f"Backpropagated {metadata.num_backpropagated_slices} slices.")
print(
f"New observable has {len(backpropagated_observable.paulis)} terms, which can be combined into "
f"{len(backpropagated_observable.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata.accumulated_error(0):.3e}"
)
print(
f"Note that backpropagating one more slice would result in {metadata.backpropagation_history[-1].num_paulis[0]} terms "
f"across {metadata.backpropagation_history[-1].num_qwc_groups} groups."
)
Backpropagated 7 slices.
New observable has 18 terms, which can be combined into 8 groups.
After truncation, the error in our observable is bounded by 0.000e+00
Note that backpropagating one more slice would result in 27 terms across 12 groups.
print(
"The remaining circuit after backpropagation without truncation looks as follows:"
)
bp_circuit.draw("mpl", scale=0.6)
The remaining circuit after backpropagation without truncation looks as follows:
Cuplikan kode di bawah ini mem-backpropagate Circuit dengan error budget pemotongan.
# Backpropagate *with* the truncation error budget
backpropagated_observable_trunc, remaining_slices_trunc, metadata_trunc = (
backpropagate(
observable,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
)
# Recombine the slices remaining after backpropagation
bp_circuit_trunc = combine_slices(
remaining_slices_trunc, include_barriers=True
)
print(f"Backpropagated {metadata_trunc.num_backpropagated_slices} slices.")
print(
f"New observable has {len(backpropagated_observable_trunc.paulis)} terms, which can be combined into "
f"{len(backpropagated_observable_trunc.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata_trunc.accumulated_error(0):.3e}"
)
print(
f"Note that backpropagating one more slice would result in {metadata_trunc.backpropagation_history[-1].num_paulis[0]} terms "
f"across {metadata_trunc.backpropagation_history[-1].num_qwc_groups} groups."
)
Backpropagated 10 slices.
New observable has 19 terms, which can be combined into 8 groups.
After truncation, the error in our observable is bounded by 4.933e-02
Note that backpropagating one more slice would result in 27 terms across 13 groups.
print(
"The remaining circuit after backpropagation with truncation looks as follows:"
)
bp_circuit_trunc.draw("mpl", scale=0.6)
The remaining circuit after backpropagation with truncation looks as follows:
Transpilasi dan jalankan beban kerja kuantumβ
Sekarang setelah kamu mem-backpropagate operator, kamu bisa mengeksekusi bagian Circuit yang tersisa di QPU. Beban kerja kuantum, menggunakan Estimator, harus menyertakan Circuit bp_circuit_trunc dan harus mengukur operator yang di-backpropagate backpropagated_observable
Untuk menunjukkan efektivitas OBP secara mandiri, cuplikan kode berikut mentranspilasi Circuit asli maupun yang di-backpropagate (dengan dan tanpa pemotongan) serta mensimulasikan Circuit secara klasik menggunakan StatevectorEstimator.
# Specify a backend and a pass manager for transpilation
backend = FakeMelbourneV2()
# pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
# Transpile original experiment
circuit_isa = pm.run(circuit)
observable_isa = observable.apply_layout(circuit_isa.layout)
# Transpile backpropagated experiment without truncation
bp_circuit_isa = pm.run(bp_circuit)
bp_obs_isa = backpropagated_observable.apply_layout(bp_circuit_isa.layout)
# Transpile the backpropagated experiment with truncated observable terms
bp_circuit_trunc_isa = pm.run(bp_circuit_trunc)
bp_obs_trunc_isa = backpropagated_observable_trunc.apply_layout(
bp_circuit_trunc_isa.layout
)
estimator = StatevectorEstimator()
# Run the experiments using the exact statevector estimator
result_exact = (
estimator.run([(circuit, observable)]).result()[0].data.evs.item()
)
result_bp = (
estimator.run([(bp_circuit_isa, bp_obs_isa)]).result()[0].data.evs.item()
)
result_bp_trunc = (
estimator.run([(bp_circuit_trunc_isa, bp_obs_trunc_isa)])
.result()[0]
.data.evs.item()
)
print(f"Exact expectation value: {result_exact}")
print(f"Backpropagated expectation value without truncation: {result_bp}")
print(f"Backpropagated expectation value with truncation: {result_bp_trunc}")
print(
f" - Expected Error for truncated observable: {metadata_trunc.accumulated_error(0):.3e}"
)
print(
f" - Observed Error for truncated observable: {abs(result_exact - result_bp_trunc):.3e}"
)
Exact expectation value: 0.8854160687717517
Backpropagated expectation value without truncation: 0.8854160687717533
Backpropagated expectation value with truncation: 0.8850236647156081
- Expected Error for truncated observable: 4.933e-02
- Observed Error for truncated observable: 3.924e-04
Terakhir, cuplikan kode berikut akan mentranspilasi dan mengeksekusi Circuit yang di-backpropagate di QPU (dengan dan tanpa pemotongan).
# Specify a backend and a pass manager for transpilation
service = QiskitRuntimeService()
backend = service.least_busy()
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
# Transpile backpropagated experiment without truncation
bp_circuit_isa = pm.run(bp_circuit)
bp_obs_isa = backpropagated_observable.apply_layout(bp_circuit_isa.layout)
# Transpile the backpropagated experiment with truncated observable terms
bp_circuit_trunc_isa = pm.run(bp_circuit_trunc)
bp_obs_trunc_isa = backpropagated_observable_trunc.apply_layout(
bp_circuit_trunc_isa.layout
)
# Run the experiments using Estimator primitive
estimator = EstimatorV2(mode=backend)
result_bp_qpu = (
estimator.run([(bp_circuit_isa, bp_obs_isa)]).result()[0].data.evs.item()
)
result_bp_trunc_qpu = (
estimator.run([(bp_circuit_trunc_isa, bp_obs_trunc_isa)])
.result()[0]
.data.evs.item()
)
print(f"Exact expectation value: {result_exact}")
print(f"Backpropagated expectation value without truncation: {result_bp_qpu}")
print(
f"Backpropagated expectation value with truncation: {result_bp_trunc_qpu}"
)
print(
f" - Observed Error for observable without truncation: {abs(result_exact - result_bp_qpu):.3e}"
)
print(
f" - Observed Error for truncated observable: {abs(result_exact - result_bp_trunc_qpu):.3e}"
)
Exact expectation value: 0.8854160687717517
Backpropagated expectation value without truncation: 0.8790435084647706
Backpropagated expectation value with truncation: 0.8759838342768448
- Observed Error for observable without truncation: 6.373e-03
- Observed Error for truncated observable: 9.432e-03
Langkah selanjutnyaβ
- Coba tutorial tentang menggunakan OBP untuk meningkatkan nilai ekspektasi.