Lewati ke konten utama

Mengurangi error Trotter pada dinamika Hamiltonian dengan formula multi-produk

Di notebook ini, kamu akan belajar cara menggunakan Multi-Product Formula (MPF) untuk mendapatkan error Trotter yang lebih kecil pada observable kita dibandingkan error yang dihasilkan oleh Circuit Trotter terdalam yang benar-benar akan kita eksekusi. Kamu akan melakukannya dengan mengikuti langkah-langkah dalam sebuah pola Qiskit:

  • Langkah 1: Petakan ke masalah kuantum
    • Inisialisasi Hamiltonian masalah kita
    • Gunakan MPF untuk membangkitkan Circuit evolusi waktu Trotterized
  • Langkah 2: Optimalkan masalah
  • Langkah 3: Jalankan eksperimen
  • Langkah 4: Rekonstruksi hasil
    • Hitung nilai ekspektasi MPF

Langkah 1: Petakan ke masalah kuantum​

1a: Menyiapkan Hamiltonian kita​

Kita menggunakan model Ising pada garis 10 situs:

H^Ising=βˆ‘i=19Ji,(i+1)ZiZ(i+1)+βˆ‘i=110hiXi ,\hat{\mathcal{H}}_{\text{Ising}} = \sum_{i=1}^{9} J_{i,(i+1)} Z_i Z_{(i+1)} + \sum_{i=1}^{10} h_i X_i \, ,

di mana JJ adalah kekuatan kopling antara dua situs dan hh adalah medan magnet eksternal. Paket qiskit_addon_utils menyediakan berbagai fungsi yang dapat digunakan ulang untuk berbagai keperluan.

Modul qiskit_addon_utils.problem_generators menyediakan fungsi-fungsi untuk membangkitkan Hamiltonian mirip Heisenberg pada graf konektivitas tertentu. Graf ini bisa berupa rustworkx.PyGraph atau CouplingMap sehingga mudah digunakan dalam alur kerja berbasis Qiskit.

Berikut ini, kita membuat garis sederhana dari 10 Qubit menggunakan metode CouplingMap.from_line.

# Added by doQumentation β€” required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-mpf qiskit-addon-utils rustworkx scipy
from qiskit.transpiler import CouplingMap

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

graphviz_draw(coupling_map.graph, method="circo")

Code output

Selanjutnya, kita membangkitkan SparsePauliOp pada konektivitas yang diberikan dengan konstanta yang diinginkan.

from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian

# 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(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 yang akan kita ukur adalah total magnetisasi yang dapat kita bangun dengan sederhana seperti di bawah ini:

from qiskit.quantum_info import SparsePauliOp

L = coupling_map.size()
observable = SparsePauliOp.from_sparse_list([("Z", [i], 1 / L / 2) for i in range(L)], num_qubits=L)
print(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])

1b: Formula Multi-Produk​

MPF mengurangi error Trotter pada dinamika Hamiltonian melalui kombinasi berbobot dari beberapa eksekusi Circuit.

Untuk lebih konkretnya, kita mendefinisikan MPF sebagai:

ΞΌ(t)=βˆ‘jxjρjkj(tkj)+someΒ remainingΒ TrotterΒ error ,\mu(t) = \sum_{j} x_j \rho^{k_j}_{j}\left(\frac{t}{k_j}\right) + \text{some remaining Trotter error} \, ,

di mana xjx_j adalah koefisien pembobotan kita, ρjkj\rho^{k_j}_j adalah matriks densitas yang bersesuaian dengan keadaan murni yang diperoleh dengan mengevolusi keadaan awal menggunakan formula produk, SkjS^{k_j}, yang melibatkan kjk_j langkah Trotter, dan jj mengindeks jumlah formula produk yang membentuk MPF.

Kuncinya adalah bahwa sisa error Trotter lebih kecil daripada error Trotter yang akan diperoleh jika hanya menggunakan nilai kjk_j terbesar!

Kamu bisa melihat kegunaan MPF dari dua sudut pandang:

  1. Untuk anggaran langkah Trotter yang tetap yang bisa kamu eksekusi, kamu bisa mendapatkan hasil dengan error Trotter yang lebih kecil secara total.
  2. Untuk jumlah langkah Trotter yang menghasilkan Circuit yang dalam, kamu bisa menggunakan MPF untuk menemukan beberapa Circuit dengan kedalaman lebih pendek yang menghasilkan error Trotter serupa.

