Kvantum programozási nyelvek

Házi feladat a BMEVIHIAV06 tantárgyhoz; a kvantum programozás elméleti bemutatása, már elérhető nyelvek és módszerek áttekintése.

Tartalomjegyzék

  1. Bevezetés
  2. Kvantumprogramok jellemzői
  3. Kvantum programozási nyelvek
    1. Kvantum pszeudokód
    2. Quipper
    3. Q#
  4. Összefoglalás

Bevezetés

A programok rövid története

A szoftverfejlesztés viszonylag új szakterület. A programozás ágazat kezdete megközelítőleg az 1950-es évekre rakható. Az ipari forradalmaknak köszönhetően ekkorra már fejlett gépek, elektronikus technológiák álltak a szakemberek rendelkezésére a számítógépek megépítéséhez, kezdődhetett a programnyelvek, szoftver tervezési folyamatok célzott, irányított létrehozása. A programozás bő fél évszázados múltja alatt ezek drasztikusan átalakultak, a hatékonyságok és komplexitásuk sokszorosára fejlődött, a folyamatok kiszámíthatóbbak, gördülékenyebbek lettek. Kezdetben csak 1-1 művelet sikeres, automatizált végrehajtása volt a cél, napjainkban azonban számos más dolog is fontos. Voltak programnyelvek, amik hibásan lettek megtervezve, használatuk túl bonyolult volt emberek számára, nehéz volt leírni bennük komplex rendszereket, feladatokat, nem alkalmazkodtak a fejlesztendő program struktúrájához. Ezekből sokat tanultunk, a tapasztalataink alapján újabb és újabb rendszereket fejlesztettünk ki. Manapság már óriási szoftvereket építünk, bonyolult algoritmusokat hozunk létre, számtalan mérnök foglalkozik egyszerre a feladattal, az elkészült programoktól pedig emberéletek függhetnek. Egyre rövidebb idő alatt, egyre nehezebb programot, egyre kevesebb pénzből kell fejlesztenünk, alkalmazkodva a kor elvárásának.

A klasszikus, elektronikus számítógépek azonban kezdenek csúcspontjukhoz jutni. Egy idő után már nem lehet gyorsaságukon fejleszteni, a technológia eléri határait, az algoritmusokat nem lehetséges tovább optimalizálni. Pedig rohamos igény lenne még a programok hatékonyságnövelése iránt, hiszen csak mostanában kezdenek igazán teret hódítani pl. a rengeteg számítást igénylő neurális hálózatok, vagy az egyre élethűbb kinézetű videójátékok, a tömérdek adat feldolgozása. Emiatt számtalan kutató cég foglalkozik azzal, hogy létre tudják hozni az elektronikus technológia utódját, a kvantummechanikára alapozó kvantumszámítógépet. Ezek az új gépek lényegesen más módszerekkel dolgoznak, elvi teljesítményük sokszorosa az elektronikus gépeknek.

A kvantum-programozás területe mostanában kezd megszületni. Napjainkban a kvantumszámítógép még fejlesztés alatt áll, messze vagyunk attól, hogy használatuk olyan széles körben elterjedt legyen, mint a mostani elektronikus személyi, vagy szerver oldali számítógépeké. Ennek ellenére egyre több és több kvantum programozási nyelv jelenik meg, számtalan olyan algoritmust megterveztek már, amit később kvantum-számítógépeken akarnak futtatni.

Igény a kvantum programokra

Mi értelme van kvantumprogramokat fejleszteni, ha még nem is igazán tudjuk min végrehajtani ezeket a programokat? A klasszikus programozás fejlődése során bebizonyosult, hogy sok elmélet hibás, és ezek a hibák csak elterjedt használatuk után derültek ki, rengeteg kárt okozva a fejlesztőknek és felhasználóknak egyaránt. Sok, korábban hatékonynak hitt algoritmushoz találtak annál sokkal gyorsabb, kevesebb memóriaigényű stb. változatot, biztonságosnak hitt protokollokban találtak támadható pontot. Mivel a kvantumszámítógépnek nagy jövőt jósolnak, tudhatjuk, hogy a közeljövőben valós igény lesz a kvantum-programokra. Annak érdekében, hogy minimalizáljuk a hibás elméletek és fejlesztési módszerek alkalmazását, vagy éppen felfedezzük, később mit tudunk még alkalmazni, a kvantumszámítógépekre szánt programok fejlesztése már most is elkezdődött. A sok évtizedekig tartó algoritmusok, protokollok fejlesztése és ellenőrzése már most is lehetséges. Szükséges, hogy találjunk olyan módszereket, amik leválthatják azokat az eljárásokat, amiket napjainkban használunk a klasszikus számítógépeknél, de egy kvantumszámítógépnek egyszerű lenne megkerülnie, hogy ha üzembe tudnak már helyezni nagyszámú kvantumszámítógépet, a szolgáltatások továbbra is működőképesek maradhassanak. Ilyen pl. a titkosított kommunikációra használt RSA algoritmus, ami az elektronikus számítógépeknél még kellő védelmet ad, de ha a kvantumszámítógépek elterjednek, már nem lehetne az interneten biztonságosan fizetni. Mivel azonban nem akarunk lemondani a biztonságos internetes fizetésről, más módszereket kell felkutatnunk, amik matematikailag bizonyítottan biztonságosak tudnak maradni a jövőre nézve is.

