Specialisatiedeclaraties
Zoals uitgelegd in de sectie over aanroepbare declaraties, is er momenteel geen reden om specialisaties voor functies expliciet te declareren. Dit onderwerp is van toepassing op bewerkingen en wordt uitgebreid over het declareren van de benodigde specialisaties ter ondersteuning van bepaalde functors.
Het is nogal een veelvoorkomend probleem in kwantumcomputing om de aangrenzende van een bepaalde transformatie te vereisen. Veel kwantumalgoritmen vereisen zowel een bewerking als de aangrenzende bewerking om een berekening uit te voeren.
Q# maakt gebruik van symbolische berekeningen die automatisch de bijbehorende aangrenzende implementatie voor een bepaalde instantie-implementatie kunnen genereren. Deze generatie is zelfs mogelijk voor implementaties die klassieke en kwantumberekeningen vrij combineren. Er zijn echter enkele beperkingen die in dit geval van toepassing zijn. Automatisch genereren wordt bijvoorbeeld niet ondersteund om prestatieredenen als de implementatie gebruikmaakt van veranderlijke variabelen. Bovendien genereert elke bewerking die binnen het lichaam wordt aangeroepen, de bijbehorende aangrenzende behoeften om de Adjoint
functor zelf te ondersteunen.
Hoewel het niet eenvoudig is om metingen in het geval van meerdere qubits ongedaan te maken, is het mogelijk om metingen te combineren, zodat de toegepaste transformatie unitair is. In dit geval betekent het dat, hoewel de implementatie van het lichaam metingen bevat die de Adjoint
functor niet ondersteunen, het lichaam in zijn geheel aangrenzend is. Het automatisch genereren van de aangrenzende implementatie mislukt echter in dit geval. Daarom is het mogelijk om handmatig de implementatie op te geven.
De compiler genereert automatisch geoptimaliseerde implementaties voor veelvoorkomende patronen, zoals conjugations.
Een expliciete specialisatie kan echter wenselijk zijn om handmatig een meer geoptimaliseerde implementatie te definiëren. Het is mogelijk om één implementatie en een willekeurig aantal implementaties expliciet op te geven.
Notitie
De compiler controleert niet de juistheid van een dergelijke handmatig opgegeven implementatie.
In het volgende voorbeeld declareert de declaratie voor een bewerking SWAP
, die de status van twee qubits q1
en q2
uitwisselt, een expliciete specialisatie voor de aangrenzende versie en de gecontroleerde versie. Hoewel de implementaties voor Adjoint SWAP
en Controlled SWAP
dus door de gebruiker zijn gedefinieerd, moet de compiler nog steeds de implementatie genereren voor de combinatie van beide functors (Controlled Adjoint SWAP
, wat hetzelfde is als Adjoint Controlled SWAP
).
operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
is Adj + Ctl {
body ... {
CNOT(q1, q2);
CNOT(q2, q1);
CNOT(q1, q2);
}
adjoint ... {
SWAP(q1, q2);
}
controlled (cs, ...) {
CNOT(q1, q2);
Controlled CNOT(cs, (q2, q1));
CNOT(q1, q2);
}
}
Instructies voor automatisch genereren
Bij het bepalen hoe een bepaalde specialisatie moet worden gegenereerd, geeft de compiler prioriteit aan door de gebruiker gedefinieerde implementaties. Dit betekent dat als een aangrenzende specialisatie door de gebruiker is gedefinieerd en een gecontroleerde specialisatie automatisch wordt gegenereerd, wordt de gecontroleerde aangrenzende specialisatie gegenereerd op basis van de door de gebruiker gedefinieerde aangrenzende en vice versa. In dit geval zijn beide specialisaties door de gebruiker gedefinieerd. Aangezien de automatische generatie van een aangrenzende implementatie meer beperkingen heeft, is de gecontroleerde aangrenzende specialisatie standaard ingesteld op het genereren van de gecontroleerde specialisatie van de expliciet gedefinieerde implementatie van de aangrenzende specialisatie.
In het geval van de SWAP
implementatie is de betere optie om naast de gecontroleerde specialisatie te voorkomen dat de uitvoering van de eerste en de laatste CNOT
van de status van de controle-qubits onnodig wordt aangepast.
Door een expliciete verklaring toe te voegen voor de gecontroleerde aangrenzende versie die een geschikte generatierichtlijn aangeeft dwingt de compiler om de gecontroleerde aangrenzende specialisatie te genereren op basis van de handmatig opgegeven implementatie van de gecontroleerde versie in plaats daarvan. Een dergelijke expliciete verklaring van een specialisatie die door de compiler wordt gegenereerd, heeft de vorm
controlled adjoint invert;
en wordt ingevoegd in de verklaring van SWAP
.
Daarentegen voegt u de regel in
controlled adjoint distribute;
dwingt de compiler om de specialisatie te genereren op basis van de gedefinieerde (of gegenereerde) aangrenzende specialisatie. Zie dit gedeeltelijke specialisatiedeductie voorstel voor meer informatie voor meer informatie.
Voor de bewerking SWAP
is er een betere optie.
SWAP
is zelf-aangrenzende, dat wil zeggen, het is zijn eigen inverse; de gedefinieerde tenuitvoerlegging van het aangrenzende gebied roept slechts het lichaam van SWAP
aan en wordt uitgedrukt in de richtlijn
adjoint self;
Het declareren van de aangrenzende specialisatie op deze manier zorgt ervoor dat de gecontroleerde aangrenzende specialisatie die de compiler automatisch invoegt, alleen de gecontroleerde specialisatie aanroept.
De volgende generatie-instructies bestaan en zijn geldig:
Specialisatie | Richtlijnen |
---|---|
body specialisatie: |
- |
adjoint specialisatie: |
self , invert |
controlled specialisatie: |
distribute |
controlled adjoint specialisatie: |
self , invert , distribute |
Dat alle generatierichtlijnen geldig zijn voor een gecontroleerde aangrenzende specialisatie is geen toeval; zolang functors pendelen, is de set geldige generatierichtlijnen voor het implementeren van de specialisatie voor een combinatie van functors altijd de samenvoeging van de set geldige generatoren voor elke.
Naast de eerder genoemde richtlijnen is de richtlijn auto
geldig voor alle specialisaties behalve body
; hiermee wordt aangegeven dat de compiler automatisch een geschikte generatierichtlijn moet kiezen.
De verklaring
operation DoNothing() : Unit {
body ... { }
adjoint auto;
controlled auto;
controlled adjoint auto;
}
is gelijk aan
operation DoNothing() : Unit
is Adj + Ctl { }
De aantekening is Adj + Ctl
in dit voorbeeld geeft de bewerkingskenmerken, die de informatie bevatten over welke functors een bepaalde bewerking ondersteunt.
Hoewel het beter leesbaar is, raden we u aan elke bewerking aantekeningen te maken met een volledige beschrijving van de kenmerken ervan, de compiler automatisch de aantekening invoegt of voltooit op basis van expliciet gedeclareerde specialisaties. Omgekeerd genereert de compiler ook specialisaties die niet expliciet worden gedeclareerd, maar moeten bestaan op basis van de geannoteerde kenmerken. We zeggen dat de gegeven aantekening impliciet verklaart deze specialisaties. De compiler genereert automatisch de benodigde specialisaties, indien mogelijk, het kiezen van een geschikte richtlijn. Q# ondersteunt dus deductie van zowel bewerkingskenmerken als bestaande specialisaties op basis van (gedeeltelijke) aantekeningen en expliciet gedefinieerde specialisaties.
In zekere zin zijn specialisaties vergelijkbaar met individuele overbelastingen voor dezelfde aanroepbare, waarbij de beperkingen gelden waarop overbelastingen u kunt declareren.