Pengantar static MPF​

MPF statis adalah yang nilai xjx_j-nya TIDAK bergantung pada waktu evolusi, tt.

Menentukan koefisien MPF statis untuk sekumpulan nilai kjk_j tertentu setara dengan menyelesaikan sistem persamaan linear: Ax=bAx=b, di mana xx adalah koefisien yang kita cari, AA adalah matriks yang bergantung pada kjk_j dan jenis PF yang kita gunakan (SS), dan bb adalah vektor batasan. Untuk singkatnya, kita tidak akan membahas lebih dalam di sini dan sebagai gantinya merujuk kamu ke dokumentasi LSE.

Kita bisa menemukan solusi untuk xx secara analitik sebagai x=Aβˆ’1bx = A^{-1}b, lihat misalnya Carrera Vazquez et al., 2023 atau Zhuk et al., 2023. Namun, solusi eksak ini bisa "ill-conditioned" sehingga menghasilkan norma L1 yang sangat besar dari koefisien kita, xx, yang bisa menyebabkan performa MPF yang buruk. Sebagai alternatif, kita juga bisa mendapatkan solusi aproksimasi yang meminimalkan norma L1 dari xx untuk mencoba mengoptimalkan perilaku MPF.

Berikut ini, kamu akan belajar cara melakukan semua itu.

Memilih kjk_j​

Pilihan kjk_j terserah kepada pengguna. Pada prinsipnya, nilai apa pun bisa dipilih tetapi beberapa kjk_j akan menyebabkan amplifikasi noise yang lebih besar pada perangkat nyata dibandingkan pilihan lainnya. Oleh karena itu, penting untuk mencoba menemukan nilai kjk_j yang "baik".

Di sini, kita akan memilih beberapa nilai tetap untuk kjk_j. Nilai terkecil dimotivasi oleh target waktu evolusi t=8.0t=8.0 yang biasanya memberitahu kita untuk memenuhi t/kmin<1t/k_{\text{min}} \lt 1 tetapi secara empiris kita tahu bahwa menetapkannya sama dengan 11 biasanya juga berhasil. Jika kamu ingin belajar lebih lanjut tentang ini dan cara memilih nilai kjk_j lainnya, lihat panduan terkait: How to choose the Trotter steps for an MPF.

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

Menyiapkan LSE​

Sekarang setelah kita memilih kjk_j, kita harus terlebih dahulu membangun LSE, Ax=bAx=b seperti yang dijelaskan di atas. Matriks AA tidak hanya bergantung pada kjk_j tetapi juga pilihan formula produk (PF) kita -- khususnya orde-nya. Selain itu, kita bisa mempertimbangkan apakah PF simetris atau tidak (lihat Carrera Vazquez et al., 2023), dengan menetapkan symmetric=True. Namun, ini tidak diperlukan seperti yang ditunjukkan oleh Zhuk et al., 2023.

Di sini, kita akan menggunakan formula Suzuki-Trotter orde kedua yang menghasilkan order=2 dan kita akan menetapkan symmetric=True.

from qiskit_addon_mpf.static import setup_static_lse

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

Menyelesaikan xx secara analitik​

Seperti yang disebutkan sebelumnya, kita bisa menemukan xx secara analitik:

import numpy as np

coeffs_analytical = lse.solve()
print(coeffs_analytical)
[ 0.17239057 -1.19447005  2.02207947]

Mengoptimalkan xx menggunakan model eksak​

Sebagai alternatif dari komputasi x=Aβˆ’1bx=A^{-1}b, kamu juga bisa menggunakan setup_exact_problem untuk membangun instance cvxpy.Problem yang menggunakan LSE sebagai batasan dan solusi optimalnya akan menghasilkan xx.

Di bagian berikutnya, akan terlihat mengapa antarmuka ini ada.

from qiskit_addon_mpf.costs import setup_exact_problem

model_exact, coeffs_exact = setup_exact_problem(lse)
model_exact.solve()
print(coeffs_exact.value)
[ 0.17239057 -1.19447005  2.02207947]

