Lewati ke konten utama

Deploy dan jalankan template Qiskit Function untuk dinamika Hamiltonian AQC + Trotter

Ikhtisar

Ini adalah template Qiskit Function yang tidak spesifik terhadap eksperimen tertentu untuk dinamika Hamiltonian. Diberikan Hamiltonian Pauli tetangga terdekat 1D, keadaan awal yang telah disiapkan (opsional), dan sekumpulan observable, template ini menjalankan evolusi waktu Trotter, kompresi sirkuit approximate quantum compilation (AQC), dan eksekusi yang dimitigasi, lalu mengembalikan deret waktu setiap observable. Tukar setup (PRE) dan analisis (POST) dan inti yang sama akan menghasilkan eksperimen yang berbeda:

PRE (setupmu)FUNCTION (dideploy di sini)POST (analisismu)
Siapkan keadaan, sebagai sirkuit atau product state, dengan kick lokal opsionalSintesis Trotter → kompresi AQC → eksekusi pada statevector, fake, atau runtime, mengembalikan O(t)\langle O \rangle(t)S(q,ω)S(q, \omega) untuk hamburan neutron, atau magnetisasi, transport, quench dynamics, dan sebagainya

Template ini dipublikasikan di repositori template Qiskit Function, bersama template aplikasi lainnya. Notebook ini mendeploy-nya ke akun Qiskit Serverless-mu sendiri. Jalankan sekali, dan notebook mana pun kemudian bisa memanggil fungsi ini dengan serverless.load("aqc-dynamics-function").

Untuk contoh ilmiah yang dikerjakan secara lengkap, lihat Simulate neutron scattering with an AQC + Trotter dynamics Serverless workflow, yang memanggil fungsi ini untuk menghitung faktor struktur dinamis KCuF3_3. Notebook ini membahas deployment dan kontrak input sebagai gantinya.

Persyaratan

Sebelum memulai, pastikan kamu memiliki hal-hal berikut di lingkungan kernel notebook ini:

  • Qiskit SDK v2.0 atau lebih baru (pip install qiskit).

  • Klien Qiskit IBM Catalog (pip install qiskit-ibm-catalog), yang mendeploy dan menjalankan workload pada Qiskit Serverless.

Dependensi ilmiah milik fungsi itu sendiri (qiskit-addon-aqc-tensor, cotengrust, qiskit-aer) tidak perlu diinstal secara lokal.

Dapatkan file sumber template

Fungsi ini adalah paket Python kecil yang dijalankan Qiskit Serverless di cloud, sehingga sumbernya harus ada sebagai file lokal yang diunggah saat waktu deploy. Paket ini dipublikasikan di repositori template Qiskit Function.

Unduh source_files

Unduhan ini berupa satu file zip tunggal, dinamai sesuai jalur lengkap direktori di repositori:

qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip

  1. Ekstrak (unzip) ke direktori yang menyimpan notebook ini.

  2. Ganti nama folder yang diekstrak dari nama panjang tersebut menjadi source_files.

Direktori kerjamu kemudian akan terlihat seperti ini:

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

Namanya harus persis source_files, karena itulah working_dir yang diunggah oleh Langkah 3.

program.py adalah entry point yang dipanggil oleh gateway. Semua yang ada di bawah source/ adalah implementasi, dipecah berdasarkan tahap: sintesis Hamiltonian dan Trotter, kompresi AQC, dan eksekusi. Tidak ada yang perlu diedit untuk menjalankan contoh-contoh berikut. Langkah 3 mengunggah seluruh direktori, jadi ulangi langkah tersebut setiap kali kamu mengubah sebuah file.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog

1. Autentikasi

Gunakan qiskit-ibm-catalog untuk mengautentikasi ke QiskitServerless dengan API key (token) dan CRN (instance) milikmu, yang bisa kamu temukan di dashboard IBM Quantum® Platform. Dengan kredensial ini kamu bisa membuat instance klien serverless secara lokal untuk mengunggah atau menjalankan fungsi yang dipilih:

