Model pemrograman
Model pemrograman adalah spesifikasi fundamental yang mendefinisikan bagaimana perangkat lunak distrukturkan dan dieksekusi. Model-model ini menyediakan kerangka kerja bagi pengembang untuk mengekspresikan algoritma dan mengatur kode, seringkali mengabstraksikan detail tingkat rendah dari perangkat keras atau lingkungan eksekusi yang mendasarinya. Model yang berbeda cocok untuk jenis masalah dan arsitektur perangkat keras yang berbeda, menawarkan berbagai tingkat abstraksi dan kontrol.
Dalam pelajaran ini, kita akan meninjau model pemrograman kuantum dan klasik dan melihat bagaimana kita dapat menggabungkannya untuk mengoperasikan algoritma di lingkungan heterogen. Iskandar Sitdikov memberi kita gambaran umum dalam video berikut.
Model pemrograman untuk QPUโ
Kita akan memulai dengan model pemrograman untuk komputer kuantum. Model pemrograman fundamental yang familiar bagi hampir semua pengembang kuantum adalah quantum circuit. Kita tidak akan masuk ke detail model quantum circuit di sini, karena kita sudah memiliki kuliah yang bagus oleh John Watrous yang menjelaskan ini secara rinci. Kita hanya akan menyebutkan bahwa circuit dibangun dari sekumpulan garis (disebut kabel/wire) yang merepresentasikan qubit, gate yang merepresentasikan operasi pada keadaan kuantum, dan sekumpulan pengukuran.
Konsep model pemrograman penting lainnya untuk komputasi kuantum adalah apa yang kita sebut primitif komputasional. Primitif-primitif ini merepresentasikan beberapa tugas yang paling umum yang ingin dicapai pengguna dengan komputer kuantum. Ada beberapa primitif yang tersedia saat ini, termasuk Executor. Dalam kursus ini kita akan fokus terutama pada primitif Sampler dan Estimator. Sampler memberi kamu kemampuan untuk mengambil sampel dari keadaan yang disiapkan oleh quantum circuit-mu. Ini memberi tahu kamu keadaan basis komputasional mana yang membentuk keadaan kuantum yang disiapkan pada quantum circuit-mu. Estimator memungkinkan kamu untuk memperkirakan nilai ekspektasi dari sebuah observabel untuk sistem dalam keadaan yang disiapkan oleh quantum circuit-mu. Konteks umum adalah memperkirakan energi sistem dalam keadaan tertentu.
Hal terakhir yang akan kita bicarakan di bagian ini adalah transpilasi. Transpilasi adalah proses penulisan ulang circuit masukan yang diberikan untuk mencocokkan batasan fisik dan Instruction Set Architecture (ISA) dari perangkat kuantum tertentu. Mirip dengan kompiler klasik, ini berarti menerjemahkan operasi uniter abstrak ke dalam himpunan gate native yang dapat dieksekusi oleh perangkat target. Ini juga mengoptimalkan instruksi circuit untuk eksekusi yang efisien pada komputer kuantum yang berisik, dengan rutinitas yang secara bertahap mengubah struktur circuit dengan menerapkan beberapa tahap optimisasi.
Uji pemahamanmuโ
Ada berapa qubit dalam circuit di bawah ini?

