Lumaktaw sa pangunahing nilalaman

Pagsisimula sa multi-product formulas (MPF)

Pagsisimula sa Multi-product formulas (MPFs)​

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.

qiskit[all]~=2.3.0
qiskit-addon-utils~=0.3.0
qiskit-addon-mpf~=0.3.0
scipy~=1.16.3

Ipinapakita ng gabay na ito kung paano gamitin ang qiskit-addon-mpf package, gamit ang time evolution ng isang Ising model bilang halimbawa. Sa pamamagitan ng package na ito, makakagawa ka ng Multi-Product Formula (MPF) na makakamit ang mas mababang Trotter error sa mga observable measurement. Ang mga tool na ibinibigay ay nagpapahintulot sa iyo na matukoy ang mga timbang ng isang napiling MPF, na maaaring gamitin para pagsamahin muli ang mga tinantyang expected value mula sa ilang time evolution circuit, bawat isa ay may iba't ibang bilang ng Trotter step.

Magsimula sa pag-isip ng Hamiltonian ng isang Ising model na may 10 site:

HIsing=βˆ‘i=19Ji,(i+1)ZiZi+1+βˆ‘i=110hiXiH_{\text{Ising}} = \sum_{i=1}^9 J_{i,(i+1)}Z_iZ_{i+1} + \sum_{i=1}^{10} h_i X_i

kung saan ang Ji,(i+1)J_{i,(i+1)} ay ang lakas ng coupling at ang hih_i ay ang lakas ng panlabas na magnetic field. Para i-set up ang problema, ang observable na susukat ay ang kabuuang magnetization ng sistema

⟨M⟩=βˆ‘i=110⟨Zi⟩.\langle M \rangle = \sum_{i=1}^{10} \langle Z_i \rangle.

Ang code snippet sa ibaba ay naghahanda ng Hamiltonian ng Ising chain gamit ang qiskit-addon-utils package, at tinutukoy ang observable na susukat.

# Added by doQumentation β€” required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-mpf qiskit-addon-utils scipy
from qiskit.transpiler import CouplingMap
from qiskit.synthesis import SuzukiTrotter
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import StatevectorEstimator
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_addon_utils.problem_generators import (
generate_xyz_hamiltonian,
generate_time_evolution_circuit,
)
from qiskit_addon_mpf.costs import (
setup_exact_problem,
setup_sum_of_squares_problem,
)
from qiskit_addon_mpf.static import setup_static_lse

from scipy.linalg import expm
import numpy as np

# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_line(10, bidirectional=False)

# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
print(f"Hamiltonian:\n {hamiltonian}\n")

L = coupling_map.size()
observable = SparsePauliOp.from_sparse_list(
[("Z", [i], 1 / L / 2) for i in range(L)], num_qubits=L
)
print(f"Observable:\n {observable}")
Hamiltonian:
SparsePauliOp(['IIIIIIIZZI', 'IIIIIZZIII', 'IIIZZIIIII', 'IZZIIIIIII', 'IIIIIIIIZZ', 'IIIIIIZZII', 'IIIIZZIIII', 'IIZZIIIIII', 'ZZIIIIIIII', 'IIIIIIIIIX', 'IIIIIIIIXI', 'IIIIIIIXII', 'IIIIIIXIII', 'IIIIIXIIII', 'IIIIXIIIII', 'IIIXIIIIII', 'IIXIIIIIII', 'IXIIIIIIII', 'XIIIIIIIII'],
coeffs=[1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j,
1. +0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j,
0.4+0.j, 0.4+0.j, 0.4+0.j])

Observable:
SparsePauliOp(['IIIIIIIIIZ', 'IIIIIIIIZI', 'IIIIIIIZII', 'IIIIIIZIII', 'IIIIIZIIII', 'IIIIZIIIII', 'IIIZIIIIII', 'IIZIIIIIII', 'IZIIIIIIII', 'ZIIIIIIIII'],
coeffs=[0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j,
0.05+0.j, 0.05+0.j, 0.05+0.j])

Susunod, ihahanda mo ang MPF. Ang unang pagpipilian ay kung ang mga coefficient ay magiging static (hindi depende sa oras) o dynamic; gumagamit ang tutorial na ito ng static MPF. Ang susunod na pagpipilian ay ang hanay ng mga kjk_j na halaga. Tinutukoy nito kung ilang termino ang magiging nasa MPF, pati na rin kung ilang Trotter step ang ginagamit ng bawat termino para gayahin ang time evolution. Ang pagpili ng mga kjk_j na halaga ay heuristic, kaya kailangan mong makakuha ng sarili mong hanay ng "magagandang" kjk_j na halaga. Basahin ang mga alituntunin sa paghahanap ng magandang hanay ng mga halaga sa dulo ng getting started page.

Pagkatapos, kapag natukoy na ang mga kjk_j na halaga, maaari kang maghanda ng sistema ng mga equation, Ax=bAx=b, para malutas. Ang matrix na AA ay tinutukoy din ng product formula na gagamitin. Ang mga pagpipilian dito ay ang order nito, na nakatakda sa 22 sa halimbawang ito, gayundin kung ang product formula ay dapat na symmetric o hindi, na nakatakda sa True para sa halimbawang ito. Pinipili ng code snippet sa ibaba ang kabuuang oras para i-evolve ang sistema, ang mga kjk_j na halaga na gagamitin, at ang hanay ng mga equation para malutas gamit ang qiskit_addon_mpf.static.setup_static_lse na method.