from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

Kamu juga bisa secara opsional menggunakan save_account() untuk menyimpan kredensialmu di lingkungan lokalmu (lihat panduan Set up your IBM Cloud® account). Perhatikan bahwa ini menulis kredensialmu ke file yang sama dengan QiskitRuntimeService.save_account():

QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

Jika akun sudah disimpan, tidak perlu memberikan token untuk mengautentikasi:

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. Deklarasikan dependensi

Paket-paket yang dibutuhkan fungsi ini di luar image dasar serverless yang dikelola.

catatan

Gateway hanya menginstal nama yang ada di daftar izinnya (requirements-dynamic-dependencies.txt), dicocokkan berdasarkan nama paket dan dipin ke versi yang diizinkan dengan ==. Hal lainnya harus datang secara transitif (sebagai dependensi dari paket yang ada di daftar izin). Sintaks [extras] didukung: qiskit-addon-aqc-tensor[quimb-jax] adalah yang menginstal quimb dan jax. cotengrust diperlukan untuk efisiensi memori selama simulasi tensor network. qiskit-aer dicantumkan secara terpisah untuk backend fake (simulasi noisy lokal).

DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]

3. Definisikan dan unggah fungsi

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. Verifikasi bahwa fungsi terdaftar

next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)

Referensi fungsi

Ini adalah pengantar singkat. Setiap field didokumentasikan secara lengkap di AQC Dynamics Template README: tabel input lengkap dengan aturan validasinya, field output, backend eksekusi, dan contoh-contoh lebih lanjut yang dikerjakan. Berikut ini adalah versi singkatnya, cukup untuk membaca contoh-contoh berikut.

Input

Setiap run adalah satu panggilan fn.run(...) tunggal. Hanya tiga input pertama dalam tabel yang wajib: hamiltonian, t_steps, dan aqc_segments. Semua yang ada setelahnya bersifat opsional dan kembali ke default yang ditampilkan, sehingga panggilan minimal adalah tiga argumen dan sisa tabel adalah fungsionalitas yang bisa kamu pilih untuk digunakan. num_qubits dari Hamiltonian menentukan panjang chain, sehingga tidak ada input ukuran terpisah.

InputDefaultDeskripsi
hamiltonianwajibHamiltonian Pauli tetangga terdekat 1D sebagai SparsePauliOp. String adalah operator Pauli, sehingga tidak ada faktor setengah implisit.
t_stepswajibTotal langkah Trotter. Berevolusi hingga T = t_steps * dt dan melaporkan setiap observable pada setiap t_k = k * dt.
aqc_segmentswajibRencana kompresi: daftar {"n_steps": k, "ansatz_steps": m}. sum(n_steps) langkah dikompresi; sisanya berjalan sebagai Trotter biasa.
dt0.2Waktu fisik yang dimajukan oleh satu langkah Trotter.
initial_state|0...0>QuantumCircuit yang disiapkan untuk dievolusikan. Sertakan kick lokal apa pun ke dalam sirkuit ini.
observablesZ per situsApa pun yang diterima EstimatorV2 sebagai argumen observables-nya. Satu observable per kolom output.
trotter_optionsSuzuki orde ke-2{"method": ..., "synthesis_settings": {...}}. reps dan time dimiliki oleh fungsi ini.
aqc_optionslihat deskripsimax_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, diteruskan apa adanya. Dictionary yang diberikan mengganti default secara keseluruhan alih-alih digabung ke dalamnya.
transpiler_options{"optimization_level": 3}Argumen keyword generate_preset_pass_manager. backend dan target ditolak, karena jalur eksekusi memilikinya.
backend"runtime""statevector", "fake", atau "runtime".
backend_namepaling tidak sibukNama backend IBM® untuk runtime, atau fake backend bernama.
batches1Pisahkan sirkuit ke N runtime job. Satu batch mengirimkan satu job tunggal dan tidak membuat session.
parallel_simFalseSebarkan jalur simulator lokal ke semua core yang tersedia dengan Ray. Tidak berpengaruh pada runtime.
return_circuitsFalseKembalikan sirkuit logis AQC + Trotter dalam hasil bersama deret observable.

