Mga modelo ng programming
Ang mga modelo ng programming ay mga pangunahing espesipikasyon na nagtatakda kung paano inuugos at inisakatuparan ang software. Nagbibigay sila ng balangkas para sa mga developer upang ipahayag ang mga algorithm at ayusin ang code, madalas na itinatago ang mga mababang antas na detalye ng pinagbabatayan na hardware o kapaligiran ng pagpapatupad. Iba't ibang modelo ang angkop sa iba't ibang uri ng mga problema at arkitektura ng hardware, nag-aalok ng iba't ibang antas ng abstraction at kontrol.
Sa araling ito, susuriin natin ang quantum at classical na mga modelo ng programming at makikita kung paano natin maaaring pagsamahin ang mga ito upang mapatakbo ang mga algorithm sa mga heterogeneous na kapaligiran. Binibigyan tayo ni Iskandar Sitdikov ng pangkalahatang-ideya sa sumusunod na video.
Modelo ng programming para sa mga QPUโ
Magsisimula tayo sa modelo ng programming para sa mga quantum computer. Ang pangunahing modelo ng programming na pamilyar sa halos lahat ng quantum developer ay ang quantum circuit. Hindi natin tatalakayin ang mga detalye ng modelo ng quantum circuit dito, dahil mayroon na tayong magandang lektura ni John Watrous na nagpapaliwanag nito nang detalyado. Banggitin lamang natin na ang circuit ay binuo mula sa isang hanay ng mga linya (tinatawag na mga wire) na kumakatawan sa mga qubit, mga gate na kumakatawan sa mga operasyon sa mga quantum state, at isang hanay ng mga pagsukat.
Isa pang mahalagang konsepto ng modelo ng programming para sa quantum computing ay ang tinatawag nating computational primitives. Ang mga primitive na ito ay kumakatawan sa ilan sa mga pinakakaraniwang gawain na layunin ng mga gumagamit na maisakatuparan sa isang quantum computer. May ilang primitive na available sa kasalukuyan, kasama ang Executor. Sa kursong ito, pangunahing magtutuon tayo sa mga primitive na Sampler at Estimator. Ang Sampler ay nagbibigay sa iyo ng kakayahang mag-sample ng isang state na inihanda ng iyong quantum circuit. Sinasabi nito kung aling mga computational basis state ang bumubuo sa quantum state na inihanda sa iyong quantum circuit. Ang Estimator naman ay nagbibigay-daan sa iyo na tantiyahin ang expected value ng isang observable para sa isang sistema sa state na inihanda ng iyong quantum circuit. Isang karaniwang konteksto ay ang pagtantiya ng enerhiya ng isang sistema sa isang tiyak na estado.
Ang huling bagay na tatalakayin natin sa seksyong ito ay ang transpilation. Ang transpilation ay ang proseso ng pagsulat muli ng isang ibinigay na input circuit upang tumugma sa mga pisikal na limitasyon at Instruction Set Architecture (ISA) ng isang tiyak na quantum device. Katulad ng mga classical compiler, nangangahulugan ito ng pagsasalin ng mga abstract na unitary operation sa native gate set na maaaring ipatupad ng target na device. Nino-optimize rin nito ang mga instruksyon ng circuit para sa mahusay na pagpapatupad sa mga maingay na quantum computer, kung saan ang routine ay unti-unting nagbabago ng istraktura ng circuit sa pamamagitan ng paglalapat ng ilang mga yugto ng optimization.
Suriin ang iyong pag-unawaโ
Ilang qubit ang nasa circuit sa ibaba?

