Delen via


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 q2uitwisselt, 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 SWAPdie 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 SWAPis 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 SWAPaanroept . 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.