A klasszikus és kvantum programok jövője

A kvantum-számítógépek megjelenésével nem érdemes eltemetni az elektronikus számítógépeket. Várhatóan a két technológia nem lecseréli egymást, hanem együtt működnek tovább. Nem éri meg ugyanis minden feladatra a (most még) méregdrága csúcstechnológiát használni. Vegyünk például egy kézi számológépet. Igaz ugyan, hogy nem tud egy nagyon bonyolult, 100 sort elfoglaló egyenletet több millió tizedesjegy pontosságú eredményét kikalkulálni egy szempillantásnyi idő alatt, na de kinek lenne erre igénye? A grafikus kártyák teljesítménye és a monitor frissítési gyorsasága se teszi lehetővé (jelenleg), hogy másodpercenként több millió, teljesen élethű képkockát megjelenítsen egy program, azonban tudjuk, hogy az emberi szem nem is lenne képes értelmezni ezt a sok részletes információt kellő gyorsasággal. Egy táblázatos adminisztrátori program működésére aligha lenne hatással, ha azt kvantumszámítógépen futtatnánk. A kvantumprogramokat számításigényes (és ebből adódóan időigényes) feladatoknál éri meg használni.

Többnyire a kényelmes, emberek által jól értelmezhető programnyelveketben megírt feladatokat le kell fordítani egy, a gép, vagy az azon futó platform által közvetlenül, egyszerűen értelmezhető, optimalizált formára. Problémát jelent ugyanakkor, hogy még nem nagyon tudjuk, milyen lesz a kvantum-számítógépek interfésze, azaz, hogy hogyan lehet hozzáférni kívülről. Jelenleg 1-2 gyártó rendelkezik kísérleti kvantumszámítógéppel. Mint az várható minden felfedezés esetében, ebben a fázisban nem a kényelmes használatra koncentrálnak, hanem a működés elérésére. Így volt ez a klasszikus számítógépek esetében is: kezdetben minimális utasításkészletet támogattak a processzorok, de új igények jelentek meg, így kevésnek bizonyultak. Ma már számtalan utasításkészlet érhető el, ám kompatibilitási okok miatt az x86 architektúra mégis elterjedt maradt, a teljesítmény rovására. Remélhetőleg, a kvantum számítógép- és programnyelv tervezők tanulni fognak ebből az esetből, számolnak azzal, hogy a kísérletek után sok változásra kell felkészülniük. A mostani szimulátorok, és a jövőbeli kvantumszámítógépek interfészét össze kell egyeztetni minél inkább, hogy a már elkészített programok átvihetők legyenek.

Ha a kvantumszámítógép elérhető lesz, a kvantum programnyelvek nagy lendületet fognak kapni. Új fejlesztőkörnyezeteket hoznak létre, vagy ha lehetséges, beépítik a már meglévő rendszerekbe a kvantum programok készítéséhez szükséges eszközöket. Várhatóan lesznek próbálkozások arra, hogy egyes programnyelveket kiterjesszék a kvantum programok világára is, hogy a fejlesztők egységes nyelven tudjanak fejleszteni a kettő között, ne kelljen külön megtanulni a sok dologban hasonlító eljárásokat. A jelenlegi szimulátorokat leváltják majd a valós fordítóprogramok.

Kvantumprogramok jellemzői

