Share via


Bewerkingen en functies

Zoals uitgebreider wordt beschreven in de beschrijving van het gegevenstype qubit, worden kwantumberekeningen uitgevoerd in de vorm van neveneffecten van bewerkingen die systeemeigen worden ondersteund op de beoogde kwantumprocessor. Dit zijn in feite de enige bijwerkingen in Q#. Omdat alle typen onveranderbaar zijn, zijn er geen neveneffecten die van invloed zijn op een waarde die expliciet wordt weergegeven in Q#. Zolang een implementatie van een bepaald aanroepbaar niet direct of indirect een van deze systeemeigen geïmplementeerde bewerkingen aanroept, produceert de uitvoering altijd dezelfde uitvoer, gegeven dezelfde invoer.

Q# hiermee kunt u dergelijke zuiver deterministische berekeningen expliciet in functies opsplitsen. Omdat de set systeemeigen ondersteunde instructies niet vast staat en is ingebouwd in de taal zelf, maar in plaats daarvan volledig configureerbaar en uitgedrukt als een Q# bibliotheek, wordt determinisme gegarandeerd door te vereisen dat functies alleen andere functies kunnen aanroepen en geen bewerkingen kunnen aanroepen. Bovendien worden systeemeigen instructies die niet deterministisch zijn, dat wil gezegd, omdat ze van invloed zijn op de kwantumstatus, weergegeven als bewerkingen. Met deze twee beperkingen kunnen functies worden geëvalueerd zodra de invoerwaarde bekend is en hoeven functies in principe nooit meer dan één keer te worden geëvalueerd voor dezelfde invoer.

Q# maakt daarom onderscheid tussen twee soorten aanroepbare functies: bewerkingen en functies. Alle aanroepbare functies nemen één argument (mogelijk tuplewaarde) als invoer en produceren één waarde (tuple) als uitvoer. Syntactisch wordt het bewerkingstype uitgedrukt als <TIn> => <TOut> is <Char>, waarbij <TIn> moet worden vervangen door het argumenttype, <TOut> moet worden vervangen door het retourtype en <Char> moet worden vervangen door de bewerkingskenmerken. Als er geen kenmerken hoeven te worden opgegeven, wordt de syntaxis vereenvoudigd tot <TIn> => <TOut>. Op dezelfde manier worden functietypen uitgedrukt als <TIn> -> <TOut>.

Afgezien van deze determinismegarantie is er weinig verschil tussen bewerkingen en functies. Beide zijn eersteklas waarden die vrijelijk kunnen worden doorgegeven; ze kunnen worden gebruikt als retourwaarden of argumenten voor andere aanroepbare functies, zoals wordt weergegeven in het volgende voorbeeld:

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
    return PowImpl(op, pow, _);
}

Beide kunnen worden geïnstantieerd op basis van een type-geparametriseerde definitie, bijvoorbeeld de functie Powtype parametrized hierboven, en ze kunnen gedeeltelijk worden toegepast zoals gedaan in de return instructie in het voorbeeld.

Bewerkingskenmerken

Naast de informatie over het invoer- en uitvoertype bevat het bewerkingstype informatie over de kenmerken van een bewerking. Deze informatie beschrijft bijvoorbeeld welke functors door de bewerking worden ondersteund. Daarnaast bevat de interne weergave ook informatie die relevant is voor optimalisatie die wordt afgeleid door de compiler.

De kenmerken van een bewerking zijn een set vooraf gedefinieerde en ingebouwde labels. Ze worden uitgedrukt in de vorm van een speciale expressie die deel uitmaakt van de typehandtekening. De expressie bestaat uit een van de vooraf gedefinieerde sets labels of uit een combinatie van kenmerkexpressies via een ondersteunde binaire operator.

Er zijn twee vooraf gedefinieerde sets, Adj en Ctl.

  • Adj is de set die één label bevat dat aangeeft dat een bewerking aangrenzend is, wat betekent dat deze de Adjoint functor ondersteunt en de toegepaste kwantumtransformatie ongedaan kan worden gemaakt, dat wil zeggen dat deze kan worden omgekeerd.
  • Ctl is de set die één label bevat dat aangeeft dat een bewerking controleerbaar is, wat betekent dat deze de Controlled functor ondersteunt en dat de uitvoering ervan afhankelijk kan zijn van de status van andere qubits.

De twee operators die worden ondersteund als onderdeel van kenmerkexpressies zijn de set union + en het ingestelde snijpunt *. In EBNF,

    predefined = "Adj" | "Ctl";
    characteristics = predefined 
        | "(", characteristics, ")" 
        | characteristics ("+"|"*") characteristics;

Zoals men zou verwachten, * heeft een hogere prioriteit dan + en zijn beide links-associatief. Het type van een eenheidsbewerking wordt bijvoorbeeld uitgedrukt als <TIn> => <TOut> is Adj + Ctl, waarbij <TIn> moet worden vervangen door het type van het bewerkingsargument en <TOut> vervangen door het type van de geretourneerde waarde.

Notitie

Het aangeven van de kenmerken van een bewerking in deze vorm heeft twee belangrijke voordelen; nieuwe labels kunnen worden geïntroduceerd zonder exponentieel veel taaltrefwoorden voor alle combinaties van labels. Misschien nog belangrijker is dat het gebruik van expressies om de kenmerken van een bewerking aan te geven, in de toekomst ook ondersteuning biedt voor parameterisaties ten opzichte van bewerkingskenmerken.