time = 8.0
trotter_steps = (8, 12, 19)

lse = setup_static_lse(trotter_steps, order=2, symmetric=True)
print(lse)
LSE(A=array([[1.00000000e+00, 1.00000000e+00, 1.00000000e+00],
[1.56250000e-02, 6.94444444e-03, 2.77008310e-03],
[2.44140625e-04, 4.82253086e-05, 7.67336039e-06]]), b=array([1., 0., 0.]))

Kapag nainstantiate na ang linear system of equations, maaari itong malutas nang eksakto o sa pamamagitan ng isang approximate na modelo gamit ang sum of squares (o ang Frobenius norm para sa dynamic na mga coefficient; tingnan ang API reference para sa karagdagang impormasyon). Ang pagpili ng approximate na modelo ay karaniwang nangyayari kapag ang norm ng mga coefficient para sa napiling hanay ng mga kjk_j na halaga ay itinuturing na masyadong mataas at hindi mapipili ang ibang hanay ng mga kjk_j na halaga. Ipinapakita ng gabay na ito ang dalawa para ikumpara ang mga resulta.

model_exact, coeffs_exact = setup_exact_problem(lse)
model_approx, coeffs_approx = setup_sum_of_squares_problem(
lse, max_l1_norm=3.0
)
model_exact.solve()
model_approx.solve()
print(f"Exact solution: {coeffs_exact.value}")
print(f"Approximate solution: {coeffs_approx.value}")
Exact solution: [ 0.17239057 -1.19447005  2.02207947]
Approximate solution: [-0.40454257 0.57553173 0.8290123 ]
tala

Ang LSE object ay nagtataglay din ng LSE.solve() na method, na malulutas ng sistema ng mga equation nang eksakto. Ang dahilan kung bakit ginagamit ang setup_exact_problem() sa gabay na ito ay para ipakita ang interface na ibinibigay ng iba pang approximate na mga method.

I-set up at i-execute ang mga Trotter circuit​

Ngayon na nakuha na ang mga coefficient na xjx_j, ang huling hakbang ay ang paggenerate ng mga time evolution circuit para sa order at napiling hanay ng mga step na kjk_j ng MPF. Maaaring mapabilis ng qiskit-addon-utils package ang prosesong ito.

circuits = []
for k in trotter_steps:
circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(order=2, reps=k),
time=time,
)
circuits.append(circ)
circuits[0].draw("mpl", fold=-1)

Output of the previous code cell

circuits[1].draw("mpl", fold=-1)

Output of the previous code cell

circuits[2].draw("mpl", fold=-1)

Output of the previous code cell

Kapag naitayo na ang mga circuit na ito, maaari mo itong i-transpile at i-execute gamit ang isang QPU. Para sa halimbawang ito, gagamitin natin ang isa sa mga noise-free simulator para ipakita kung paano nababawasan ang Trotter error.

backend = GenericBackendV2(num_qubits=10)
transpiler = generate_preset_pass_manager(
optimization_level=2, backend=backend
)

transpiled_circuits = [transpiler.run(circ) for circ in circuits]

estimator = StatevectorEstimator()
job = estimator.run([(circ, observable) for circ in transpiled_circuits])
result = job.result()

mpf_evs = [res.data.evs for res in result]
print(mpf_evs)
[array(0.23799162), array(0.35754312), array(0.38649906)]

I-reconstruct ang mga resulta​

Ngayon na na-execute na ang mga circuit, ang pag-reconstruct ng mga resulta ay medyo straightforward. Tulad ng nabanggit sa pahinang MPF overview, ang ating observable ay nire-reconstruct sa pamamagitan ng weighted sum

⟨M⟩=βˆ‘jxj⟨Mj⟩.\langle M \rangle = \sum_j x_j \langle M_j \rangle.

kung saan ang xjx_j ang mga coefficient na nahanap natin at ang ⟨Mj⟩\langle M_j \rangle ay ang tinantyang observable na βˆ‘i⟨Zi⟩\sum_i \langle Z_i \rangle para sa bawat circuit. Maaari naming ikumpara ang mga resultang nakuha natin sa eksaktong halaga gamit ang scipy.linalg package.

exp_H = expm(-1j * time * hamiltonian.to_matrix())
initial_state = np.zeros(exp_H.shape[0])
initial_state[0] = 1.0

time_evolved_state = exp_H @ initial_state
exact_obs = (
time_evolved_state.conj() @ observable.to_matrix() @ time_evolved_state
)

# Print out the different observable measurements
print(f"Exact value: {exact_obs.real}")
print(f"PF with 19 steps: {mpf_evs[-1]}")
print(f"MPF using exact solution: {mpf_evs @ coeffs_exact.value}")
print(f"MPF using approximate solution: {mpf_evs @ coeffs_approx.value}")
Exact value: 0.4006024248789992
PF with 19 steps: 0.3864990619977402
MPF using exact solution: 0.3954847855979902
MPF using approximate solution: 0.4299121425348959

Dito makikita mo na ang MPF ay nakabawas ng Trotter error kumpara sa nakuha gamit lamang ang isang PF na may kj=19k_j=19. Gayunpaman, ang approximate na modelo ay nagresulta sa isang expected value na mas masahol pa kaysa sa exact na modelo. Ipinapakita nito ang kahalagahan ng paggamit ng mahigpit na convergence criteria sa approximate na modelo at paghahanap ng "magandang" hanay ng mga kjk_j na halaga.