Dela via


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 SWAPvilket ä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 SWAPfinns 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 SWAPi . 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.