Megjegyzés: A programok leírását, értelmezését lehetővé tevő nyelveket típusuk szerint különféle kategóriákba soroljuk, és emiatt eltérően is hívjuk őket (pl. a HTML jelölő, a Java programozási, a VHDL hardverleíró nyelv, az x86 pedig egy utasításkészlet), noha programokkal kapcsolatos területen használjuk fel őket. A továbbiakban a “kvantum programozási nyelv” főleg csak a kvantumszámítógépekkel és programokkal kapcsolatba hozható jellemzésre használt, nem pedig a nyelv céljának, típusának meghatározására.

Eltérések a klasszikus és kvantum programok között

A klasszikus programok felépítése néhány szempontból hasonlít a kvantum programokéra, más esetekben azonban gyökeres eltérések vannak köztük. Néhány ilyen eltérés (a teljesség igénye nélkül):

A felsorolt eltéréseknek számtalan további következménye is van, ami szintén eltérést eredményez. Ilyen pl. a kvantum teleportálás, ahol egy adatot úgy tudunk elküldeni másnak, hogy a valódi értékét ismernénk. A jelenlegi klasszikus számítógépek csak ismert biteket tudnak továbbítani a kábeleken keresztül.

Kvantum programozási nyelvek csoportosítása

Bár még jóval kevesebb nyelvet készítettem a kvantum programokhoz, mint a klasszikusokhoz, már most is elég sok kategóriát lefednek az elérhető nyelvek, így különféle területeken, különféle feladathoz lehet őket felhasználni. A programozási nyelvek csoportosíthatóak többek között a következők szerint is:

További csoportosítási szempontok átvehetőek a klasszikus programozási nyelvek esetéből, mint pl. erősen típusús nyelvek, natív vagy értelmezett nyelvek. Ezeknek a csoportoknak azonban még nem igazán van szerepük jelenleg.

Kvantum programozási nyelvek

Ebben a fejezetben néhány, egymástól eléggé eltérő kvantum programozással kapcsolatos nyelvet ismerhetünk meg. Az összehasonlíthatóság érdekében mindegyik nyelvnél találunk egy példát, ami a jól ismert Deutsch–Jozsa algoritmust mutatja be.

A feladat a következő: Adott egy függvény, f(x), amiről csak annyit tudunk, hogy néha egy konstans értéket rendel a bemenethez, máskor viszont úgy viselkedik, mint egy pénzfeldobás: az esetek felében a, másik felében b értéket rendel a bemenethez. Határozzuk meg, egy konkrét esetben f melyik szabály szerint működik!

A feladat megoldható klasszikus programokkal is, azonban lépésszámra jóval nagyobb, mint amennyire a kvantumprogramoknak szüksége van. Egy lehetséges klasszikus megoldás, Java nyelven:

package DeutschJozsaTest;

import java.util.Random;
import java.util.function.IntUnaryOperator;

public class Main {
    public static void main(String[] args) {
        IntUnaryOperator function;
        int constantValue = new Random().nextInt(2);
        if (args[0].equals("constant"))
            function = (x) -> constantValue;
        else
            function = (x) -> (x + constantValue) % 2;

        int n = Integer.parseInt(args[1]);
        
        int result = 0;
        for (int i = 0; i < n/2+1; ++i)
            result += function.applyAsInt(i);
        
        boolean isConstant = (result == 0 || result == n/2+1);
        String type = isConstant ? "constant" : "balanced";
        System.out.println("Function is " + type + ".");
    }
}

Kvantum pszeudokód

A pszeudokód nem tekinthető programozási nyelvnek, hiszen a célja pont az, hogy absztrakt módon, konkrét implementáció nélkül lehessen leírni vele az algoritmusokat. A pszeudokód elvben bármilyen másik programozási nyelvre átalakítható egyszerűen, majd annak felhasználásával futtatható. A pszeudókód célja, hogy az emberek számára könnyen értelmezhető, csak a lényeges dolgokra koncentrálva, nyelvfüggetlen leírást adhassunk különféle algoritmusokról.

A pszeudokódoknak többféle változata is van, kisebb-nagyobb eltérésekkel. Ezek többnyire a megértést nem akadályozzák, mert pl. csak másfajta jelölést használnak az értékadáshoz (let x be zero, vagy x := 0). A könnyebb kezelhetőség érdekében készült néhány ajánlás a pszeudokódok készítésére.

