Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Come illustrato nella sezione sulle dichiarazioni chiamabili , attualmente non esiste alcun motivo per dichiarare esplicitamente le specializzazioni per le funzioni. Questo argomento si applica alle operazioni e illustra come dichiarare le specializzazioni necessarie per supportare determinati funtori.
È un problema piuttosto comune nel calcolo quantistico per richiedere l'adiacente di una determinata trasformazione. Molti algoritmi quantistici richiedono sia un'operazione che la relativa adiacente per eseguire un calcolo.
Q# usa il calcolo simbolico che può generare automaticamente l'implementazione adiacente corrispondente per un'implementazione del corpo specifica. Questa generazione è possibile anche per le implementazioni che combinano liberamente calcoli classici e quantistici. Esistono tuttavia alcune restrizioni che si applicano in questo caso. Ad esempio, la generazione automatica non è supportata per motivi di prestazioni se l'implementazione usa variabili modificabili. Inoltre, ogni operazione chiamata all'interno del corpo genera la corrispondente necessità di supportare il funtore Adjoint stesso.
Anche se non è possibile annullare facilmente le misurazioni nel caso multi-qubit, è possibile combinare le misurazioni in modo che la trasformazione applicata sia unitaria. In questo caso, significa che, anche se l'implementazione del corpo contiene misurazioni che da soli non supportano il funtore Adjoint, il corpo nella sua interezza è adiacente. Tuttavia, la generazione automatica dell'implementazione adiacente non riesce in questo caso. Per questo motivo, è possibile specificare manualmente l'implementazione.
Il compilatore genera automaticamente implementazioni ottimizzate per modelli comuni, ad esempio coniugazioni.
Tuttavia, una specializzazione esplicita può essere auspicabile definire un'implementazione più ottimizzata a mano. È possibile specificare un'implementazione e un numero qualsiasi di implementazioni in modo esplicito.
Nota
Il compilatore non verifica la correttezza di tale implementazione specificata manualmente.
Nell'esempio seguente la dichiarazione per un'operazione SWAP, che scambia lo stato di due qubit q1 e q2, dichiara una specializzazione esplicita per la versione adiacente e la relativa versione controllata. Mentre le implementazioni per Adjoint SWAP e Controlled SWAP sono quindi definite dall'utente, il compilatore deve comunque generare l'implementazione per la combinazione di entrambi i functor (Controlled Adjoint SWAP, che corrisponde a 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);
}
}
Direttive di generazione automatica
Quando si determina come generare una particolare specializzazione, il compilatore assegna la priorità alle implementazioni definite dall'utente. Ciò significa che se una specializzazione adiacente è definita dall'utente e viene generata automaticamente una specializzazione controllata, la specializzazione adiacente controllata viene generata in base all'adiacente definito dall'utente e viceversa. In questo caso, entrambe le specializzazioni sono definite dall'utente. Poiché la generazione automatica di un'implementazione adiacente è soggetta a maggiori limitazioni, per impostazione predefinita la specializzazione adiacente controllata genera la specializzazione controllata dell'implementazione esplicitamente definita della specializzazione adiacente.
Nel caso dell'implementazione SWAP, la migliore opzione consiste nell'adiacente alla specializzazione controllata per evitare inutilmente di condizionare l'esecuzione del primo e dell'ultimo CNOT sullo stato dei qubit di controllo.
L'aggiunta di una dichiarazione esplicita per la versione adiacente controllata che specifica una direttiva di generazione appropriata forza il compilatore a generare la specializzazione adiacente controllata in base all'implementazione specificata manualmente della versione controllata. Tale dichiarazione esplicita di una specializzazione generata dal compilatore assume il formato
controlled adjoint invert;
e viene inserito all'interno della dichiarazione di SWAP.
D'altra parte, inserendo la linea
controlled adjoint distribute;
forza il compilatore a generare la specializzazione in base alla specializzazione adiacente definita (o generata). Per altre informazioni, vedere questa 'inferenza di specializzazione parziale proposta per altri dettagli.
Per l'operazione SWAP, è disponibile un'opzione migliore.
SWAP è adiacente, ovvero è il proprio inverso; l'implementazione definita dell'adiacente chiama semplicemente il corpo di SWAP ed è espressa con la direttiva
adjoint self;
Dichiarando la specializzazione adiacente in questo modo, si garantisce che la specializzazione adiacente controllata che il compilatore inserisca automaticamente richiama semplicemente la specializzazione controllata.
Esistono le direttive di generazione seguenti e sono valide:
| Specializzazione | Direttive |
|---|---|
body specializzazione: |
- |
adjoint specializzazione: |
self, invert |
controlled specializzazione: |
distribute |
controlled adjoint specializzazione: |
self, invert, distribute |
Che tutte le direttive di generazione siano valide per una specializzazione adiacente controllata non è una coincidenza; a condizione che i funtori commutano, il set di direttive di generazione valide per l'implementazione della specializzazione per una combinazione di funtori è sempre l'unione del set di generatori validi per ognuno di essi.
Oltre alle direttive elencate in precedenza, la direttiva auto è valida per tutte le specializzazioni tranne body; indica che il compilatore deve selezionare automaticamente una direttiva di generazione appropriata.
Dichiarazione
operation DoNothing() : Unit {
body ... { }
adjoint auto;
controlled auto;
controlled adjoint auto;
}
equivale a
operation DoNothing() : Unit
is Adj + Ctl { }
L'annotazione is Adj + Ctl in questo esempio specifica le caratteristiche dell'operazione , che contengono le informazioni sui funtori supportati da una determinata operazione.
Anche se per motivi di leggibilità, è consigliabile annotare ogni operazione con una descrizione completa delle sue caratteristiche, il compilatore inserisce o completa automaticamente l'annotazione in base alle specializzazioni dichiarate in modo esplicito. Viceversa, il compilatore genera anche specializzazioni che non sono dichiarate in modo esplicito, ma devono esistere in base alle caratteristiche annotate. Si supponga che l'annotazione specificata dichiara in modo implicito queste specializzazioni. Il compilatore genera automaticamente le specializzazioni necessarie, se possibile, selezionando una direttiva appropriata. Q# supporta quindi l'inferenza delle caratteristiche dell'operazione e delle specializzazioni esistenti basate su annotazioni (parziali) e specializzazioni definite in modo esplicito.
In un certo senso, le specializzazioni sono simili a singoli overload per lo stesso chiamabile, con l'avvertenza che determinate restrizioni si applicano a quali overload è possibile dichiarare.