ข้ามไปยังเนื้อหาหลัก

เริ่มต้นใช้งาน multi-product formulas (MPF)

เริ่มต้นใช้งาน Multi-product formulas (MPFs)

เวอร์ชันของแพ็กเกจ

โค้ดในหน้านี้พัฒนาขึ้นโดยใช้ข้อกำหนดต่อไปนี้ แนะนำให้ใช้เวอร์ชันเหล่านี้หรือใหม่กว่า

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

คู่มือนี้แสดงวิธีใช้แพ็กเกจ qiskit-addon-mpf โดยใช้การวิวัฒนาการตามเวลาของโมเดล Ising เป็นตัวอย่าง ด้วยแพ็กเกจนี้ คุณสามารถสร้าง Multi-Product Formula (MPF) ที่ช่วยลด Trotter error ในการวัด observable ได้ เครื่องมือที่มีให้ช่วยให้คุณกำหนดน้ำหนักของ MPF ที่เลือก ซึ่งสามารถนำไปใช้รวมค่า expectation values ที่ประมาณได้จาก Circuit วิวัฒนาการตามเวลาหลายตัว โดยแต่ละตัวใช้จำนวน Trotter steps ที่ต่างกัน

เริ่มต้นด้วยการพิจารณา Hamiltonian ของโมเดล Ising ที่มี 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 คือความแข็งแกร่งของสนามแม่เหล็กภายนอก ในการตั้งค่าปัญหา observable ที่จะวัดคือค่าแม่เหล็กรวมของระบบ

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

โค้ดด้านล่างเตรียม Hamiltonian ของ Ising chain โดยใช้แพ็กเกจ qiskit-addon-utils และกำหนด observable ที่จะวัด

# 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 การเลือกแรกคือ coefficients จะเป็นแบบ static (ไม่ขึ้นกับเวลา) หรือแบบ dynamic บทแนะนำนี้ใช้ static MPF การเลือกถัดมาคือชุดค่า kjk_j ซึ่งกำหนดจำนวน terms ใน MPF รวมถึงจำนวน Trotter steps ที่แต่ละ term ใช้ในการจำลองการวิวัฒนาการตามเวลา การเลือกค่า kjk_j เป็นกระบวนการเชิง heuristic ดังนั้นจึงต้องหาชุดค่า kjk_j "ที่ดี" ของตัวเอง อ่านแนวทางการหาชุดค่าที่ดีได้ที่ส่วนท้ายของหน้าเริ่มต้น

จากนั้น เมื่อกำหนดค่า kjk_j ได้แล้ว ก็สามารถเตรียมระบบสมการ Ax=bAx=b สำหรับการแก้ได้ เมทริกซ์ AA ยังถูกกำหนดโดย product formula ที่ใช้ด้วย โดยตัวเลือกที่นี่คือ order ซึ่งตั้งเป็น 22 ในตัวอย่างนี้ และว่า product formula ควรเป็นแบบ symmetric หรือไม่ ซึ่งตั้งเป็น True ในตัวอย่างนี้ โค้ดด้านล่างเลือกเวลารวมสำหรับการ evolve ระบบ ค่า 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.]))

เมื่อสร้าง linear system of equations แล้ว ก็สามารถแก้ได้แบบ exact หรือผ่านโมเดลประมาณโดยใช้ sum of squares (หรือ Frobenius norm สำหรับ dynamic coefficients; ดูเอกสาร API สำหรับข้อมูลเพิ่มเติม) การเลือกใช้โมเดลประมาณมักเกิดขึ้นเมื่อ norm ของ coefficients สำหรับชุดค่า 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() ด้วย ซึ่งจะแก้ระบบสมการแบบ exact เหตุผลที่ใช้ setup_exact_problem() ในคู่มือนี้คือเพื่อแสดง interface ที่วิธี approximate อื่น ๆ มีให้

ตั้งค่าและรัน Trotter circuits

เมื่อได้ coefficients xjx_j แล้ว ขั้นตอนสุดท้ายคือสร้าง Circuit วิวัฒนาการตามเวลาสำหรับ order และชุด steps 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

เมื่อสร้าง Circuit เหล่านี้แล้ว คุณสามารถ transpile และรันบน QPU ได้ ในตัวอย่างนี้เราจะใช้ simulator ที่ปราศจาก noise เพื่อแสดงให้เห็นว่า 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)]

สร้างผลลัพธ์ใหม่

เมื่อรัน Circuit แล้ว การสร้างผลลัพธ์ใหม่นั้นค่อนข้างตรงไปตรงมา ดังที่กล่าวไว้ในหน้าภาพรวม MPF observable ของเราถูกสร้างใหม่ผ่าน weighted sum

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

โดยที่ xjx_j คือ coefficients ที่หาได้ และ Mj\langle M_j \rangle คือการประมาณ observable iZi\sum_i \langle Z_i \rangle สำหรับแต่ละ Circuit จากนั้นเราสามารถเปรียบเทียบผลลัพธ์ที่ได้กับค่าที่แน่นอนโดยใช้แพ็กเกจ 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 ช่วยลด Trotter error เมื่อเทียบกับการใช้ PF เพียงตัวเดียวที่มี kj=19k_j=19 อย่างไรก็ตาม โมเดลประมาณให้ค่า expectation value ที่แย่กว่าโมเดล exact ซึ่งแสดงให้เห็นถึงความสำคัญของการใช้เกณฑ์การลู่เข้าที่เข้มงวดบนโมเดลประมาณ และการหาชุดค่า kjk_j "ที่ดี"

Source: IBM Quantum docs — updated 13 ก.พ. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569