A hagyományos pszeudokódnak van kvantumos változata is. A felhasználási cél egyező: algoritmusok leírása implementációtól függetlenül, csak éppen már kvantumszámítógép meglétét feltételezve. A kvantum pszeudokód nem sokban különbözik a hagyományostól, mert bár sok új funkciót lehetséges kvantum programokkal elkészíteni, ezek többségének leírása a hagyományos formában is lehetséges (pl. az unitér operátorok hagyományos metódusokkal leírhatók).

A kvantum pszeudokódra is készült egy ajánlás. E. Knill készítette 1996-ban a kezdetleges ajánlást, azóta módosítás nem történt rajta. Ez a dokumentum nem tárgyalja a hagyományos pszeudokód elemeit, csak a kvantumprogramozással kapcsolatos dolgokkal egészíti ki azt. Az ajánlás egy része formai leírás. Fő elemei:

A Deutsch–Jozsa algoritmus leírása kvantum pszeudokóddal

DeutschJozsa(n, f)
Input: f egy konstans vagy kiegyensúlyozott függvény,
       n pedig az f függvény bemeneti kvantumregiszerének
       dimenziója (a qbit-ek száma).
Output: Klasszikus igaz, ha f konstans, illetve hamis,
        ha kiegegysúlyozott.


control 🠄 Initialize(0, n)
    C: A kvantumregiszterbe betöltünk n darab 0 fázisú qbitet.
data 🠄 Initialize(1)
state0 🠄 Merge(control, data)
    C: A state0 a két qregiszer összevont állapotát jelzi.
state1 🠄 H⊗n+1(state0)
    C: Mindegyik qbiten alkalmazunk egy Hadamard kaput.
state2 🠄 U-Controlled-CNOT(f, control, data)
    C: Az U-Controlled-CNOT egy olyan kaput jelöl, ami a CNOT kapuhoz
       hasonlóan működik, de a data ágon y ⊕ f(x) lesz a hatása.
state3 🠄 H⊗n(state2)value 🠄 state3
    C: Megmérjük a qregisztert, eredményül klasszikus állapotot kapunk.
if value is -1 or 1 then
     result 🠄 true
else
     result 🠄 false

Értékelés

Quipper

A Quipper nyelvet 2013-tól kezdték el fejleszteni több ember közreműködésével. A Quipper tulajdonképpen Haskell könyvtárak és segédprogramok gyűjteménye, aminek felhasználásával készíthetünk Haskell nyelven írt kvantumprogramot. Számtalan kvantum programozással kapcsolatos műveletet, típust, eljárást biztosít a Quipper a fejlesztő számára, illetve sok, előre megírt algoritmus található benne. A Quipper képes szimulációra, a leírt kapuk/adatokról képeket készíteni. Ebben a fejezetben az utóbbival foglalkozunk csak.

A Haskell egy funkcionális programozási nyelv, ami azt jelenti, hogy a leírt műveletek nem egymás után, szekvenciálisan hajtódnak végre (mint pl. egy C vagy Java nyelvben), hanem jellemezhetjük a dolgokat, majd ennek a leírásnak segítségével, amint szükség van rá, a számítógép kikalkulálja az eredményt. A szekvenciális programozás esetében azt mondjuk meg a gépnek, hogy mit csináljon, funkcionális programozás esetében viszont csak azt, hogy hogyan tudja az eredményt megkapni amikor kell. Ez a két programozási módszer lényegesen eltér egymástól, nehéz a kettő közti váltás a fejlesztők számára. Néhány klasszikus számítógép támogatja CPU szinten a funkcionális végrehajtást, de többnyire a funkcionálisan leírt programok is speciális, szekvenciálisan végrehajtott programokká fordulnak számítógépeken.

Kvantumszámítógépek esetében viszont más a helyzet. A funkcionális nyelv alkalmazása, és a hálózati kép kimenet nem véletlen: jól mutatja, hogy a kvantum programok jelenleg eltérnek a klasszikus programoktól, amiknek többsége szekvenciálisan, előre nem látható műveleteket hajthat végre, a kvantum programok többsége viszont vezetékekkel összekapcsolt kapuk sorozatából áll (másként mondva operátorokat alkalmazunk egymás után), ennek a hálózatnak az elkészítése időfüggetlen. A kvantum programok hasonlítanak a klasszikus, elektronikus hardverekhez, az áramkörök leírására is funkcionális nyelvet szokás használni: két komponens közti kapcsolat leírásánál nem azt mondjuk meg, mit csináljon a program, hanem csak jellemezzük a kapcsolatukat, a végeredményként létrejött áramkör már magától tud működni megfelelően.

