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

Початок роботи з формулами мультипродукту (MPF)

Початок роботи з формулами мультипродукту (MPF)

Версії пакетів

Код на цій сторінці було розроблено з використанням наступних залежностей. Рекомендуємо використовувати ці версії або новіші.

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

Цей посібник демонструє, як використовувати пакет qiskit-addon-mpf, на прикладі часової еволюції моделі Ізінга. За допомогою цього пакету ти можеш побудувати формулу мультипродукту (MPF), що дозволяє досягти меншої похибки Троттера при вимірюванні спостережуваних величин. Надані інструменти дозволяють визначити ваги обраної MPF, які потім можна використати для рекомбінації оцінених значень очікування з кількох схем часової еволюції, кожна з різною кількістю кроків Троттера.

Почнемо з розгляду гамільтоніана моделі Ізінга з 10 вузлами:

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

де Ji,(i+1)J_{i,(i+1)} — сила зв'язку, а hih_i — сила зовнішнього магнітного поля. Для постановки задачі спостережуваною величиною для вимірювання буде повна намагніченість системи

M=i=110Zi.\langle M \rangle = \sum_{i=1}^{10} \langle Z_i \rangle.

Наведений нижче фрагмент коду готує гамільтоніан ланцюжка Ізінга за допомогою пакету qiskit-addon-utils та визначає спостережувану, яку буде виміряно.

# 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])

Далі підготуємо MPF. Перший вибір — чи будуть коефіцієнти статичними (незалежними від часу) чи динамічними; у цьому підручнику використовується статична MPF. Наступний вибір — набір значень kjk_j. Це визначає кількість доданків у MPF, а також кількість кроків Троттера, яку кожен доданок використовує для симуляції часової еволюції. Вибір значень kjk_j є евристичним, тому тобі потрібно самостійно знайти «хороший» набір значень kjk_j. Прочитай рекомендації щодо пошуку хорошого набору значень наприкінці сторінки початку роботи.

Потім, коли значення kjk_j визначено, можна підготувати систему рівнянь Ax=bAx=b для розв'язання. Матриця AA також визначається формулою продукту, яку використовуємо. Тут є вибір її порядку, який у цьому прикладі встановлено рівним 22, а також того, чи повинна формула продукту бути симетричною — для цього прикладу встановлено True. Наведений нижче фрагмент коду вибирає загальний час еволюції системи, значення kjk_j для використання та набір рівнянь для розв'язання за допомогою методу qiskit_addon_mpf.static.setup_static_lse.

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.]))

Після того як лінійну систему рівнянь створено, її можна розв'язати або точно, або за допомогою наближеної моделі з використанням суми квадратів (або норми Фробеніуса для динамічних коефіцієнтів; дивись довідник API для отримання додаткової інформації). Вибір наближеної моделі зазвичай виникає, коли норма коефіцієнтів для обраного набору значень kjk_j вважається занадто великою, а інший набір значень kjk_j не можна підібрати. Цей посібник демонструє обидва підходи для порівняння результатів.

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 ]
примітка

Об'єкт LSE також має метод LSE.solve(), який розв'язує систему рівнянь точно. Причина використання setup_exact_problem() у цьому посібнику — продемонструвати інтерфейс, який надають інші наближені методи.

Налаштування та виконання схем Троттера

Тепер, коли коефіцієнти xjx_j отримано, останній крок — генерація схем часової еволюції для порядку та обраного набору кроків kjk_j MPF. Пакет qiskit-addon-utils може прискорити цей процес.

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

Після побудови цих схем їх можна транспілювати та виконати на QPU. У цьому прикладі ми використаємо один із симуляторів без шуму, щоб продемонструвати зменшення похибки Троттера.

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)]

Реконструкція результатів

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

M=jxjMj.\langle M \rangle = \sum_j x_j \langle M_j \rangle.

де xjx_j — знайдені нами коефіцієнти, а Mj\langle M_j \rangle — оцінка спостережуваної iZi\sum_i \langle Z_i \rangle для кожної схеми. Потім можна порівняти отримані результати з точним значенням за допомогою пакету scipy.linalg.

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

Тут видно, що MPF зменшила похибку Троттера порівняно з тією, яку отримано лише за допомогою одиночної PF з kj=19k_j=19. Однак наближена модель дала значення очікування, гірше за точну модель. Це демонструє важливість використання жорстких критеріїв збіжності для наближеної моделі та пошуку «хорошого» набору значень kjk_j.