Jawaban:
Empat.
Uji pemahamanmuโ
Misalkan kamu memodelkan elektron dalam molekul. Kamu ingin memperkirakan (a) energi ground state molekul, dan (b) keadaan basis komputasional mana yang paling dominan dalam ground state molekul. Dalam setiap kasus, apakah kamu akan menggunakan primitif Estimator atau Sampler?
Jawaban:
(a) Estimator (b) Sampler
Model pemrograman klasikโ
Ada banyak model pemrograman untuk komputer klasik, tetapi untuk bagian ini kita akan fokus pada dua yang paling populer: pemrograman paralel dan alur kerja tugas. Menggunakan kedua model ini bersama model pemrograman kuantum, seseorang dapat mengekspresikan hampir semua alur kerja hybrid quantum-classical dengan kompleksitas apa pun.
Pemrograman paralelโ
Pemrograman paralel adalah model yang membagi program menjadi sub-masalah yang dapat dieksekusi secara bersamaan. Ada dua paradigma utama pemrograman paralel:
-
Paralelisme memori bersama (Open Multiprocessing, atau OpenMP): Digunakan untuk mengeksploitasi beberapa core dalam satu node komputasi. Thread eksekusi berbagi satu ruang memori.
-
Paralelisme memori terdistribusi (Message Passing Interface, atau MPI): Digunakan untuk penskalaan di beberapa node komputasi terpisah. Setiap proses memiliki ruang memorinya sendiri yang terisolasi.
Di sini, kita akan fokus pada model memori terdistribusi karena penting untuk superkomputing multi-node dan mengkoordinasikan pekerjaan hybrid quantum-classical skala besar.
Ada beberapa konsep yang perlu kita pahami untuk beroperasi dalam model pemrograman paralel memori terdistribusi:
- Proses - Instance independen dari program dengan ruang memorinya sendiri.
- Rank - Pengidentifikasi integer unik yang ditetapkan ke setiap proses, digunakan khusus untuk mengidentifikasi pengirim dan penerima selama komunikasi (belum tentu "rank" dalam arti memprioritaskan).
- Sinkronisasi - Mekanisme untuk koordinasi di antara rank dan proses yang berbeda.
- Single program, multiple data (SPMD) - Model komputasi abstrak di mana satu instance kode sumber berjalan secara bersamaan pada beberapa proses, masing-masing beroperasi pada subset berbeda dari total data.
- Pengiriman pesan - Paradigma komunikasi yang digunakan dalam arsitektur memori terdistribusi yang memungkinkan proses independen untuk bertukar data dan hasil antara. Ini mengandalkan operasi 'kirim' dan 'terima' yang eksplisit untuk mengkoordinasikan eksekusi antara node komputasi yang berbeda.
Ada standar yang disebut MPI yang mengimplementasikan paradigma pengiriman pesan ini untuk arsitektur paralel. MPI berfungsi sebagai perwujudan fungsional dari semua konsep yang tercantum di atas, menyediakan panggilan library tertentu yang diperlukan untuk mengelola proses, menetapkan rank, memfasilitasi sinkronisasi, dan memungkinkan pengiriman pesan di bawah model SPMD. Mengumpulkan semua konsep ini bersama-sama, kita dapat mengatakan eksekusi program paralel terjadi dengan cara berikut:
- Satu program yang dikompilasi (binary file yang sama) disalin ke dan dieksekusi oleh peluncur pekerjaan untuk membuat beberapa proses paralel di beberapa node.
- Aliran kontrol utama program didiktekan oleh rank proses. Ini adalah prinsip SPMD dalam aksi: program menggunakan logika kondisional (misalnya, if (rank == 0)) untuk memastikan bahwa hanya bagian tertentu dan terparalelkan dari kode yang dieksekusi oleh proses pekerja, sementara proses master (seringkali Rank 0) menangani inisialisasi dan agregasi akhir.
- Komunikasi antara proses terjadi melalui pengiriman pesan (menggunakan MPI), yang dipanggil setiap kali suatu proses perlu bertukar data atau hasil antara dengan rank lain.
Secara visual, tampilannya akan terlihat seperti ini:
Mari kita coba menerapkan beberapa konsep yang baru saja kita pelajari ke dalam kode.
Pertama, kita akan mencoba menjalankan program paralel "hello world" sederhana menggunakan OpenMPI, yang merupakan implementasi dari protokol MPI, standar untuk pengiriman pesan dalam pemrograman paralel. Di sini, kita akan menggunakan paket Python mpi4py, yang merupakan binding Python untuk standar Message Passing Interface (MPI).
$ vim mpi-hello-world.py
from mpi4py import MPI
import sys
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
sys.stdout.write(f"[Rank {rank}] Hello from process {rank} of {size}!\n")
if rank == 0:
data = {'answer': 42, 'pi': 3.14}
sys.stdout.write(f"[Rank {rank}] Sending: {data}\n")
comm.send(data, dest=1, tag=42)
elif rank == 1:
data = comm.recv(source=0, tag=42)
sys.stdout.write(f"[Rank {rank}] Received: {data}\n")
~
~
Kita akan menggunakan dua node untuk menjalankan program ini, yang akan kita tentukan dalam skrip pengiriman kita.
$ vim mpi-hello-world.sh
#!/bin/bash
#
#SBATCH --job-name=mpi-hello-world
#SBATCH --output=mpi-hello-world.out
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=normal
/usr/lib64/openmpi/bin/mpirun python /data/ch3/parallel/mpi-hello-world.py
Kemudian jalankan skrip shell.
$ sbatch mpi-hello-world.sh
Kita dapat memeriksa log hasil dari pekerjaan.
$ cat mpi-hello-world.out | grep Rank
[Rank 1] Hello from process 1 of 2!
[Rank 0] Hello from process 0 of 2!
[Rank 0] Sending: {'answer': 42, 'pi': 3.14}
[Rank 1] Received: {'answer': 42, 'pi': 3.14}
Di sini kita menggunakan dua node dan proses pada setiap node sekarang diidentifikasi oleh rank - Rank 0 dan Rank 1 - yang digunakan untuk memutuskan aliran kontrol program.
Alur kerja tugasโ
Sekarang mari kita bicara tentang model pemrograman alur kerja Tugas. Alur kerja tugas mengabstraksikan komputasi ke dalam directed acyclic graph (DAG). Dalam grafik ini, setiap node merepresentasikan tugas atau pekerjaan tertentu, dan tepi (panah yang menghubungkan node) merepresentasikan ketergantungan (data dan pengurutan) di antara mereka. Penjadwal adalah komponen yang memetakan tugas ke sumber daya dan mengatur eksekusi.
Contoh konkret dari model alur kerja tugas yang diterapkan pada komputasi kuantum adalah kerangka pola Qiskit. Pola Qiskit adalah kerangka kerja umum yang dirancang untuk memecah masalah domain-spesifik menjadi urutan tahap, terutama untuk tugas kuantum. Ini memungkinkan komposabilitas mulus dari kemampuan baru yang dikembangkan oleh peneliti IBM Quantumยฎ (dan lainnya) dan memungkinkan masa depan di mana tugas komputasi kuantum dilakukan oleh infrastruktur komputasi heterogen yang kuat (CPU/GPU/QPU). Empat langkah pola Qiskit adalah pemetaan, optimisasi, eksekusi, dan pasca-pemrosesan, di mana semua tugas dieksekusi satu demi satu dalam pipeline. Tetapi dengan alur kerja tugas kita tidak terikat pada urutan eksekusi linear dan dapat mengeksekusi tugas secara paralel. Setiap tugas alur kerja dapat menjadi pekerjaan paralel penuh tersendiri. Jadi, kamu dapat mencampur dan mencocokkan model-model ini untuk menggambarkan algoritma yang sangat kompleks, dan manajer beban kerja seperti Slurm akan menanganinya.
Gambar di atas mengilustrasikan pola Qiskit dalam aksi. Alur kerja memiliki struktur grafik dengan empat tahap. Struktur seperti cabang ini diatur dan dieksekusi oleh penjadwal. Masalah dipetakan ke dalam bentuk yang dapat dieksekusi secara kuantum (quantum circuit) pada tahap awal. Pada tahap berikutnya, quantum circuit ini dioptimalkan untuk perangkat keras kuantum tertentu. Gambar menunjukkan ini sebagai proses paralel, yang mendemonstrasikan bagaimana beberapa strategi optimisasi dapat diterapkan secara bersamaan. Quantum circuit yang dioptimalkan kemudian dieksekusi pada perangkat keras kuantum yang sebenarnya. Ini adalah tahap ketiga dari gambar di mana penjadwal bekerja dengan satu unit pemrosesan kuantum ungu. Akhirnya, hasilnya diproses pasca oleh sumber daya klasik.
Mengapa keduanya?โ
Jadi mengapa kita memerlukan pemrograman paralel dan alur kerja tugas? Dengan semua pembicaraan tentang paralelisme kuantum, layak untuk mengklarifikasi bahwa tidak semuanya paralel dalam komputasi kuantum.
Pelajaran sebelumnya tentang alur kerja SQD menyebutkan beberapa proses yang tidak dapat diparalelkan. Misalnya, kita memerlukan hasil dari banyak pengukuran kuantum untuk memproyeksikan matriks kita ke dalam subruang dengan dimensi yang dapat ditangani. Sebagai gantinya, kita memerlukan matriks yang didiagonalisasi dan vektor keadaan yang terkait untuk memeriksa konsistensi diri dari pengukuran kuantum (menggunakan, misalnya, konservasi muatan). Setelah semua itu, kita perlu memutuskan apakah energi ground state telah konvergen dengan cukup untuk tujuan kita. Langkah-langkah ini harus berurutan dan memerlukan pengujian kondisi konvergensi dan konsistensi diri sebelum melanjutkan.
Alur kerja ini akan ditinjau lebih rinci dan diimplementasikan di bagian berikutnya. Satu-satunya hal yang perlu kamu pahami dari bagian ini adalah bahwa alur kerja tugas diperlukan.
Praktik pemrogramanโ
Keindahan model pemrograman adalah kamu bisa mencampur dan mencocokkan semuanya. Dengan mengetahui model pemrograman kuantum dan klasik, kamu dapat menggambarkan komputasi heterogen dengan kompleksitas sembarang dan mengeksekusinya pada perangkat keras. Mari kita praktikkan ini dengan contoh kecil dari alur kerja gabungan, yang mengimplementasikan pola Qiskit (peta, optimalkan, eksekusi, dan pasca-proses) dalam Slurm yang kita pelajari di bab terakhir. Setiap empat tugas akan menjadi pekerjaan Slurm terpisah, masing-masing dengan sumber dayanya sendiri. Tugas optimisasi akan menggunakan MPI untuk mengoptimalkan circuit secara paralel (hanya untuk tujuan contoh, seperti gambar di atas). Tugas eksekusi akan menggunakan sumber daya kuantum dan model pemrograman kuantum (circuit dan Sampler). Tugas terakhir - pasca-pemrosesan - akan kembali menggunakan MPI secara paralel dengan sumber daya klasik.
Pemetaanโ
Program mapping.py dirancang untuk membangun circuit PauliTwoDesign, yang sering digunakan dalam literatur quantum machine learning dan literatur benchmark kuantum, dengan observabel sederhana yang mengukur qubit ke- dalam arah dari sistem -qubit dengan parameter awal acak. Masing-masing (quantum circuit yang dikonversi ke file qasm, observabel, dan parameter) akan disimpan ke file terpisah di bawah direktori data dan akan digunakan sebagai masukan dalam tahap optimisasi.
Skrip shell dari tahap ini (mapping.sh) adalah
#!/bin/bash
#
#SBATCH --job-name=mapping
#SBATCH --output=mapping.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=normal
srun python /data/ch3/workflows/mapping.py
yang mendefinisikan nama pekerjaan, format keluaran, dan jumlah node/tugas/CPU-nya.
Optimisasiโ
Program optimization.py dimulai dengan membawa file dari tahap pemetaan. Di sini kamu akan menggunakan QRMI untuk membawa sumber daya kuantum ke dalam program ini.
qrmi = QRMI()
resources = qrmi.resources()
quantum_resource = resources[0]
...
Kemudian melakukan optimisasi ringan dengan mengatur optimization_level=1 untuk mentranspilasi quantum circuit dan menerapkan tata letak circuit ke observabel, kemudian menyimpan ini ke folder data.
Skrip shell dari tahap ini (optimization.sh) adalah
#!/bin/bash
#SBATCH --job-name=optimization
#SBATCH --output=output/optimization.out
#SBATCH --ntasks=4
#SBATCH --partition=classical
srun python3 /tmp/optimization.py
Di sini --ntasks=4 meminta empat tugas klasik dari Slurm untuk proses paralel.
Eksekusiโ
Ini adalah tahap kuantum inti di mana quantum circuit yang dioptimalkan dari langkah sebelumnya dijalankan pada QPU oleh Estimator. Untuk melakukan ini, pertama kita akan membawa tiga file - quantum circuit yang ditranspilasi, observabel, dan parameter awal - kemudian meneruskannya ke Estimator. Ini menghasilkan nilai perkiraan dari observabel dan mencetaknya.
Skrip execution.sh memanfaatkan plugin Slurm untuk menggunakan sumber daya kuantum.
#!/bin/bash
#
#SBATCH --job-name=execution
#SBATCH --output=execution.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=quantum
#SBATCH --gres=qpu:1
srun python /data/ch3/workflows/execution.py
Pasca-pemrosesanโ
Langkah pasca-pemrosesan seringkali melibatkan diagonalisasi klasik dan pemeriksaan konsistensi diri. Ini juga mungkin bersifat iteratif. Paling berguna untuk mempertimbangkan langkah pasca-pemrosesan dalam pelajaran berikutnya, di mana konteks fisik dan tujuan dari langkah iteratif menjadi jelas.
Menggabungkan semuanyaโ
Kita dapat merantai semua tugas ini ke dalam alur kerja dengan menggunakan argumen ketergantungan untuk perintah sbatch:
$ MAPPING_JOB=$(sbatch --parsable mapping.sh)
$ OPTIMIZE_JOB=$(sbatch --parsable --dependency=afterok:$MAPPING_JOB optimization.sh)
$ EXECUTE_JOB=$(sbatch --parsable --dependency=afterok:$OPTIMIZE_JOB execute.sh)
Dan kita dapat memeriksa antrian eksekusi Slurm kita.
$ squeue
# JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
# 3 classical mapping admin PD 0:00 1 (None)
# 4 classical optimiza admin PD 0:00 1 (Dependency)
# 5 quantum execute admin PD 0:00 1 (Dependency)
Ini adalah contoh mainan untuk mendemonstrasikan campuran model pemrograman. Dalam bab berikutnya kita akan melihat algoritma dunia nyata dan mendemonstrasikan model pemrograman dan manajemen sumber daya pada alur kerja yang berguna.
Ringkasanโ
Dalam pelajaran ini, kita telah mendemonstrasikan cara menggabungkan beberapa model pemrograman klasik dan kuantum untuk membangun, mengelola, dan mengeksekusi alur kerja empat tahap yang lengkap. Kita mulai dengan konsep fundamental dari quantum circuit dan primitif, kemudian mengeksplorasi model klasik seperti pemrograman paralel dan alur kerja tugas. Dengan menggabungkan semua konsep, kita membangun pola Qiskit โ peta, optimalkan, eksekusi, dan pasca-proses โ yang diatur oleh manajer beban kerja Slurm dengan quantum circuit sederhana dan sebuah observabel.
Dalam pelajaran berikutnya, kita akan menggunakan kerangka ini untuk menjalankan algoritma kuantum berbasis sampel, menunjukkan bagaimana alur kerja ini dapat diterapkan untuk memecahkan masalah yang bermakna.
Semua kode dan skrip yang digunakan dalam bab ini tersedia untukmu dalam repositori Github ini.