A Quipper programokat egy quipper script futtatásával lehet lefordítani. Ez a script néhány beállítás hozzáadása után továbbítja a kódot a Haskell fordítójának. Eredményként egy futtatható állományt kapunk, ami pl. szimulációt, vagy képgenerálást végezhet el. Ha felhasználjuk a Preview-t, akkor az Adobe Acrobat Reader-ben megnyílik a generált hálózat ábrája.

Ez a generált hálózati ábra a megszokott formában tartalmazza a kvantum hálózatot. A létrehozott qbiteket vezetékek reprezentálják. A qbiteken végrehajtott operációkat különféle szimbólumokkal, többnyire egy négyszög dobozzal jelölik. A négyszög azokat a vezetékeket érinti, amin végrehajtódik az operáció. Az ábrát balról jobbra kell értelmezni, az x tengely a végrehajtási sorrendet jelöli. Kezdetben az alapállapotoktól indulunk, majd az operátorokon keresztül eljutunk egy végállapotba, amikor is megmérjük a qbitet, és a mért adattal további műveleteket hajthatunk végre. A mérés utáni adatfolyamot dupla vonalas vezeték jelzi, itt az adat értéke klasszikus állapot, míg a kvantumbitek között egyszeres vonal található.

A Quipper lehetőséget ad a kép testreszabására pl. kommentek, feliratok elhelyezésével, elhelyezések módosításával. A hálózati elrendezés nagy része azonban generált pozíció szerint működik.

A programot előre megadott típusokból, emőre megadott műveletek felhasználásával írhatjuk meg Haskell nyelven, illetve tetszőlegesen kiegészíthetjük saját értékekkel, műveletekkel. A Hasekll ismerete után a Quipper elemeinek megértése nem okoz nehézséget. Az elemek listája megtalálható a hivatalos dokumentációban. Aki nem jártas a Haskell használatában, nem ismeri a funkcionális programozást, ajánlott a Learn You a Haskell for Great Good! című könyv átolvasása - már csak a humoros írásmód miatt is. A Haskell megtanulása talán nehézkes lehet, ugyanakkor használata sok dolgot megkönnyít.

A Deutsch–Jozsa algoritmus leírása Quipper nyelven

Megjegyzés: A példaprogram a Five Quantum Algorithms Using Quipper című rövid útmutatóból származik, annak egyszerűsített változata.

import Quipper

deutsch_jozsa_circuit :: (([Qubit], Qubit) -> Circ ([Qubit], Qubit)) -> Circ [Bit]
deutsch_jozsa_circuit oracle = do
    top_qubits <- qinit (replicate 2 False)
    bottom_qubit <- qinit True
    
    mapUnary hadamard top_qubits
    hadamard_at bottom_qubit
    
    comment "before oracle"
    oracle (top_qubits, bottom_qubit)
    comment "after oracle"
    
    mapUnary hadamard top_qubits
    
    (top_qubits, bottom_qubit) <- measure (top_qubits, bottom_qubit)
    cdiscard bottom_qubit
    return top_qubits

main = do  
    putStrLn "Choose oracle type [constant/balanced]:"
    oracle_type <- getLine
    if oracle_type == "constant"
        then print_generic Preview (deutsch_jozsa_circuit constant_oracle_function)
        else print_generic Preview (deutsch_jozsa_circuit balanced_oracle_function)
        where
            constant_oracle_function (ins,out) = do
                return (ins, out)
            
            balanced_oracle_function ([x,y],out) = do
                qnot_at out `controlled` x
                qnot_at out `controlled` y
                return ([x,y],out)

Ha futtatjuk a programot, és a kiegyensúlyozott típust választjuk, a következő ábrát generálja a Quipper:

Quipper által generált kvantum-hálózat képe kiegyensúlyozott függvény esetében

Konstans típus esetén a kimenet:

Quipper által generált kvantum-hálózat képe konstans függvény esetében