Backend eksekusi

Ketiga jalur ini berbagi kode yang sama dan pengaturan mitigasi yang sama. Perbedaannya hanya di mana sirkuit dijalankan.

backendApa ituKredensialCatatan
"statevector"StatevectorEstimator eksakHanya akun ServerlessJalur referensi eksak. Tidak ada waktu QPU.
"fake"Simulasi lokal noisy pada fake backend QiskitHanya akun ServerlessLatihan yang setia dari jalur runtime yang dimitigasi. Membutuhkan qiskit-aer. Default ke fake_sherbrooke 127-qubit.
"runtime" (default)EstimatorV2 yang dimitigasi terhadap QPU nyataAkun Serverless dan instance dengan akses QPUbackend_name opsional; jika dihilangkan akan memilih perangkat paling tidak sibuk.

Kedua jalur simulator tetap memanggil fungsi yang telah dideploy, sehingga mereka membutuhkan akun Serverless yang tersimpan meskipun mereka tidak menggunakan waktu QPU. Dua contoh berikut menjalankan workload yang sama pada statevector terlebih dahulu, lalu pada runtime.

Output

job.result() mengembalikan dictionary biasa:

{
"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 dan circuit_stats adalah dua yang perlu dibaca terlebih dahulu: bersama-sama mereka memberi tahu apakah kompresi tetap setia dan apakah benar-benar menghemat depth. Pada runtime, resource_usage melaporkan waktu tunggu antrean secara terpisah dari waktu QPU yang dikenakan biaya padamu. Input yang ditolak akan gagal dengan cepat sebagai ServerlessError terstruktur (kode 4615).

Contoh simulator

Jalankan fungsi ini pada backend statevector eksak terlebih dahulu. Ini tidak menghabiskan waktu QPU dan memvalidasi deployment secara menyeluruh. Model di sini adalah chain Ising transverse-field delapan-qubit, dan observables dihilangkan sehingga fungsi mengukur ZZ per situs default.

Rencana kompresi adalah input yang layak untuk dipahami. Setiap segmen {"n_steps": k, "ansatz_steps": m} mengompresi k langkah Trotter berurutan menjadi sebuah ansatz yang dibangun dari target Trotter m-langkah, dan langkah apa pun di luar sum(n_steps) berjalan sebagai Trotter biasa. Langkah-langkah awal dengan entanglement rendah terkompresi dengan baik menjadi ansatz satu-lapis yang dangkal; langkah-langkah selanjutnya yang keterjeratannya lebih tinggi membutuhkan ansatz yang lebih dalam.

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

Ikuti run dan baca hasilnya

status() melaporkan siklus hidup job secara garis besar sekaligus sub-status per tahap yang dipublikasikan fungsi saat berjalan. Tahapan yang sama berlaku untuk run hardware nanti di panduan ini:

QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE

Nilai status()Tahap
RUNNING: OPTIMIZING_FOR_HARDWAREpersiapan state, pembuatan Trotter, kompresi AQC
RUNNING: WAITING_FOR_QPUmengantre di QPU (hanya backend runtime)
RUNNING: EXECUTING_QPUsirkuit sedang dieksekusi (simulator lokal menandai ini secara langsung)
RUNNING: POST_PROCESSINGmerakit dictionary hasil

Status akhir adalah DONE, ERROR, dan CANCELED. Run statevector ini tidak memiliki antrean QPU, sehingga melewati RUNNING: WAITING_FOR_QPU. Gunakan job.logs() kapan saja untuk melihat log per tahap, termasuk fidelity AQC yang dicapai pada setiap langkah.

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)

Contoh hardware