Sebagai indikator apakah MPF yang dibangun dengan koefisien ini akan menghasilkan hasil yang baik, kita bisa menggunakan norma L1 (lihat juga Carrera Vazquez et al., 2023).

print(np.linalg.norm(coeffs_exact.value, ord=1))
3.3889400921655914

Mengoptimalkan xx menggunakan model aproksimasi​

Bisa terjadi bahwa norma L1 untuk sekumpulan nilai kjk_j yang dipilih dianggap terlalu tinggi. Jika demikian dan kamu tidak bisa memilih kumpulan nilai kjk_j yang berbeda, kamu bisa menggunakan solusi aproksimasi untuk LSE sebagai pengganti solusi eksak.

Untuk melakukannya, cukup gunakan setup_sum_of_squares_problem untuk membangun instance cvxpy.Problem yang berbeda yang membatasi norma L1 pada ambang batas tertentu sambil meminimalkan perbedaan antara AxAx dan bb.

from qiskit_addon_mpf.costs import setup_sum_of_squares_problem

model_approx, coeffs_approx = setup_sum_of_squares_problem(lse, max_l1_norm=3.0)
model_approx.solve()
print(coeffs_approx.value)
print(np.linalg.norm(coeffs_approx.value, ord=1))
[-0.40454257  0.57553173  0.8290123 ]
1.8090865903790838

Perlu dicatat bahwa kamu memiliki kebebasan penuh dalam cara menyelesaikan masalah optimasi ini, artinya kamu bisa mengganti solver optimasi, ambang batas konvergensinya, dan sebagainya. Lihat panduan terkait tentang How to use the approximate model.

1c: Menyiapkan Circuit Trotter​

Pada titik ini, kita telah menemukan koefisien ekspansi kita, xx, dan yang tersisa adalah membangkitkan Circuit kuantum Trotterized. Sekali lagi, modul qiskit_addon_utils.problem_generators hadir untuk melakukan hal tersebut:

from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit

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)

Quantum circuit diagram

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

Quantum circuit diagram

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

Quantum circuit diagram

Langkah 2: Optimalkan masalah​

Biasanya, ini adalah langkah dalam pola di mana kamu mengoptimalkan Circuit untuk eksekusi pada hardware. Di sini, karena kita hanya menggunakan simulator tanpa noise, kita cukup mentranspilasi Circuit kita untuk sebuah GenericBackendV2.

from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler import generate_preset_pass_manager

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

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

Langkah 3: Jalankan eksperimen kuantum​

Seperti yang dijelaskan di awal, kita akan melewati langkah optimasi 2 karena kita hanya akan menghitung nilai ekspektasi observable target menggunakan simulator bebas noise, yaitu StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

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

Langkah 4: Rekonstruksi hasil​

Pertama, kita ekstrak nilai ekspektasi individual yang diperoleh untuk masing-masing Circuit Trotter:

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

Selanjutnya, kita tinggal menggabungkannya dengan koefisien MPF kita untuk mendapatkan total nilai ekspektasi MPF. Di bawah ini, kita melakukannya untuk masing-masing cara berbeda dalam menghitung xx.

print("Analytical    solution:", evs @ coeffs_analytical)
print("Exact model solution:", evs @ coeffs_exact.value)
print("Approx. model solution:", evs @ coeffs_approx.value)
Analytical    solution: 0.3954847855980006
Exact model solution: 0.39548478559800204
Approx. model solution: 0.42991214253489807

Terakhir, untuk masalah kecil ini kita bisa menghitung nilai referensi eksak menggunakan scipy.linalg.expm sebagai berikut:

from scipy.linalg import expm

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(exact_obs.real)
0.40060242487899755

Kita bisa dengan jelas melihat bahwa MPF telah mengurangi error Trotter dibandingkan yang diperoleh dengan PF individual terdalam dengan kj=19k_j=19. Namun, kita juga melihat bahwa model aproksimasi tidak sempurna karena ternyata menghasilkan nilai ekspektasi yang lebih buruk dari solusi eksak. Ini menunjukkan pentingnya menggunakan kriteria konvergensi yang ketat pada model aproksimasi seperti yang akan kamu pelajari dalam panduan How to use the approximate model.