Úgy tűnhet, hogy ez a program mégsem oldotta meg a Deutsch–Jozsa problémát. Ez részben igaz is, mert szimulációt nem hajtottunk végre, az eredmény nem lett kiértékelve. Azonban ne felejtsük el, hogy a programok futtattásának pillanatában a kérdéses függvénynek ismertnek kell lennie minden esetben, így a függvény típusától függően kétféle hálózat lehetséges a válasz kikalkulálásának időpontjában. A Quipper erről a kétféle változatról készített pillanatképeket. Ha a generált hálózat alapján építenénk egy mini-kvantumszámítógépet, és elindítanánk, a helyes választ kapnánk vissza. Természetesen lehetséges olyan hálózati leírást is generálni a Quipper-rel, ami általánosságban írja le a problémát megoldó hálózatot, de ha ezt akarnánk megvalósítani, a mini-kvantumszámítógép építése közben már egyik vagy másik variációt kell választanunk.

Értékelés

Q#

A Q# nyelvet 2017 végén mutatta be a Microsoft, jelezve, hogy a cég a későbbiekben nagy figyelmet fog fordítani a kvantumprogramozásnak. A Microsoft Quantum Development Kit nagyban épít már korábban kifejlesztett és bevált technológiákra, mint pl. a C# nyelv, a Visual Studio fejlesztőkörnyezet. A Q# nyelv sok sajátosságot mutat más, főleg C# és F# nyelvekkel, ami könnyebbé teszi a szintaktika megértését, gyorsítva ezzel a fejlesztési folyamatot. Viszonylag gazdag elemekkel rendelkezik, a benne leírt algoritmusok könnyedén felismerhetők.

Jelenleg a Microsoft Quantum Development Kit, és azon belül a Q# nyelv egy Visual Studio kiegészítőként telepíthető. Ez egy kifejlett fejlesztőeszköz, amit a Microsoft már 1997 óta fejleszt, sok változáson ment keresztül. Eddigiekben főleg C++ és .NET fejlesztésre használták, de az eszköz számtalan más nyelvet képes kezelni.

Ez a kiegészítő még nem túl fejlett, ami nem meglepő, tekintve, hogy kb. fél éve jelent meg. A helyes működésre helyezték a legnagyobb hangsúlyt, az automatikus kiegészítés, hibák azonnali kijelzése, tippek megjelenítése - ahogy az ma már elvárható egy kompakt fejlesztőeszköztől - még csak kezdetlegesen érhető el. Várhatóan a közeljövőben ezt fejleszteni fogják, amint igény mutatkozik a Q# nyelv használata iránt, illetve az alap nyelvi elemeket sikerül hibátlanra, könnyen használhatóra alakítani.

A Microsoft kvantum fejlesztőcsomagjában a szimuláció és a kvantum program leírása élesen elkülönül. A szimuláció C# nyelven lehetséges - itt fel lehet használni a C# nyelv tetszőleges elemét, ami nagy szabadságot ad a fejlesztők számára, a tesztesetek lehetnek nagyon komplexek is, illetve könnyű a külvilággal kommunikálni (pl. megjeleníteni az eredményt). A két nyelv párhuzamos használata jól mutatja a :NET keretrendszer egyik fő jellemzőjét, mégpedig a nyelvfüggetlenséget: a különböző nyelvekben leírt dolgok egyező névtérben vannak, így elérhető az egyik a másikból fennakadás nélkül, a fordító elvégzi a szükséges konverziókat.

A Q#, és q C# illetve F# szintaktikái között sok hasonlósság figyelmhető meg. Sok elemet, módszert átvettek a korábbi nyelvekből - ilyen pl. az using kulcsszó és a köré épülő minta: lefoglaljuk az erőforrást, végrehajtunk rajta tetszőleges műveletet, majd felszabadítjuk, vagy az F# nyelvben használt let kulcsszó a módosíthatatlan változók esetében. Mégis a Microsoft fejlesztői törekedtek arra, hogy felismerhető legyen a különbség: a Q# nyelvben a típusoknak nagy kezdőbetűi vannak, a névtér meghatározása és kibontása kissé eltérű módon adható meg, az operátorok paraméterei eltérő helyen és sorrendben vannak, mint a hagyományos metódusoknál stb. Erre azért lehetett szükség, hogy a fejlesztők ránézésre meg tudják mondani, a két nyelv közül melyikben dolgoznak éppen, ugyanis sok hibát okozhat, ha összekevernék a nyelveket és azok elemeit, hiszen a C# nyelven elérhető, a .NET keretrendszerben megtalálható könyvtáraknak akár lehet, nincs is értelme egy kvantum programban, vagy teljesen máshogy kell a műveletet elvégezni.

