ปรับใช้และรัน Qiskit Function template สำหรับ AQC + Trotter Hamiltonian dynamics
ภาพรวม
นี่คือ Qiskit Function template สำหรับ Hamiltonian dynamics ที่ไม่ขึ้นกับการทดลองใดโดยเฉพาะ เมื่อกำหนด 1D nearest-neighbor Pauli Hamiltonian, สถานะเริ่มต้นที่เตรียมไว้ (ทางเลือก) และชุดของ observables แล้ว จะรัน Trotter time-evolution, การบีบอัดวงจรด้วย approximate quantum compilation (AQC) และการรันแบบมี mitigation จากนั้นส่งคืน time series ของแต่ละ observable สลับส่วน setup (PRE) และการวิเคราะห์ (POST) แล้วแกนหลักเดียวกันจะขับเคลื่อนการทดลองที่แตกต่างออกไป:
| PRE (การเตรียมของคุณ) | FUNCTION (ปรับใช้ที่นี่) | POST (การวิเคราะห์ของคุณ) |
|---|---|---|
| เตรียมสถานะ ในรูปแบบวงจรหรือ product state โดยมี local kick เป็นทางเลือก | Trotter synthesis → การบีบอัด AQC → การรันบน statevector, fake, หรือ runtime ซึ่งส่งคืน | สำหรับ neutron scattering หรือ magnetization, transport, quench dynamics และอื่นๆ |
template นี้เผยแพร่อยู่ใน Qiskit Function templates repository ร่วมกับ application template อื่นๆ notebook นี้จะปรับใช้ไปยัง Qiskit Serverless account ของคุณเอง รันครั้งเดียว แล้ว notebook ใดก็ตามจะสามารถเรียกใช้ function ได้ด้วย serverless.load("aqc-dynamics-function")
สำหรับตัวอย่างเชิงวิทยาศาสตร์แบบละเอียด ดู Simulate neutron scattering with an AQC + Trotter dynamics Serverless workflow ซึ่งเรียกใช้ function นี้เพื่อคำนวณ dynamical structure factor ของ KCuF notebook นี้ครอบคลุมเรื่องการปรับใช้และ input contract แทน
ข้อกำหนด
ก่อนเริ่มต้น ตรวจสอบให้แน่ใจว่ามีสิ่งต่อไปนี้ใน kernel environment ของ notebook นี้:
-
Qiskit SDK v2.0 หรือใหม่กว่า (
pip install qiskit) -
Qiskit IBM Catalog client (
pip install qiskit-ibm-catalog) ซึ่งใช้ปรับใช้และรัน workload บน Qiskit Serverless
dependency ทางวิทยาศาสตร์ของ function เอง (qiskit-addon-aqc-tensor, cotengrust, qiskit-aer) ไม่จำเป็นต้องติดตั้งในเครื่อง
รับไฟล์ซอร์สของ template
function นี้เป็น Python package ขนาดเล็กที่ Qiskit Serverless รันบนคลาวด์ ดังนั้นซอร์สของมันต้องมีอยู่เป็นไฟล์ในเครื่องที่จะถูกอัปโหลดตอนปรับใช้ package นี้เผยแพร่อยู่ใน Qiskit Function templates repository
ดาวน์โหลด source_files
การดาวน์โหลดจะได้ zip ไฟล์เดียว ตั้งชื่อตามพาธเต็มของไดเรกทอรีใน repository:
qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip
-
แตกไฟล์ zip ลงในไดเรกทอรีที่เก็บ notebook นี้
-
เปลี่ยนชื่อโฟลเดอร์ที่แตกออกมาจากชื่อยาวนั้นเป็น
source_files
working directory ของคุณจะมีลักษณะดังนี้:
your-working-directory/
├── function-template-aqc-trotter.ipynb <- this notebook
└── source_files/ <- the renamed folder
├── __init__.py
├── program.py
└── source/
├── __init__.py
├── _serverless.py
├── app_function.py
├── aqc.py
├── build.py
├── execute.py
└── hamiltonian.py
ชื่อต้องเป็น source_files เท่านั้น เพราะเป็น working_dir ที่ Step 3 จะอัปโหลด
program.py คือ entry point ที่ gateway เรียกใช้ ทุกอย่างภายใต้ source/ คือส่วนของการทำงาน แบ่งตามขั้นตอน: Hamiltonian และ Trotter synthesis, การบีบอัด AQC และการรัน ไม่จำเป็นต้องแก้ไขส่วนใดเลยเพื่อรันตัวอย่างต่อไปนี้ Step 3 จะอัปโหลดทั้งไดเรกทอรี ดังนั้นให้ทำขั้นตอนนั้นซ้ำทุกครั้งที่คุณเปลี่ยนไฟล์
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog
1. การยืนยันตัวตน
ใช้ qiskit-ibm-catalog เพื่อยืนยันตัวตนกับ QiskitServerless ด้วย API key (token) และ CRN (instance) ของคุณ ซึ่งสามารถหาได้จาก dashboard ของ IBM Quantum® Platform เมื่อมีข้อมูลรับรองเหล่านี้แล้ว คุณสามารถสร้าง instance ของ serverless client ในเครื่องเพื่ออัปโหลดหรือรัน function ที่เลือกได้:
from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
คุณสามารถใช้ save_account() เพื่อบันทึกข้อมูลรับรองในเครื่องได้ตามต้องการ (ดูคู่มือ Set up your IBM Cloud® account) โปรดทราบว่าการทำเช่นนี้จะเขียนข้อมูลรับรองของคุณลงในไฟล์เดียวกับ QiskitRuntimeService.save_account():
QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
หากบันทึกบัญชีไว้แล้ว ก็ไม่จำเป็นต้องระบุ token เพื่อยืนยันตัวตน:
from qiskit_ibm_catalog import QiskitServerless
# Authenticate to the remote cluster
# In this case, loading a saved account
serverless = QiskitServerless()
# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT
# serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
2. ประกาศ dependencies
package ที่ function ต้องการเพิ่มเติมจาก managed base serverless image
gateway จะติดตั้งเฉพาะชื่อที่อยู่ใน allowlist เท่านั้น (requirements-dynamic-dependencies.txt) จับคู่ตามชื่อ package และตรึงไว้ที่เวอร์ชันที่อนุญาตด้วย == สิ่งอื่นใดต้องมาแบบ transitive (เป็น dependency ของ package ที่อยู่ใน allowlist) ไวยากรณ์ [extras] ยังใช้ได้: qiskit-addon-aqc-tensor[quimb-jax] คือสิ่งที่ติดตั้ง quimb และ jax cotengrust จำเป็นสำหรับประสิทธิภาพหน่วยความจำระหว่างการจำลอง tensor network qiskit-aer แสดงแยกต่างหากสำหรับ backend fake (การจำลองแบบ noisy ในเครื่อง)
DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]
3. กำหนดและอัปโหลด function
from qiskit_ibm_catalog import QiskitFunction
fn = QiskitFunction(
title="aqc-dynamics-function",
entrypoint="program.py",
working_dir="source_files/",
dependencies=DEPENDENCIES,
)
serverless.upload(fn)
QiskitFunction(aqc-dynamics-function)
4. ตรวจสอบว่าลงทะเบียนสำเร็จ
next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)
ข้อมูลอ้างอิงของ Function
นี่เป็นบทนำอย่างย่อ ทุกฟิลด์มีเอกสารอธิบายอย่างครบถ้วนใน AQC Dynamics Template README: ตารางอินพุตแบบสมบูรณ์พร้อมกฎการตรวจสอบ, ฟิลด์เอาต์พุต, execution backend และตัวอย่างแบบละเอียดเพิ่มเติม สิ่งที่ตามมาต่อไปนี้คือฉบับย่อ เพียงพอสำหรับอ่านตัวอย่างที่ตามมา
อินพุต
การรันแต่ละครั้งคือการเรียก fn.run(...) เพียงครั้งเดียว มีเพียงสามอินพุตแรกในตารางเท่านั้นที่จำเป็น: hamiltonian, t_steps และ aqc_segments ทุกอย่างหลังจากนั้นเป็นทางเลือกและจะใช้ค่าเริ่มต้นที่แสดงไว้ ดังนั้นการเรียกแบบขั้นต่ำสุดคือสามอาร์กิวเมนต์ และส่วนที่เหลือของตารางคือฟังก์ชันการทำงานที่คุณสามารถเลือกใช้เพิ่มเติมได้ num_qubits ของ Hamiltonian กำหนดความยาวของ chain ดังนั้นจึงไม่มีอินพุตขนาดแยกต่างหาก
| Input | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|
hamiltonian | จำเป็น | 1D nearest-neighbor Pauli Hamiltonian ในรูปแบบ SparsePauliOp สตริงคือ Pauli operator ดังนั้นจึงไม่มีตัวประกอบ 1/2 แฝงอยู่โดยปริยาย |
t_steps | จำเป็น | จำนวน Trotter steps ทั้งหมด วิวัฒนาการไปถึง T = t_steps * dt และรายงานทุก observable ที่แต่ละ t_k = k * dt |
aqc_segments | จำเป็น | แผนการบีบอัด: รายการของ {"n_steps": k, "ansatz_steps": m} ขั้นตอน sum(n_steps) จะถูกบีบอัด ส่วนที่เหลือรันเป็น Trotter ปกติ |
dt | 0.2 | เวลาทางกายภาพที่เลื่อนไปในหนึ่ง Trotter step |
initial_state | |0...0> | QuantumCircuit ที่เตรียมไว้เพื่อวิวัฒนาการ ผสาน local kick ใดๆ เข้าไปในวงจรนี้ |
observables | Z แบบ per-site | สิ่งใดก็ตามที่ EstimatorV2 ยอมรับเป็นอาร์กิวเมนต์ observables หนึ่ง observable ต่อหนึ่งคอลัมน์เอาต์พุต |
trotter_options | Suzuki อันดับสอง | {"method": ..., "synthesis_settings": {...}} reps และ time เป็นของ function |
aqc_options | ดูคำอธิบาย | max_bond (32), cutoff (1e-8), autodiff_backend ("jax"), fidelity_target (None), optimizer_settings (L-BFGS-B, jac=True, maxiter=300) |
estimator_options | DD, twirling, TREX | EstimatorV2.options ส่งผ่านตามที่เป็น dictionary ที่ระบุจะแทนที่ค่าเริ่มต้นทั้งหมดแทนที่จะรวมเข้าด้วยกัน |
transpiler_options | {"optimization_level": 3} | อาร์กิวเมนต์คีย์เวิร์ดของ generate_preset_pass_manager backend และ target จะถูกปฏิเสธ เนื่องจาก execution path เป็นผู้ควบคุมสิ่งเหล่านี้ |
backend | "runtime" | "statevector", "fake" หรือ "runtime" |
backend_name | ว่างที่สุด | ชื่อ backend ของ IBM® สำหรับ runtime หรือชื่อ fake backend |
batches | 1 | แบ่งวงจรออกเป็น N runtime jobs หนึ่ง batch จะส่ง job เดียวและไม่สร้าง session |
parallel_sim | False | กระจายเส้นทาง local simulator ไปยังทุกคอร์ที่มีด้วย Ray ไม่มีผลกับ runtime |
return_circuits | False | ส่งคืนวงจร AQC + Trotter เชิงตรรกะในผลลัพธ์ควบคู่กับ observable series |
Execution backend
ทั้งสามเส้นทางใช้โค้ดเดียวกันและการตั้งค่า mitigation เดียวกัน แตกต่างกันเพียงตำแหน่งที่วงจรรัน
backend | คืออะไร | ข้อมูลรับรอง | หมายเหตุ |
|---|---|---|---|
"statevector" | StatevectorEstimator แบบแม่นตรง | Serverless account เท่านั้น | เส้นทางอ้างอิงที่แม่นตรง ไม่ใช้เวลา QPU |
"fake" | การจำลองแบบ noisy ในเครื่องบน Qiskit fake backend | Serverless account เท่านั้น | การซ้อมที่สมจริงของเส้นทาง runtime ที่มี mitigation ต้องใช้ qiskit-aer ค่าเริ่มต้นคือ fake_sherbrooke แบบ 127-qubit |
"runtime" (ค่าเริ่มต้น) | EstimatorV2 ที่มี mitigation กับ QPU จริง | Serverless account และ instance ที่มีสิทธิ์เข้าถึง QPU | backend_name เป็นทางเลือก หากไม่ระบุจะเลือกอุปกรณ์ที่ว่างที่สุด |
ทั้งสองเส้นทาง simulator ยังคงเรียกใช้ function ที่ปรับใช้แล้ว จึงต้องมี Serverless account ที่บันทึกไว้ แม้จะไม่ใช้เวลา QPU เลยก็ตาม สองตัวอย่างต่อไปนี้จะรัน workload เดียวกันบน statevector ก่อน แล้วจึงรันบน runtime
เอาต์พุต
job.result() ส่งคืน dictionary ธรรมดา:
{
"times": [...], # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)
"expectation_values": [[...]], # shape (n_times, n_observables)
"observable_labels": [...], # for example: ["Z_0", "ZZ_0_1"]
"metadata": {
"n", "t_steps", "dt", "tier",
"aqc_compressed_steps": 5, # total compressed steps (= sum of segment n_steps)
"aqc_segments": [ # per segment: the plan plus its own results
{"n_steps": 3, "ansatz_steps": 1, "steps": [1, 2, 3], "n_params": 133,
"fidelities": {"1": ..., "2": ..., "3": ...}},
{"n_steps": 2, "ansatz_steps": 2, "steps": [4, 5], "n_params": 245,
"fidelities": {"4": ..., "5": ...}},
],
"execution_backend",
"aqc_fidelities": {"1": ..., "2": ...}, # flat per-step fidelity, all compressed steps
"circuit_stats": { # per-step 2q depth and gate count, full Trotter vs AQC
"1": {"full_trotter": {"depth_2q": ..., "num_2q_gates": ...},
"aqc_trotter": {"depth_2q": ..., "num_2q_gates": ...}},
"2": {...},
},
"warnings": [...], # non-fatal notices; for example, a cotengrust fallback
"resource_usage": { # per stage; QPU_TIME is the charged QPU time
"RUNNING: OPTIMIZING_FOR_HARDWARE": {"CPU_TIME": ...},
"RUNNING: WAITING_FOR_QPU": {"CPU_TIME": ...},
"RUNNING: EXECUTING_QPU": {"QPU_TIME": ...},
},
},
# present only when return_circuits=True
"circuits": [QuantumCircuit, ...], # one per evolved step; circuits[i] is at times[i + 1]
}
aqc_fidelities และ circuit_stats เป็นสองสิ่งที่ควรอ่านก่อน: รวมกันแล้วบอกได้ว่าการบีบอัดยังคงความแม่นยำหรือไม่ และช่วยลด depth ได้จริงหรือไม่ บน runtime นั้น resource_usage จะรายงานเวลารอคิวแยกจากเวลา QPU ที่คุณถูกเรียกเก็บเงิน อินพุตที่ถูกปฏิเสธจะล้มเหลวทันทีในรูปแบบ ServerlessError ที่มีโครงสร้าง (code 4615)
ตัวอย่าง Simulator
รัน function บน backend statevector ที่แม่นตรงก่อน ไม่ใช้เวลา QPU และตรวจสอบความถูกต้องของการปรับใช้แบบ end-to-end โมเดลในที่นี้คือ transverse-field Ising chain แบบแปดคิวบิต และละเว้น observables ไว้ ดังนั้น function จะวัดค่า แบบ per-site เริ่มต้น
แผนการบีบอัดคืออินพุตที่ควรทำความเข้าใจ แต่ละ segment {"n_steps": k, "ansatz_steps": m} จะบีบอัด Trotter steps ที่ต่อเนื่องกัน k ขั้นตอนให้เป็น ansatz ที่สร้างจาก Trotter target แบบ m-step และขั้นตอนใดๆ ที่เกิน sum(n_steps) จะรันเป็น Trotter ปกติ ขั้นตอนแรกๆ ที่มี entanglement ต่ำจะบีบอัดได้ดีลงใน ansatz แบบชั้นเดียวที่ตื้น ส่วนขั้นตอนหลังๆ ที่มี entanglement มากขึ้นต้องการ ansatz ที่ลึกกว่า
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 8
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=8,
aqc_segments=[
{
"n_steps": 4,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 2,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="statevector",
)
print("job ID:", job.job_id)
job ID: ee1f3793-e995-427d-81d1-5924549beb38
ติดตามการรันและอ่านผลลัพธ์
status() รายงานทั้ง job lifecycle แบบหยาบและ sub-status รายขั้นตอนที่ function เผยแพร่ระหว่างรัน ขั้นตอนเดียวกันนี้ใช้กับการรันบนฮาร์ดแวร์ในภายหลังของคู่มือนี้:
QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE
ค่า status() | ขั้นตอน |
|---|---|
RUNNING: OPTIMIZING_FOR_HARDWARE | การเตรียมสถานะ, การสร้าง Trotter, การบีบอัด AQC |
RUNNING: WAITING_FOR_QPU | รอคิวบน QPU (เฉพาะ backend runtime) |
RUNNING: EXECUTING_QPU | วงจรกำลังรัน (local simulator จะทำเครื่องหมายขั้นนี้โดยตรง) |
RUNNING: POST_PROCESSING | การประกอบ result dictionary |
terminal state ได้แก่ DONE, ERROR และ CANCELED การรัน statevector นี้ไม่มีคิว QPU จึงข้าม RUNNING: WAITING_FOR_QPU ใช้ job.logs() ได้ทุกเมื่อเพื่อดู log รายขั้นตอน รวมถึง AQC fidelity ที่ได้ในแต่ละขั้นตอน
print(job.status()) # re-run until this reports DONE
DONE
import numpy as np
result = job.result()
ev = np.array(result["expectation_values"])
print("observables:", result["observable_labels"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("first row (t = 0, the prepared state):", np.round(ev[0], 4))
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']
shape: (9, 8) -> (n_times, n_observables)
first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]
last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}
2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)
ตัวอย่างฮาร์ดแวร์
การเรียก function ด้วย backend="runtime" จะทำการ transpile และรันบนโปรเซสเซอร์ IBM Quantum จริง พร้อมด้วย error mitigation ที่มีอยู่ในตัว function: dynamical decoupling (XY4), gate twirling และ twirled readout error extinction (TREX) backend_name ใช้เลือกอุปกรณ์ หากละเว้นไว้ function จะเลือกอุปกรณ์ที่ว่างที่สุด
ไม่มีสิ่งใดในโค้ดเชิงวิทยาศาสตร์เปลี่ยนแปลง สิ่งที่แตกต่างจากตัวอย่าง simulator คือความยาวของ chain, จำนวน Trotter steps, แผนการบีบอัด, backend และการตั้งค่า mitigation อย่างชัดเจนที่กล่าวถึงในส่วนถัดไป
การกำหนดขนาด job สำหรับฮาร์ดแวร์ควบคุม
estimator_options เป็นอินพุตที่ควรตั้งค่าอย่างตั้งใจ gate twirling จะสร้างวงจรสุ่มแยกกัน num_randomizations วงจรสำหรับทุก PUB และ job ทั้งหมด ทุก PUB พร้อมการสุ่มทั้งหมดของมัน ต้องพอดีกับหน่วยความจำคำสั่งของระบบควบคุมแบบคลาสสิกของ QPU function มีค่าเริ่มต้นที่ 1000 การสุ่ม ดังนั้นการวิวัฒนาการ 10 ขั้นตอนจะส่ง 11 PUB ที่มี 1000 วงจรต่อ PUB: ประมาณ 11,000 instance ของวงจรในหนึ่ง job
หากเกินสิ่งที่ระบบควบคุมรองรับได้ job จะล้มเหลวด้วย error 6073 Job limits ให้ขีดจำกัดและวิธีนับเทียบกับขีดจำกัดเหล่านั้น โดยขีดจำกัดหลักคือ 26.8 ล้านคำสั่งของระบบควบคุมต่อคิวบิต ซึ่งใช้ต่อ job ไม่ใช่ต่อ PUB dynamical decoupling จะเพิ่มเกตที่นับรวมเข้ากับขีดจำกัดนี้ด้วย
อินพุตสองตัวควบคุมขนาด:
-
estimator_optionsกำหนดงบประมาณ shot จำนวน shot ทั้งหมดคือnum_randomizations * shots_per_randomizationดังนั้นคุณสามารถแลกเปลี่ยนระหว่างจำนวนการสุ่มกับจำนวน shot ต่อการสุ่ม โดยยังคงสถิติไว้และลดขนาดโปรแกรมได้ เซลล์ต่อไปนี้ใช้ 100 การสุ่มที่ 200 shot ต่อครั้ง ซึ่งเท่ากับ 20,000 shot ต่อ observable และประมาณหนึ่งในสิบของจำนวน instance วงจรที่ค่าเริ่มต้นจะส่ง ดู TwirlingOptions และ Estimator options สำหรับชุดฟิลด์แบบเต็ม -
batchesจะแบ่ง PUB ออกเป็น runtime job แยกกันตามจำนวนนั้น ซึ่งเป็นวิธีแก้ที่ error 6073 แนะนำเอง และเป็นเหตุผลว่าทำไมกรอบคิดแบบต่อ job จึงสำคัญ การตั้งค่าbatches=4จะส่งประมาณสาม PUB ต่อ job แทนที่จะส่งสิบเอ็ดพร้อมกัน และ job ทั้งหมดจะถูกส่งออกไปพร้อมกันในหนึ่ง batch ทำให้กลุ่มนี้เข้าคิวเพียงครั้งเดียวแทนที่จะให้แต่ละ job เข้าคิวแยกกัน
โปรดจำไว้ว่า estimator_options ที่ระบุจะแทนที่ค่าเริ่มต้นของ function ทั้งหมดแทนที่จะรวมเข้าด้วยกัน ดังนั้น dynamical decoupling และ TREX จึงถูกระบุซ้ำในเซลล์ต่อไปนี้เพื่อให้ยังคงเปิดใช้งานอยู่
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 10
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=10,
aqc_segments=[
{
"n_steps": 3,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 3,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="runtime",
backend_name="ibm_marrakesh",
# The function defaults to 1000 twirling randomizations, which was too large
# for this device. Total shots is num_randomizations *
# shots_per_randomization, so this is 20,000 shots per observable.
estimator_options={
"dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
"twirling": {
"enable_gates": True,
"num_randomizations": 100,
"shots_per_randomization": 200,
},
"resilience": {"measure_mitigation": True},
},
)
print("job ID (save this to reconnect later):", job.job_id)
job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9
การรันบนฮาร์ดแวร์ไม่รวดเร็ว และเวลาส่วนใหญ่เป็นการประมวลผลแบบคลาสสิกมากกว่าบน QPU การบีบอัด AQC รันภายใน function ก่อนที่จะถึง QPU และคิว QPU ยังเพิ่มเข้ามาอีก คุณไม่จำเป็นต้องเปิด notebook หรือ kernel นี้ทิ้งไว้ระหว่างที่มันรัน
คัดลอก job ID ที่พิมพ์ออกมาจากเซลล์ก่อนหน้าและบันทึกไว้ สามเซลล์ถัดไปช่วยให้คุณกลับมาดำเนินการต่อได้ในภายหลัง:
-
เชื่อมต่อใหม่ จำเป็นเฉพาะใน kernel session ใหม่เท่านั้น: รันเซลล์ Authentication ซ้ำเพื่อสร้าง
serverlessใหม่ แล้วสร้างjobhandle ขึ้นใหม่จาก ID ที่บันทึกไว้ ข้ามเซลล์นี้หากคุณยังอยู่ใน session ที่ส่งงานไป เพราะ handle นั้นยังทำงานอยู่แล้ว -
ตรวจสอบสถานะ: รันซ้ำจนกว่าจะรายงาน
DONE -
ดึงผลลัพธ์: รันเมื่อสถานะเป็น
DONEเท่านั้น
วาง ID ที่บันทึกไว้ทับ placeholder ในเซลล์ reconnect ต่อไปนี้
# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Re-run this until it reports DONE, then fetch the result in the following cell.
print(job.status())
DONE
import numpy as np
# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits.
result = job.result()
ev = np.array(result["expectation_values"])
print("backend:", result["metadata"]["execution_backend"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
backend: runtime
shape: (11, 10) -> (n_times, n_observables)
last row (t = t_steps * dt): [0.1504 0.1361 0.218 0.2144 0.2275 0.1783 0.1749 0.1599 0.0915 0.0922]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}
2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)
ขั้นตอนถัดไป
-
ลองทำตาม Simulate neutron scattering with an AQC + Trotter dynamics Serverless workflow ซึ่งเป็นตัวอย่างประกอบที่เรียกใช้ function ที่ปรับใช้นี้เพื่อคำนวณ dynamical structure factor ของ KCuF
-
อ่าน AQC Dynamics Template on GitHub สำหรับ input และ output contract แบบเต็ม ตัวอย่างเพิ่มเติม และรายละเอียดการอ้างอิง
-
เรียกดู Qiskit Function templates repository สำหรับ application template อื่นๆ ที่สร้างในลักษณะเดียวกัน
-
อ่าน Qiskit Serverless guide สำหรับการจัดการ function ที่ปรับใช้แล้ว
-
เจาะลึกขั้นตอนการบีบอัด AQC เพิ่มเติมได้ที่เอกสาร Qiskit addon: AQC-Tensor