Magsimula sa OBP
Mga bersyon ng package
Ang code sa pahinang ito ay ginawa gamit ang mga sumusunod na kinakailangan. Inirerekomenda naming gamitin ang mga bersyong ito o mas bago pa.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-addon-utils~=0.3.0
qiskit-addon-obp~=0.3.0
Kapag inihahanda mo ang isang quantum workload gamit ang operator backpropagation (OBP), una kang kailangang pumili ng "circuit slices", at pangalawa, dapat kang magtakda ng truncation threshold o "error budget" upang alisin ang mga termino na may maliliit na coefficient sa backpropagated operator at magtakda ng upper bound sa kabuuang laki ng backpropagated operator. Sa panahon ng backpropagation, ang bilang ng mga termino sa operator ng isang -qubit circuit ay mabilis na lalapit sa sa pinakamasamang sitwasyon. Ipinapakita ng gabay na ito ang mga hakbang na kasangkot sa pag-apply ng OBP sa isang quantum workload.
Ang pangunahing bahagi ng qiskit-addons-obp package ay ang backpropagate() function. Tinatanggap nito ang mga argumento para sa panghuling observable na ire-reconstruct, isang set ng mga circuit slice na kukumputin nang klasikal, at, opsyonal, isang TruncationErrorBudget o OperatorBudget upang magbigay ng mga hadlang sa truncation na ginagawa. Kapag natukoy na ang mga ito, ang klasikal na computed na backpropagated operator ay kinakalkula nang paulit-ulit sa pamamagitan ng pag-apply ng mga gate mula sa bawat slice, , sa sumusunod na paraan:
kung saan ang ay ang kabuuang bilang ng mga slice at ang ay kumakatawan sa isang solong slice ng Circuit. Ginagamit ng halimbawang ito ang qiskit-addons-utils package upang ihanda ang mga circuit slice pati na rin upang bumuo ng halimbawang circuit.
Para magsimula, isaalang-alang ang time evolution ng isang Heisenberg XYZ chain. Ang Hamiltonian na ito ay may anyo
at ang expectation value na susukathin ay .
Ang sumusunod na code snippet ay bumubuo ng Hamiltonian sa anyo ng isang SparsePauliOp sa pamamagitan ng paggamit ng qiskit_addons_utils.problem_generators module at isang CouplingMap. Itakda ang mga coupling constant sa , , at ang mga panlabas na magnetic field sa , , , at pagkatapos ay bumuo ng circuit na nagmomodelo ng time evolution nito.
# 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")
Ihanda ang mga input para sa backpropagateβ
Susunod, buuin ang mga circuit slice para sa backpropagation. Sa pangkalahatan, ang pagpili kung paano mag-slice ay maaaring makaapekto sa gana ng backpropagation para sa isang partikular na problema. Dito, i-grupo ang mga gate ng parehong uri sa mga slice gamit ang qiskit_addons_utils.slice_by_gate_types function.
slices = slice_by_gate_types(circuit)
print(f"Separated the circuit into {len(slices)} slices.")
Separated the circuit into 18 slices.
Kapag nabuo na ang mga slice, magtakda ng OperatorBudget upang bigyan ang backpropagate() function ng kondisyon para tumigil sa backpropagation ng operator at pigilan ang classical overhead na lumago pa. Maaari ka ring magtakda ng truncation error budget para sa bawat slice kung saan ang mga Pauli term na may maliliit na coefficient ay ita-truncate mula sa bawat slice hanggang mapuno ang error budget. Ang anumang natitira pang budget ay idadagdag sa budget ng susunod na slice.
Dito, itakda na ang backpropagation ay titigil kapag ang bilang ng qubit-wise commuting Pauli group sa operator ay lumagpas sa , at mag-allocate ng error budget na para sa bawat slice.
op_budget = OperatorBudget(max_qwc_groups=8)
truncation_error_budget = setup_budget(max_error_per_slice=0.005)
Mag-backpropagate ng mga sliceβ
Sa hakbang na ito, tutukuyin mo ang panghuling observable na susukathin at patatakbuhin ang backpropagation sa bawat slice. Ang backpropagate() function ay nagbabalik ng tatlong output: ang backpropagated observable, ang mga natitirang circuit slice na hindi na-backpropagate (at dapat na patakbuhin sa quantum hardware), at metadata tungkol sa backpropagation.
Tandaan na ang parehong OperatorBudget at TruncationErrorBudget ay opsyonal na parameter para sa backpropagate() method. Sa pangkalahatan, ang pinakamainam na pagpili para sa dalawa ay dapat piliin nang heuristically at nangangailangan ng ilang halaga ng eksperimento. Sa halimbawang ito, mag-ba-backpropagate tayo nang may at walang TruncationErrorBudget.
Bilang default, ang backpropagate() ay gumagamit ng norm ng mga na-truncate na coefficient upang limitahan ang kabuuang error na nagmumula sa truncation, ngunit maaaring gamitin ang iba pang kung nais mong baguhin ang paraan ng pagkalkula ng truncation error.
# 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:
Ang mga sumusunod na code snippet ay nag-ba-backpropagate ng circuit na may truncation error budget.
# 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:
I-transpile at isagawa ang quantum workloadβ
Ngayon na na-backpropagate mo na ang operator, maaari mong isagawa ang natitirang bahagi ng Circuit sa isang QPU. Ang quantum workload, gamit ang Estimator, ay dapat na isama ang bp_circuit_trunc circuit at kailangang sukatin ang backpropagated operator na backpropagated_observable.
Upang ipakita ang bisa ng OBP nang mag-isa, ang sumusunod na code snippet ay nagta-transpile ng parehong orihinal at backpropagated circuit (na may at walang truncation) at nag-si-simulate ng mga circuit nang klasikal gamit ang 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
Sa wakas, ang sumusunod na code snippet ay magta-transpile at magsasagawa ng backpropagated circuit sa isang QPU (parehong may at walang truncation).
# 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
Mga susunod na hakbangβ
- Subukan ang tutorial sa paggamit ng OBP upang mapabuti ang mga expectation value.