A kvantum programokat egy .qs kiterjesztésű fájlokban kell megírni Q# nyelven. Ebbe a fájlba több operációt és függvényt, típusokat és értékeket írhatunk le. A kvantum programnak van egy belépő, más nyelvekből main-ként ismert pontja, ahol a bemeneti paramétereket várja, illetve visszatérési értéke megadja a kvantum program eredményét. Ezek tetszőleges értékeke lehetnek, így a klasszikus programból bármilyen adat átvihető a kvantum programba. Természetesen a kvantum programból nem vihető ki a klasszikus programba kvantumbit, csak azoknak mért eredményei.

A Q# típusrendszere klasszikus (pl. Int, Bool), és kvantum (Qubit, Result), és további segédtípusokat (pl. operációk, generikus, saját típusok) különböztet meg. A klasszikus típusok lehetővé teszik a két program közti kommunikációt, illetve az algoritmusok beállításait (pl. a qregiszter elemeinek számát megadhatjuk egy Int típussal). A qregiszterek Qubit tömbökből állnak. A qbiteket az operáció elején le kell foglalni, majd végén visszaállítani 0 fázisba, és elengedni őket. A foglalás és elengedés között tetszőleges transzformációkat, méréseket alkalmazhatunk a qbiteken. Számtalan, előre definiált segédoperáció érhető el a Microsoft.Quantum névtér alatt, amiket felhasználva gyakori műveleteket gyorsan tudunk elvégezni. Ilyenek a gyakran használt általános unitér operátorok, illetve a kapuk több qbiten való végrehajtását lehetővé tevő ApplyToEach függvény, vagy a hibakeresésre használható AssertQubit művelet.

Ellentétben a hagyományos programozási nyelvekkel, a Q#-ban egy operációnak több “belseje” is lehet. Ugyanis ha az operációnak nincs visszatérési értéke, akkor megadhatjuk az adjoint (megfordított) és a controlled (irányított), illetve ezek ötvözésének viselkedését is. Többnyire a fordító elég okos ahhoz, hogy ezeket a viselkedéseket a rendes viselkedésből kikövetkeztesse (mivel a legtöbb alap kapu maga is unitér operátor, így megfordítható és irányítható), de ha mégse, vagy hatékonyságnövelés miatt szükséges, megadhatjuk mi is mindegyiket külön-külön.

A Q# kétféle típusú változókat különböztet meg:

  1. Mutable: Az ilyen változók deklarálása a mutable kulcsszóval lehetséges. A benne tárolt adat később megváltoztatható, a set kulcsszó felhasználásával. Ilyen típusúnak érdemes választani pl. a mérési eredményeket tároló tömböt: elsőnek lefoglaljuk a tömböt, majd minden mérési eredményt beleírunk.
  2. Immutable: Ezek a let kulcsszóval deklarálhatók. A későbbiekben nem rendelhető hozzájuk másik érték. Ilyen típusúnak érdemes választani a konstans paramétereket (mint pl. az algoritmusban használt qbitek száma), a köztes állapotokban qbitek azonosítására használt változókat (ebben az esetben a qbit értéke megváltozhat, de az, hogy az immutable változó melyik qbitre hivatkozik, az nem).

A mérések eredménye Result típusú változóban tárolódik. Egy ilyen típusú változó értéke One vagy Zero lehet. Ezek az értékek nem 1 és 0 számokat jelölnek! Noha természetesen ha a mérési eredmény biztosan 1, akkor a változó One-t fog tárolni, azonban ha a qbit mért együtthatója alapján az érték nem biztos, a program (az alkalmazott mérési operátornak megfelelően) dönti el, melyik klasszikus bitre alakítja át a kvantumbit értékét.

A Q# nyelv további elemei könnyen értelmezhetőek, ha már van ismeretünk más szekvenciális, objektum-orientált programozási nyelvekben. A teljes lista megtalálható a hivatalos dokumentációban.

Egy tipikus Q# nyelven leírt program felépítése:

  1. Felhasznált névterek megadása az open kulcsszóval.
  2. Segéd- és főoperátorok deklarálása.
  3. Az operációkban lefoglaljuk a szükséges qbiteket.
  4. Végrehajtjuk a kívánt műveleteket.
  5. Ha kell, elvégezzük a méréseket a qbiteken.
  6. A mérési eredmény alapján kikalkuláljuk a kívánt eredményt, esetleg további műveleteket végzünk el.
  7. Alaphelyzetbe állítjuk a qbiteket.
  8. Átadjuk a klasszikus programnak a kvantum program eredményét.

