เริ่มต้นใช้งาน 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 ตำแหน่ง:
โดยที่ คือความแข็งแกร่งของการคัปปลิง และ คือความแข็งแกร่งของสนามแม่เหล็กภายนอก ในการตั้งค่าปัญหา observable ที่จะวัดคือค่าแม่เหล็กรวมของระบบ
โค้ดด้านล่างเตรียม 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 การเลือกถัดมาคือชุดค่า ซึ่งกำหนดจำนวน terms ใน MPF รวมถึงจำนวน Trotter steps ที่แต่ละ term ใช้ในการจำลองการวิวัฒนาการตามเวลา การเลือกค่า เป็นกระบวนการเชิง heuristic ดังนั้นจึงต้องหาชุดค่า "ที่ดี" ของตัวเอง อ่านแนวทางการหาชุดค่าที่ดีได้ที่ส่วนท้ายของหน้าเริ่มต้น
จากนั้น เมื่อกำหนดค่า ได้แล้ว ก็สามารถเตรียมระบบสมการ สำหรับการแก้ได้ เมทริกซ์ ยังถูกกำหนดโดย product formula ที่ใช้ด้วย โดยตัวเลือกที่นี่คือ order ซึ่งตั้งเป็น ในตัวอย่างนี้ และว่า product formula ควรเป็นแบบ symmetric หรือไม่ ซึ่งตั้งเป็น True ในตัวอย่างนี้ โค้ดด้านล่างเลือกเวลารวมสำหรับการ evolve ระบบ ค่า ที่ใช้ และชุดสมการสำหรับการแก้โดยใช้วิธี 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 สำหรับชุดค่า ที่เลือกถูกมองว่าสูงเกินไปและไม่สามารถเลือกชุดค่า ชุดอื่นได้ คู่มือนี้แสดงทั้งสองวิธีเพื่อเปรียบเทียบผลลัพธ์
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 แล้ว ขั้นตอนสุดท้ายคือสร้าง Circuit วิวัฒนาการตามเวลาสำหรับ order และชุด steps ของ 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)
circuits[1].draw("mpl", fold=-1)
circuits[2].draw("mpl", fold=-1)
เมื่อสร้าง 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
โดยที่ คือ coefficients ที่หาได้ และ คือการประมาณ observable สำหรับแต่ละ 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 เพียงตัวเดียวที่มี อย่างไรก็ตาม โมเดลประมาณให้ค่า expectation value ที่แย่กว่าโมเดล exact ซึ่งแสดงให้เห็นถึงความสำคัญของการใช้เกณฑ์การลู่เข้าที่เข้มงวดบนโมเดลประมาณ และการหาชุดค่า "ที่ดี"