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

ปรับใช้และรัน 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 ซึ่งส่งคืน O(t)\langle O \rangle(t)S(q,ω)S(q, \omega) สำหรับ 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 ของ KCuF3_3 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

  1. แตกไฟล์ zip ลงในไดเรกทอรีที่เก็บ notebook นี้

  2. เปลี่ยนชื่อโฟลเดอร์ที่แตกออกมาจากชื่อยาวนั้นเป็น 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 ปกติ
dt0.2เวลาทางกายภาพที่เลื่อนไปในหนึ่ง Trotter step
initial_state|0...0>QuantumCircuit ที่เตรียมไว้เพื่อวิวัฒนาการ ผสาน local kick ใดๆ เข้าไปในวงจรนี้
observablesZ แบบ per-siteสิ่งใดก็ตามที่ EstimatorV2 ยอมรับเป็นอาร์กิวเมนต์ observables หนึ่ง observable ต่อหนึ่งคอลัมน์เอาต์พุต
trotter_optionsSuzuki อันดับสอง{"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_optionsDD, twirling, TREXEstimatorV2.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
batches1แบ่งวงจรออกเป็น N runtime jobs หนึ่ง batch จะส่ง job เดียวและไม่สร้าง session
parallel_simFalseกระจายเส้นทาง local simulator ไปยังทุกคอร์ที่มีด้วย Ray ไม่มีผลกับ runtime
return_circuitsFalseส่งคืนวงจร AQC + Trotter เชิงตรรกะในผลลัพธ์ควบคู่กับ observable series

Execution backend

ทั้งสามเส้นทางใช้โค้ดเดียวกันและการตั้งค่า mitigation เดียวกัน แตกต่างกันเพียงตำแหน่งที่วงจรรัน

backendคืออะไรข้อมูลรับรองหมายเหตุ
"statevector"StatevectorEstimator แบบแม่นตรงServerless account เท่านั้นเส้นทางอ้างอิงที่แม่นตรง ไม่ใช้เวลา QPU
"fake"การจำลองแบบ noisy ในเครื่องบน Qiskit fake backendServerless account เท่านั้นการซ้อมที่สมจริงของเส้นทาง runtime ที่มี mitigation ต้องใช้ qiskit-aer ค่าเริ่มต้นคือ fake_sherbrooke แบบ 127-qubit
"runtime" (ค่าเริ่มต้น)EstimatorV2 ที่มี mitigation กับ QPU จริงServerless account และ instance ที่มีสิทธิ์เข้าถึง QPUbackend_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 จะวัดค่า ZZ แบบ 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
การเชื่อมต่อกลับไปยัง job ที่รันเป็นเวลานาน

การรันบนฮาร์ดแวร์ไม่รวดเร็ว และเวลาส่วนใหญ่เป็นการประมวลผลแบบคลาสสิกมากกว่าบน QPU การบีบอัด AQC รันภายใน function ก่อนที่จะถึง QPU และคิว QPU ยังเพิ่มเข้ามาอีก คุณไม่จำเป็นต้องเปิด notebook หรือ kernel นี้ทิ้งไว้ระหว่างที่มันรัน

คัดลอก job ID ที่พิมพ์ออกมาจากเซลล์ก่อนหน้าและบันทึกไว้ สามเซลล์ถัดไปช่วยให้คุณกลับมาดำเนินการต่อได้ในภายหลัง:

  1. เชื่อมต่อใหม่ จำเป็นเฉพาะใน kernel session ใหม่เท่านั้น: รันเซลล์ Authentication ซ้ำเพื่อสร้าง serverless ใหม่ แล้วสร้าง job handle ขึ้นใหม่จาก ID ที่บันทึกไว้ ข้ามเซลล์นี้หากคุณยังอยู่ใน session ที่ส่งงานไป เพราะ handle นั้นยังทำงานอยู่แล้ว

  2. ตรวจสอบสถานะ: รันซ้ำจนกว่าจะรายงาน DONE

  3. ดึงผลลัพธ์: รันเมื่อสถานะเป็น 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 ของ KCuF3_3

  • อ่าน AQC Dynamics Template on GitHub สำหรับ input และ output contract แบบเต็ม ตัวอย่างเพิ่มเติม และรายละเอียดการอ้างอิง

  • เรียกดู Qiskit Function templates repository สำหรับ application template อื่นๆ ที่สร้างในลักษณะเดียวกัน

  • อ่าน Qiskit Serverless guide สำหรับการจัดการ function ที่ปรับใช้แล้ว

  • เจาะลึกขั้นตอนการบีบอัด AQC เพิ่มเติมได้ที่เอกสาร Qiskit addon: AQC-Tensor