Panggilan fungsi dengan backend="runtime" mentranspilasi dan mengeksekusi pada prosesor IBM Quantum yang nyata, dengan mitigasi error bawaan fungsi: dynamical decoupling (XY4), gate twirling, dan twirled readout error extinction (TREX). backend_name memilih perangkat; jika dihilangkan, fungsi akan mengambil yang paling tidak sibuk.

Kode ilmiahnya sendiri tidak berubah sama sekali. Yang berbeda dari contoh simulator adalah panjang chain, jumlah langkah Trotter, rencana kompresi, backend, dan pengaturan mitigasi eksplisit yang dibahas di bagian berikut.

Menentukan ukuran job untuk hardware kontrol

estimator_options adalah input yang layak diatur secara sengaja. Gate twirling membangun num_randomizations sirkuit acak terpisah untuk setiap PUB, dan seluruh job, setiap PUB dengan semua randomization-nya, harus muat dalam memori instruksi sistem kontrol klasik QPU. Fungsi ini secara default menggunakan 1000 randomization, sehingga evolusi 10-langkah mengirimkan 11 PUB dengan masing-masing 1000 sirkuit: kira-kira 11.000 instance sirkuit dalam satu job tunggal.

Jika kapasitas yang bisa ditampung sistem kontrol terlampaui, job akan gagal dengan error 6073. Job limits memberikan ambang batas dan cara menghitungnya, yang utama adalah 26,8 juta instruksi sistem kontrol per qubit, diterapkan per job bukan per PUB. Dynamical decoupling menambahkan gate yang turut dihitung ke dalamnya.

Dua input mengontrol ukurannya:

  • estimator_options mengatur anggaran shot. Total shot adalah num_randomizations * shots_per_randomization, jadi kamu bisa menukar randomization dengan shot per randomization, mempertahankan statistiknya, dan tetap mengecilkan programnya. Cell berikut menggunakan 100 randomization dengan 200 shot masing-masing, yaitu 20.000 shot per observable dan sekitar sepersepuluh dari instance sirkuit yang akan dikirim oleh default. Lihat TwirlingOptions dan Estimator options untuk kumpulan field lengkap.

  • batches memisahkan PUB ke sejumlah runtime job terpisah, yang merupakan solusi yang disarankan oleh error 6073 itu sendiri dan mengapa framing per-job itu penting. Mengatur batches=4 mengirimkan kira-kira tiga PUB per job alih-alih sebelas sekaligus, dan job-job tersebut dikirim bersama dalam satu batch sehingga grup mengantre sekali alih-alih setiap job mengantre secara terpisah.

Ingat bahwa estimator_options yang diberikan mengganti default fungsi secara keseluruhan alih-alih digabung ke dalamnya, sehingga dynamical decoupling dan TREX dinyatakan ulang di cell berikut untuk membuatnya tetap aktif.

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
Menyambung kembali ke job yang berjalan lama

Run hardware tidak cepat, dan sebagian besar waktunya bersifat klasik alih-alih di QPU. Kompresi AQC berjalan di dalam fungsi sebelum apa pun mencapai QPU, dan antrean QPU berada di atas itu. Kamu tidak perlu menjaga notebook atau kernel ini tetap terbuka selama ia berjalan.

Salin ID job yang dicetak oleh cell sebelumnya dan simpan. Tiga cell berikutnya memungkinkanmu untuk melanjutkan run tersebut nanti:

  1. Sambung kembali, hanya dibutuhkan di sesi kernel baru: jalankan ulang cell Authentication untuk membuat ulang serverless, lalu bangun ulang handle job dari ID yang kamu simpan. Lewati cell ini jika kamu masih berada di sesi tempat kamu mengirimkan job, karena handle-nya sudah aktif.

  2. Periksa status: jalankan ulang hingga melaporkan DONE.

  3. Ambil hasilnya: jalankan hanya setelah status-nya DONE.

Ganti placeholder pada cell sambung kembali berikut dengan ID yang kamu simpan.

# 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)

Langkah selanjutnya

Rekomendasi