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 gaat dieper in op het declareren van de benodigde specialisaties om bepaalde functors te ondersteunen.
Het is een veelvoorkomend probleem in kwantumcomputing om de aangrenzende van een bepaalde transformatie te vereisen. Veel kwantumalgoritmen vereisen zowel een bewerking als de bijbehorende 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 combineren. Er zijn echter enkele beperkingen die van toepassing zijn in dit geval. 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 metingen niet gemakkelijk ongedaan kunnen worden gemaakt in het geval van meerdere qubits, is het mogelijk om metingen te combineren zodat de toegepaste transformatie unitair is. In dit geval betekent het dat, hoewel de body-implementatie metingen bevat die op zichzelf de functor niet ondersteunen Adjoint
, het lichaam in zijn geheel aangrenzend is. Niettemin zal het automatisch genereren van de aangrenzende implementatie in dit geval mislukken. Daarom is het mogelijk om de implementatie handmatig op te geven.
De compiler genereert automatisch geoptimaliseerde implementaties voor algemene patronen, zoals vervoegingen.
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 juistheid van een dergelijke handmatig opgegeven implementatie wordt niet geverifieerd door de compiler.
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 beheerde 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
die 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, de gecontroleerde aangrenzende specialisatie wordt 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 onderhevig is aan meer beperkingen, genereert de gecontroleerde aangrenzende specialisatie standaard de gecontroleerde specialisatie van de expliciet gedefinieerde implementatie van de aangrenzende specialisatie.
In het geval van de SWAP
implementatie is de betere optie om de gecontroleerde specialisatie te koppelen om te voorkomen dat de uitvoering van de eerste en de laatste CNOT
onnodig wordt bepaald op de toestand van de controle-qubits.
Het toevoegen van een expliciete declaratie voor de gecontroleerde aangrenzende versie die een geschikte generatie-instructie specificeert, 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 moet worden gegenereerd, heeft de vorm
controlled adjoint invert;
en wordt ingevoegd in de declaratie van SWAP
.
Aan de andere kant, de regel invoegen
controlled adjoint distribute;
dwingt de compiler om de specialisatie te genereren op basis van de gedefinieerde (of gegenereerde) aangrenzende specialisatie. Zie dit voorstel voor gedeeltelijke specialisatiedeductie voor meer informatie.
Voor de bewerking SWAP
is er een betere optie. SWAP
is zelf-aangrenzend, dat wil zeggen, het is zijn eigen inverse; de -gedefinieerde uitvoering van het aangrenzende slechts de hoofdtekst van SWAP
aanroept . U drukt dit uit met de instructie
adjoint self;
Het op deze manier declareren van de aangrenzende specialisatie zorgt ervoor dat de gecontroleerde aangrenzende specialisatie die automatisch door de compiler wordt ingevoegd, alleen de gecontroleerde specialisatie aanroept.
De volgende generatierichtlijnen bestaan en zijn geldig:
Specialisatie | Richtlijn(en) |
---|---|
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 functoren pendelen, is de reeks geldige generatierichtlijnen voor het implementeren van de specialisatie voor een combinatie van functors altijd de samenvoeging van de set geldige generatoren voor elke generator.
Naast de eerder genoemde richtlijnen is de richtlijn auto
geldig voor alle specialisaties behalve body
; het geeft aan dat de compiler automatisch een geschikte generatierichtlijn moet kiezen.
De declaratie
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 aan, die de informatie bevatten over welke functors een bepaalde bewerking ondersteunt.
Hoewel het omwille van de leesbaarheid wordt aanbevolen dat u aantekeningen maakt bij elke bewerking met een volledige beschrijving van de kenmerken. De compiler voegt automatisch de aantekening in of voltooit deze op basis van expliciet gedeclareerde specialisaties. Omgekeerd genereert de compiler ook specialisaties die niet expliciet zijn gedeclareerd, maar moeten bestaan op basis van de geannoteerde kenmerken. We zeggen dat de gegeven aantekening deze specialisaties impliciet heeft gedeclareerd . De compiler genereert zo mogelijk automatisch de benodigde specialisaties en kiest een geschikte instructie. Q# ondersteunt dus deductie van zowel bedrijfskenmerken als bestaande specialisaties op basis van (gedeeltelijke) aantekeningen en expliciet gedefinieerde specialisaties.
In zekere zin zijn specialisaties vergelijkbaar met individuele overloads voor dezelfde aanroepbare, met het voorbehoud dat bepaalde beperkingen van toepassing zijn op welke overloads u kunt declareren.
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort beschikbaar: In de loop van 2024 zullen we GitHub-problemen geleidelijk uitfaseren als het feedbackmechanisme voor inhoud en deze vervangen door een nieuw feedbacksysteem. Zie voor meer informatie:Feedback verzenden en weergeven voor