A Deutsch–Jozsa algoritmus leírása Q# nyelvben és tesztelése C# nyelven

Megjegyzés: A példaprogram a hivatalos Q# repository-ből származik, annak egyszerűsített változata.

Tesztelésre használt szimuláció:

using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;

namespace DeutschJozsaTest
{
    class Driver
    {
        static void Main(string[] args)
        {
            var sim = new QuantumSimulator(throwOnReleasingQubitsNotInZeroState: true);

            bool isConstant = new System.Random().Next(0, 2) == 0;
            System.Console.WriteLine("The function operates as " + (isConstant ? "constant" : "balanced") + ".");

            // Manuálisan megadjuk, a függvény melyik esetekben ad vissza 0-t vagy 1-et.
            var onesForConstant = new QArray<long> { 0, 1, 2, 3 };
            var onesForBalanced = new QArray<long> { 1, 2 };
            var oneValues = isConstant ? onesForConstant : onesForBalanced;
            var result = DeutschJozsaTestCase.Run(sim, 2, oneValues).Result;

            System.Console.WriteLine("Detected function as " + (result ? "constant" : "balanced") + ".");
            System.Console.ReadLine();
        }
    }
}

Az algoritmust leíró kvantum program:

namespace DeutschJozsaTest {
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;
    
    operation DeutschJozsaTestCase(n : Int, markedElements : Int[]) : Bool {
        body {
            mutable resultArray = new Result[n];
            using (qubits = Qubit[n + 1]) {
                // Az utolsó qbit-et 1-re állítjuk.
                X(qubits[n]);
                
                ApplyToEach(H, qubits);
                
                let target = qubits[n];
                let inputs = qubits[0..n-1];
                for (idxMarked in 0..Length(markedElements)-1) {
                    // Létrehozzuk és futtatjuk a függvényt, aminek hozzárendelési szabályát előre megadjuk.
                    (ControlledOnInt(markedElements[idxMarked], ApplyToEachCA(X, _))) (inputs, [target]);
                }
                ApplyToEach(H, qubits[0..(n-1)]);
                
                for (idx in 0..(n - 1)) {
                    set resultArray[idx] = MResetZ(qubits[idx]);
                }
                Reset(qubits[n]);
            }

            // A függvény létrehozása miatt ebben a tesztben akkor konstans f, ha minden mérési
            // eredmény 0. Át lehet alakítani a függvény létrehozását úgy, hogy az eredeti formát
            // visszakaphassuk, ahol f akkor konstans, ha a mérési eredmény 1 vagy -1. Ez ugyanakkor
            // a végrehajtott műveleteket számottevően nem befolyásolja.
            return ForAll(IsResultZero, resultArray);
         }
    }
}

Értékelés

Összefoglalás

Mint látható, a klasszikus- és a kvantumprogramok között sok hasonlóság és eltérés található egyaránt. Jelenleg még nem léteznek igazi kvantum programok, többnyire csak szimulációval hajtják végre a leírt algoritmusokat, statisztikákat készítenek az eredményekről, vagy modelleket, hálózatokat készítenek hozzá. A kvantum programozási nyelvek egy része még fejlesztés alatt áll (vagy éppen abbamaradt), ám folyamatosan jönnek létre új nyelvek, és fejlesztőeszközök.

A kvantum programozási nyelvek nagy jövőnek néznek elé. Valamelyest követjük a klasszikus programozás életciklusát: legelsőnek a működőképes, egyszerű nyelvek jöttek létre (mint pl. a QCL), majd az új nyelvek már egyre több szempontot figyelembe vettek, felhasználtak bevált módszereket (mint pl. a Q# esetében). A kvantumszámítógép megjelenésével erőteljesebb fejlesztés várható ezen a területen.

A bemutatott nyelveken kívül számtalan másik is elérhető. Amiket megismerhettünk, azok lényegesen eltérnek egymástól, más esetekben érdemes őket használni. Ha absztrakt módon, főleg a matematikai jellemzőket kiemelve szeretnénk leírni algoritmusokat, használhatjuk a kvantum pszeudokódot. A Quipper-rel jól áttekinthető ábrákat hozhatunk létre, amik pl. tanításra felhasználhatóak. A Q# nyelv segítségével könnyen kezelhető kvantumszimulátorhoz férhetünk hozzá, amivel tesztelni is lehet az elméleti dolgokat. Igényeiknek megfelelően az interneten kereshetünk másféle nyelveket is.