Lumaktaw sa pangunahing nilalaman

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 NN-qubit circuit ay mabilis na lalapit sa 4N4^N 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 Oβ€²O' ay kinakalkula nang paulit-ulit sa pamamagitan ng pag-apply ng mga gate mula sa bawat slice, ss, sa sumusunod na paraan:

Oβ€²(s)=USβˆ’s+1†Oβ€²(sβˆ’1)USβˆ’s+1O'^{(s)} = \mathcal{U}_{S-s+1}^\dagger O'^{(s-1)} \mathcal{U}_{S-s+1}

kung saan ang SS ay ang kabuuang bilang ng mga slice at ang Us\mathcal{U}_{s} 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

H^=βˆ‘(j,k)(JxXjXk+JyYjYk+JzZjZk)+βˆ‘j(hxXj+hyYj+hzZj) \hat{H} = \sum_{(j,k)} \left( J_xX_jX_k + J_yY_jY_k + J_z Z_jZ_k \right) + \sum_{j} \left(h_xX_j + h_yY_j + h_zZ_j\right)

at ang expectation value na susukathin ay ⟨Z0⟩\langle Z_0 \rangle.

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 Jx=Ο€/8J_x=\pi/8, Jy=Ο€/4J_y=\pi/4, Jz=Ο€/2J_z=\pi/2 at ang mga panlabas na magnetic field sa hx=Ο€/3h_x=\pi/3, hy=Ο€/6h_y=\pi/6, hz=Ο€/9h_z=\pi/9, 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")

Output of the previous code cell

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 88, at mag-allocate ng error budget na 0.0050.005 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.

Tandaan

Bilang default, ang backpropagate() ay gumagamit ng L1L_1 norm ng mga na-truncate na coefficient upang limitahan ang kabuuang error na nagmumula sa truncation, ngunit maaaring gamitin ang iba pang LpL_p 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:

Output of the previous code cell

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:

Output of the previous code cell

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​

Mga Rekomendasyon