Sagot:
Apat.
Suriin ang iyong pag-unawaโ
Sabihin nating nagmo-modelo ka ng mga elektron sa isang molekula. Gusto mong tantiyahin ang (a) ang ground state energy ng molekula, at (b) kung aling mga computational basis state ang pinakadominate sa ground state ng molekula. Sa bawat kaso, gagamitin mo ba ang Estimator o Sampler primitive?
Sagot:
(a) Estimator (b) Sampler
Mga classical na modelo ng programmingโ
Maraming modelo ng programming para sa mga classical computer, ngunit para sa seksyong ito ay magtutuon tayo sa dalawa sa mga pinakasikat: parallel programming at task workflows. Gamit ang dalawang modelong ito kasabay ng mga quantum na modelo ng programming, maaaring ipahayag ng isa ang halos anumang hybrid quantum-classical workflow ng anumang kumplikasyon.
Parallel programmingโ
Ang parallel programming ay isang modelo na naghahatid ng isang programa sa mga sub-problema na maaaring isagawa nang sabay-sabay. May dalawang pangunahing paradigma ng parallel programming:
-
Shared memory parallelism (Open Multiprocessing, o OpenMP): Ginagamit upang samantalahin ang maraming core sa loob ng isang compute node. Ang mga thread ng pagpapatupad ay nagbabahagi ng iisang memory space.
-
Distributed memory parallelism (Message Passing Interface, o MPI): Ginagamit para sa pag-scale sa maraming hiwalay na compute node. Ang bawat proseso ay may sariling isolated na memory space.
Dito, magtutuon tayo sa distributed memory model dahil mahalaga ito para sa multi-node supercomputing at pag-coordinate ng malalaking heterogeneous quantum-classical job.
May ilang konsepto na kailangan nating maunawaan upang makapag-operate sa mga distributed memory parallel programming model:
- Proseso - Isang independiyenteng instansya ng programa na may sariling memory space.
- Rank - Isang natatanging integer identifier na inilaan sa bawat proseso, ginagamit partikular upang tukuyin ang nagpadala at tatanggap sa panahon ng komunikasyon (hindi nangangahulugang "rank" sa diwa ng pagbibigay-prioridad).
- Synchronization - Isang mekanismo para sa koordinasyon sa pagitan ng iba't ibang rank at proseso.
- Single program, multiple data (SPMD) - Isang abstract na computational model kung saan ang isang solong source code instance ay tumatakbo nang sabay-sabay sa maraming proseso, bawat isa ay gumagana sa ibang subset ng kabuuang data.
- Message passing - Ang paradigma ng komunikasyon na ginagamit sa mga distributed memory architecture na nagbibigay-daan sa mga independiyenteng proseso na mag-palitan ng data at mga intermediate na resulta. Umaasa ito sa mga tahasang operasyong 'send' at 'receive' upang i-coordinate ang pagpapatupad sa pagitan ng iba't ibang compute node.
Mayroon itong pamantayan na tinatawag na MPI na nagpapatupad ng message passing paradigm na ito para sa mga parallel na arkitektura. Ang MPI ay nagsisilbing functional na kabuuan ng lahat ng mga konsepto na nakalista sa itaas, nagbibigay ng mga tiyak na library call na kinakailangan upang pamahalaan ang mga proseso, magtalaga ng mga rank, mapadali ang synchronization, at paganahin ang message passing sa ilalim ng SPMD model. Pinagsama-sama ang lahat ng mga konseptong ito, masasabi natin na ang pagpapatupad ng isang parallel na programa ay nangyayari sa sumusunod na paraan:
- Ang isang solong compiled na programa (ang parehong binary file) ay kinokopya at pinatakbo ng isang job launcher upang lumikha ng maraming parallel na proseso sa maraming node.
- Ang pangunahing control flow ng programa ay diktado ng rank ng proseso. Ito ang SPMD na prinsipyo sa aksyon: gumagamit ang programa ng conditional logic (halimbawa,
if (rank == 0)) upang matiyak na ang ilang partikular, na-parallelize na seksyon ng code ay isinasagawa lamang ng mga worker process, habang ang isang master process (madalay ay Rank 0) ang humahawak ng initialization at huling aggregation. - Ang komunikasyon sa pagitan ng mga proseso ay nagaganap sa pamamagitan ng message passing (gamit ang MPI), na tinatawag kapag kailangan ng isang proseso na mag-palitan ng data o mga intermediate na resulta sa ibang rank.
Sa visual, magiging ganito ang hitsura:
Subukan nating ilapat ang ilan sa mga konseptong natutunan natin sa code.
Una, susubukan nating patakbuhin ang isang simpleng "hello world" parallel na programa gamit ang OpenMPI, na isang implementasyon ng MPI protocol, isang pamantayan para sa message passing sa parallel programming. Dito, gagamitin natin ang mpi4py Python package, na isang Python binding para sa Message Passing Interface (MPI) standard.
$ 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")
~
~
Gagamitin natin ang dalawang node para patakbuhin ang programang ito, na ititiyak natin sa ating submission script.
$ 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
Pagkatapos ay patakbuhin ang shell script.
$ sbatch mpi-hello-world.sh
Maaari nating suriin ang mga result log ng job.
$ 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}
Dito ay gumamit tayo ng dalawang node at ang proseso sa bawat node ay natukoy na ngayon ng isang rank - Rank 0 at Rank 1 - na ginagamit upang matukoy ang control flow ng programa.
Mga task workflowโ
Pag-usapan naman natin ang Task workflow programming model. Ang isang task workflow ay nag-a-abstract ng computation sa isang directed acyclic graph (DAG). Sa graph na ito, ang bawat node ay kumakatawan sa isang partikular na gawain o trabaho, at ang mga edge (ang mga arrow na nagkokonekta sa mga node) ay kumakatawan sa mga dependency (data at pagkakasunod-sunod) sa pagitan nila. Ang scheduler ay ang bahagi na nagmamapa ng mga gawain sa mga mapagkukunan at nag-o-orchestrate ng pagpapatupad.
Isang kongkretong halimbawa ng isang task workflow model na inilapat sa quantum computing ay ang Qiskit patterns framework. Ang isang Qiskit pattern ay isang pangkalahatang balangkas na dinisenyo upang hatiin ang mga domain-specific na problema sa isang pagkakasunod ng mga yugto, lalo na para sa mga quantum na gawain. Nagbibigay-daan ito para sa maayos na pagsasama ng mga bagong kakayahang ginawa ng mga mananaliksik ng IBM Quantumยฎ (at iba pa) at nagbubukas ng kinabukasan kung saan ang mga quantum computing na gawain ay isinasagawa ng makapangyarihang heterogeneous (CPU/GPU/QPU) na computing infrastructure. Ang apat na hakbang ng isang Qiskit pattern ay mapping, optimization, execution, at post-processing, kung saan ang lahat ng gawain ay isinasagawa nang isa-isa sa isang pipeline. Ngunit sa mga task workflow hindi tayo nakatali sa isang linear na pagkakasunod ng pagpapatupad at maaaring magsagawa ng mga gawain nang sabay-sabay. Ang bawat gawain ng isang workflow ay maaaring maging isang buong parallel na trabaho sa sarili nito. Kaya, maaari mong pagsamahin ang mga modelong ito upang ilarawan ang mga arbitraryong kumplikadong algorithm, at ang isang workload manager tulad ng Slurm ang hahawak nito.
Ang imahe sa itaas ay naglalarawan ng Qiskit pattern sa aksyon. Ang workflow ay may istraktura ng graph na may apat na yugto. Ang branch-like na istruktura na ito ay inioorkestra at isinasagawa ng scheduler. Ang problema ay nima-map sa quantum-executable na anyo (quantum circuit) sa unang yugto. Sa susunod na yugto, ang quantum circuit na ito ay ino-optimize para sa tiyak na quantum hardware. Ipinapakita ito ng imahe bilang isang parallel na proseso, na nagpapakita kung paano maaaring ilapat nang sabay-sabay ang maraming estratehiya ng optimization. Ang na-optimize na quantum circuit ay pagkatapos ay isinasagawa sa aktwal na quantum hardware. Ito ang ikatlong yugto ng imahe kung saan ang scheduler ay gumagawa kasama ang isang purple na quantum processing unit. Sa wakas, ang mga resulta ay post-process ng mga classical na mapagkukunan.
Bakit parehong kailangan?โ
Kaya bakit kailangan natin ang parehong parallel programming at task workflows? Sa lahat ng usapan tungkol sa quantum parallelism, sulit na linawin na hindi lahat ay parallel sa quantum computing.
Binanggit ng nakaraang aralin sa SQD workflow ang ilang mga prosesong hindi maaaring i-parallelize. Halimbawa, kailangan natin ang mga resulta ng maraming quantum na pagsukat upang ma-project ang ating matrix sa isang subspace ng tractable na dimensyon. Sa turn, kailangan natin ang diagonalized na matrix at ang mga kaugnay na state vector upang suriin ang self-consistency ng mga quantum na pagsukat (gamit, halimbawa, ang charge conservation). Pagkatapos ng lahat ng iyon, kailangan nating tukuyin kung ang ground state energy ay sapat na na-converge para sa ating mga layunin. Ang mga hakbang na ito ay kinakailangang sequential at nangangailangan ng pagsubok ng mga kondisyon ng convergence at self-consistency bago magpatuloy.
Ang workflow na ito ay muling tatalakayin nang mas detalyado at ipapatupad sa susunod na seksyon. Ang tanging bagay na kailangan mong kunin mula sa seksyong ito ay ang mga task workflow ay kinakailangan.
Pagsasanay sa programmingโ
Ang kagandahan ng mga modelo ng programming ay maaari mong pagsamahin ang lahat ng ito. Sa pag-alam ng mga quantum at classical na modelo ng programming, maaari kang maglalarawan ng isang heterogeneous na computation ng anumang kumplikasyon at ipatupad ito sa hardware. Magsanay tayo nito sa isang maliit na halimbawa ng isang pinagsanib na workflow, na nagpapatupad ng Qiskit pattern (map, optimize, execute, at post-process) sa loob ng Slurm na natutunan natin sa nakaraang kabanata. Ang bawat isa sa apat na gawain ay magiging isang hiwalay na Slurm job, bawat isa ay may sariling mga mapagkukunan. Ang optimization task ay gagamit ng MPI upang mag-optimize ng mga circuit nang sabay-sabay (para lamang sa halimbawa, tulad ng imahe sa itaas). Ang execution task ay gagamit ng quantum na mapagkukunan at mga quantum programming model (circuit at sampler). Ang huling gawain - post-processing - ay muli ay gagamit ng MPI nang parallel sa mga classical na mapagkukunan.
Mappingโ
Ang mapping.py na programa ay dinisenyo upang bumuo ng isang PauliTwoDesign circuit, na madalas na ginagamit sa quantum machine learning literature at quantum benchmark literature, na may isang simpleng observable na sumusukat sa qubit sa direksyon ng isang -qubit na sistema na may random na mga paunang parameter. Ang bawat isa sa mga ito (ang quantum circuit na na-convert sa isang qasm file, ang observable, at ang mga parameter) ay mase-save sa isang hiwalay na file sa ilalim ng data directory at gagamitin bilang input sa optimization stage.
Ang shell script ng yugtong ito (mapping.sh) ay
#!/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
na nagtatakda ng pangalan ng trabaho nito, format ng output, at bilang ng mga node/task/CPU.
Optimizationโ
Ang optimization.py na programa ay nagsisimula sa pamamagitan ng pagdadala ng mga file mula sa mapping stage. Dito ay gagamitin mo ang QRMI upang dalhin ang mga quantum na mapagkukunan sa programang ito.
qrmi = QRMI()
resources = qrmi.resources()
quantum_resource = resources[0]
...
Pagkatapos ay nagsasagawa ito ng magaang na optimization sa pamamagitan ng pagtatakda ng optimization_level=1 upang i-transpile ang quantum circuit at ilapat ang layout ng circuit sa observable, pagkatapos ay i-save ang mga ito sa data folder.
Ang shell script ng yugtong ito (optimization.sh) ay
#!/bin/bash
#SBATCH --job-name=optimization
#SBATCH --output=output/optimization.out
#SBATCH --ntasks=4
#SBATCH --partition=classical
srun python3 /tmp/optimization.py
Dito ang --ntasks=4 ay humihiling ng apat na classical na gawain mula sa Slurm para sa isang parallel na proseso.
Executionโ
Ito ang pangunahing quantum stage kung saan ang na-optimize na quantum circuit mula sa nakaraang hakbang ay pinapatakbo sa QPU ng Estimator. Upang gawin ito, una ay magdala tayo ng tatlong file - ang na-transpile na quantum circuit, ang observable, at ang mga paunang parameter - pagkatapos ay ipasa ito sa Estimator. Nagbubunga ito ng tinantyang halaga ng observable at inilalabas ito.
Ang execution.sh script ay gumagamit ng isang Slurm plugin upang gumamit ng quantum na mapagkukunan.
#!/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
Post-processingโ
Ang hakbang ng post-processing ay madalas na kinabibilangan ng classical diagonalization at mga self-consistency check. Maaari rin itong maging iterative. Pinaka-kapaki-pakinabang na isaalang-alang ang hakbang ng post-processing sa susunod na aralin, kung saan ang pisikal na konteksto at ang layunin ng mga iterative na hakbang ay malinaw.
Pinagsama-sama ang lahatโ
Maaari nating i-chain ang lahat ng gawaing ito sa isang workflow sa pamamagitan ng paggamit ng dependency argument para sa sbatch command:
$ 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)
At maaari nating suriin ang ating Slurm execution queue.
$ 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)
Ito ay isang toy example upang ipakita ang pagsasama ng mga modelo ng programming. Sa susunod na kabanata, titingnan natin ang mga real-world na algorithm at ipapakita ang mga modelo ng programming at pamamahala ng mapagkukunan sa mga kapaki-pakinabang na workflow.
Buodโ
Sa araling ito, ipinakita natin kung paano pagsamahin ang maraming classical at quantum na modelo ng programming upang bumuo, pamahalaan, at ipatupad ang isang kumpletong apat na yugtong workflow. Nagsimula tayo sa mga pangunahing konsepto ng mga quantum circuit at primitive, pagkatapos ay tinalakay ang mga classical na modelo tulad ng parallel programming at task workflows. Sa pamamagitan ng pagsasama ng lahat ng mga konsepto, nagtatag tayo ng isang Qiskit pattern โ map, optimize, execute, at post-process โ na inioorkestra ng Slurm workload manager na may simpleng quantum circuit at isang observable.
Sa susunod na aralin, gagamitin natin ang balangkas na ito upang patakbuhin ang mga sample-based quantum algorithm, na nagpapakita kung paano maaaring ilapat ang workflow na ito upang malutas ng mga makabuluhang problema.
Lahat ng code at script na ginamit sa kabanatang ito ay available sa iyo sa loob ng Github repository na ito.