Specialiseringsdeklarationer
Som förklaras i avsnittet om anropbara deklarationer finns det för närvarande ingen anledning att uttryckligen deklarera specialiseringar för funktioner. Det här avsnittet gäller åtgärder och beskriver hur du deklarerar de specialiseringar som krävs för att stödja vissa belackare.
Det är ett ganska vanligt problem inom kvantberäkning att kräva en viss omvandlings adjoint. Många kvantalgoritmer kräver både en åtgärd och dess adjoint för att utföra en beräkning.
Q# använder symbolisk beräkning som automatiskt kan generera motsvarande adjoint-implementering för en viss kroppsimplementering. Den här generationen är möjlig även för implementeringar som fritt blandar klassiska beräkningar och kvantberäkningar. Det finns dock vissa begränsningar som gäller i det här fallet. Automatisk generering stöds till exempel inte av prestandaskäl om implementeringen använder föränderliga variabler. Dessutom genererar varje åtgärd som anropas i brödtexten motsvarande angränsande behov för att stödja själva functorn Adjoint
.
Även om man inte enkelt kan ångra mätningar i multi-qubit-fallet är det möjligt att kombinera mätningar så att den tillämpade omvandlingen är enhetlig. I det här fallet innebär det att även om kroppsimplementeringen innehåller mått som på egen hand inte stöder Adjoint
functorn, är kroppen i sin helhet angränsande. Icke desto mindre misslyckas automatisk generering av den adjoint-implementeringen i det här fallet. Därför är det möjligt att manuellt ange implementeringen.
Kompilatorn genererar automatiskt optimerade implementeringar för vanliga mönster, till exempel konjugationer.
En uttrycklig specialisering kan dock vara önskvärd för att definiera en mer optimerad implementering för hand. Det är möjligt att uttryckligen ange en implementering och ett valfritt antal implementeringar.
Anteckning
Korrektheten i en sådan manuellt angiven implementering verifieras inte av kompilatorn.
I följande exempel deklarerar deklarationen för en åtgärd SWAP
, som utbyter tillståndet för två qubitar q1
och q2
, en uttrycklig specialisering för dess angränsande version och dess kontrollerade version. Implementeringarna för Adjoint SWAP
och Controlled SWAP
är därför användardefinierade, men kompilatorn måste fortfarande generera implementeringen för kombinationen av båda functors (Controlled Adjoint SWAP
vilket är samma som 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);
}
}
Direktiv för automatisk generering
När du bestämmer hur en viss specialisering ska genereras prioriterar kompilatorn användardefinierade implementeringar. Det innebär att om en adjoint-specialisering är användardefinierad och en kontrollerad specialisering genereras automatiskt genereras den kontrollerade adjoint-specialiseringen baserat på det användardefinierade adjoint och vice versa. I det här fallet är båda specialiseringarna användardefinierade. Eftersom den automatiska genereringen av en adjoint-implementering är mer begränsad, genererar den kontrollerade angränsande specialiseringen standard för att generera den kontrollerade specialiseringen av den uttryckligen definierade implementeringen av den adjoint-specialiseringen.
När det gäller implementeringen SWAP
är det bättre alternativet att förena den kontrollerade specialiseringen för att undvika onödig konditionering av körningen av den första och den sista CNOT
på kontrollkvabiternas tillstånd.
Om du lägger till en explicit deklaration för den kontrollerade adjoint-versionen som anger ett lämpligt generationsdirektiv tvingar du kompilatorn att generera den kontrollerade adjoint-specialiseringen baserat på den manuellt angivna implementeringen av den kontrollerade versionen i stället. En sådan uttrycklig deklaration av en specialisering som ska genereras av kompilatorn tar formen
controlled adjoint invert;
och infogas i deklarationen av SWAP
.
Å andra sidan infogar du linjen
controlled adjoint distribute;
tvingar kompilatorn att generera specialiseringen baserat på den definierade (eller genererade) angränsande specialiseringen. Mer information finns i det här förslaget om partiell specialiseringsinferens .
För åtgärden SWAP
finns det ett bättre alternativ.
SWAP
är självtilldelad, det vill sägs att det är sin egen invertering; den -definierade implementeringen av adjointen anropar bara brödtexten SWAP
i . Ni uttrycker detta med direktivet
adjoint self;
Om du deklarerar den angränsande specialiseringen på det här sättet ser du till att den kontrollerade angränsande specialiseringen som automatiskt infogas av kompilatorn bara anropar den kontrollerade specialiseringen.
Följande generationsdirektiv finns och är giltiga:
Specialisering | Direktiv |
---|---|
body Specialisering: |
- |
adjoint Specialisering: |
self , invert |
controlled Specialisering: |
distribute |
controlled adjoint Specialisering: |
self , invert , distribute |
Att alla generationsdirektiv är giltiga för en kontrollerad angränsande specialisering är ingen tillfällighet. så länge som functors pendlar, är uppsättningen av giltiga utvecklingdirektiv för att genomföra specialiseringen för en kombination av functors alltid unionen av uppsättningen av giltiga generatorer för varje.
Förutom de tidigare angivna direktiven är direktivet auto
giltigt för alla specialiseringar utom body
. Det anger att kompilatorn automatiskt bör välja ett lämpligt generationsdirektiv.
Deklarationen
operation DoNothing() : Unit {
body ... { }
adjoint auto;
controlled auto;
controlled adjoint auto;
}
motsvarar
operation DoNothing() : Unit
is Adj + Ctl { }
is Adj + Ctl
Kommentaren i det här exemplet anger åtgärdsegenskaperna, som innehåller information om vilka functors en viss åtgärd stöder.
För läsbarhetens skull rekommenderar vi att du kommenterar varje åtgärd med en fullständig beskrivning av dess egenskaper, men kompilatorn infogar eller slutför automatiskt anteckningen baserat på uttryckligen deklarerade specialiseringar. Omvänt genererar kompilatorn också specialiseringar som inte uttryckligen har deklarerats men som måste finnas baserat på de kommenterade egenskaperna. Vi säger att den angivna kommentaren implicit har deklarerat dessa specialiseringar. Kompilatorn genererar automatiskt nödvändiga specialiseringar om det kan, och väljer ett lämpligt direktiv. Q# stöder därför slutsatsdragning av både åtgärdsegenskaper och befintliga specialiseringar baserat på (partiella) anteckningar och uttryckligen definierade specialiseringar.
På sätt och vis liknar specialiseringar enskilda överlagringar för samma anropbara, med förbehållet att vissa begränsningar gäller för vilka överlagringar du kan deklarera.