Esercitazione: Implementare la trasformazione Quantum Fourier in Q#
Questa esercitazione illustra come scrivere e simulare un programma quantistico di base che opera su singoli qubit.
Anche se Q# è stato creato principalmente come linguaggio di programmazione di alto livello per programmi quantistici su larga scala, può essere usato anche per esplorare il livello inferiore della programmazione quantistica, cioè l'indirizzamento diretto di qubit specifici. In particolare, questa esercitazione approfondisce la trasformata di Fourier quantistica (QFT), una subroutine che è parte integrante di molti algoritmi quantistici più grandi.
Questa esercitazione illustra come:
- Definire le operazioni quantistico in Q#.
- Scrivere il circuito Quantum Fourier Transform
- Simulare un'operazione quantistica dall'allocazione del qubit all'output di misurazione.
- Osservare come la funzione d'onda simulata del sistema quantistico si evolve durante l'operazione.
Nota
Questa visualizzazione di livello inferiore dell'elaborazione di informazioni quantistiche viene spesso descritta in termini di circuiti quantistici, che rappresentano l'applicazione sequenziale di controlli, o operazioni, a qubit specifici di un sistema. Di conseguenza, le operazioni a singolo qubit e a più qubit applicate in sequenza possono essere immediatamente rappresentate in diagrammi di circuito. Ad esempio, la trasformazione quantum Fourier a tre qubit completa usata in questa esercitazione ha la rappresentazione seguente come circuito:
Suggerimento
Per accelerare il percorso di calcolo quantistico, vedere Codice con Azure Quantum, una funzionalità univoca del sito Web di Azure Quantum. Qui è possibile eseguire esempi predefiniti Q# o programmi personalizzati Q# , generare nuovo Q# codice dalle richieste, aprire ed eseguire il codice in VS Code per il Web con un solo clic e porre a Copilot eventuali domande sul calcolo quantistico.
Prerequisiti
La versione più recente di Visual Studio Code o aprire VS Code sul Web.
Versione più recente dell'estensione di AzureQuantum Development Kit. Per informazioni dettagliate sull'installazione, vedere Installazione di QDK in VS Code.
Se si vogliono usare Jupyter Notebook, è anche necessario installare Python e le estensioni Jupyter e il pacchetto Python più recente
qsharp
. A tale scopo, aprire un terminale ed eseguire il comando seguente:$ pip install --upgrade qsharp
Creare un nuovo Q# file
- In VS Code selezionare File > nuovo file di testo
- Salvare il file come QFTcircuit.qs. Questo file conterrà il Q# codice per il programma.
- Aprire QFTcircuit.qs.
Scrivere un circuito QFT in Q#
La prima parte di questa esercitazione consiste nella definizione dell'operazione Main
di Q#, che esegue la trasformata di Fourier quantistica su tre qubit. La funzione DumpMachine
viene usata per osservare l'evoluzione della funzione d'onda simulata del sistema a tre qubit nell'operazione. Nella seconda parte dell'esercitazione si aggiungerà la funzionalità di misurazione e si confronteranno gli stati pre-misurazione e post-misurazione dei qubit.
L'operazione verrà compilata passo per passo. Copiare e incollare il codice nelle sezioni seguenti nel file QFTcircuit.qs .
È possibile visualizzare il codice completo Q# per questa sezione come riferimento.
Importare le librerie necessarie Q#
All'interno del Q# file importare gli spazi dei nomi pertinenti Microsoft.Quantum.*
.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
// operations go here
Definire operazioni con argomenti e risultati restituiti
Definire quindi l'operazione Main
:
operation Main() : Unit {
// do stuff
}
L'operazione Main()
non accetta mai argomenti e per il momento restituisce un Unit
oggetto, analogo alla restituzione void
in C# o a una tupla vuota, Tuple[()]
in Python.
In seguito si modificherà l'operazione in modo che restituisca una matrice di risultati della misurazione.
Allocare qubit
All'interno dell'operazione Q# allocare un registro di tre qubit con la use
parola chiave . Con use
, i qubit vengono allocati automaticamente nello stato $\ket{0}$.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Come in un calcolo quantistico reale, Q# non consente di accedere direttamente agli stati del qubit. Tuttavia, l'operazione DumpMachine
stampa lo target stato corrente del computer, in modo da poter fornire informazioni utili per il debug e l'apprendimento quando viene usato insieme al simulatore di stato completo.
Applicare operazioni a qubit singolo e controllato
Successivamente, si applicano le operazioni che costituiscono l'operazione Main
stessa. Q# contiene già molte di queste e altre operazioni quantistico di base nello spazio dei Microsoft.Quantum.Intrinsic
nomi .
Nota
Si noti che Microsoft.Quantum.Intrinsic
non è stato importato nel frammento di codice precedente con gli altri spazi dei nomi, perché viene caricato automaticamente dal compilatore per tutti i Q# programmi.
La prima operazione applicata è l'operazione H
(Hadamard) al primo qubit:
Per applicare un'operazione a un qubit specifico da un registro (ad esempio, un singolo Qubit
da una matrice Qubit[]
), usare la notazione di indice standard.
Quindi, l'applicazione dell'operazione H
al primo qubit del registro qs
assume il formato seguente:
H(qs[0]);
Oltre ad applicare l'operazione H
ai singoli qubit, il circuito QFT è costituito principalmente da rotazioni R1
controllate. Un'operazione R1(θ, <qubit>)
in generale lascia invariato il componente $\ket{0}$ del qubit applicando una rotazione di $e^{i\theta}$ al componente $\ket{1}$.
Q# semplifica il condizionamento dell'esecuzione di un'operazione su uno o più qubit di controllo. In generale, la chiamata è preceduta da Controlled
e gli argomenti dell'operazione cambiano come segue:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
Si noti che l'argomento qubit di controllo deve essere una matrice, anche se è per un singolo qubit.
Le operazioni controllate nel QFT sono le R1
operazioni che agiscono sul primo qubit (e controllate dal secondo e dal terzo qubit):
Nel file Q# chiamare queste operazioni con queste istruzioni:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
La funzione PI()
viene usata per definire le rotazioni in termini di radianti pi greco.
Applicare l'operazione SWAP
Dopo aver applicato le operazioni pertinenti H
e le rotazioni controllate ai secondi e al terzo qubit, il circuito è simile al seguente:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Infine, si applica un'operazione SWAP
al primo e al terzo qubit per completare il circuito. Ciò è necessario perché la natura della trasformata di Fourier quantistica restituisce i qubit in ordine inverso, quindi gli scambi consentono una perfetta integrazione della subroutine in algoritmi più grandi.
SWAP(qs[2], qs[0]);
A questo punto è stata completata la scrittura delle operazioni a livello di qubit della trasformata di Fourier quantistica nell'operazione Q#:
Deallocare i qubit
L'ultimo passaggio consiste nel chiamare di nuovo DumpMachine()
per visualizzare lo stato post-operazione e per deallocare i qubit. Al momento dell'allocazione i qubit si trovavano nello stato $\ket{0}$ e devono essere reimpostati allo stato iniziale usando l'operazione ResetAll
.
Richiedere che tutti i qubit vengano reimpostati in modo esplicito su $\ket{0}$ è una funzionalità di base di Q#, in quanto consente ad altre operazioni di conoscere il proprio stato esattamente quando iniziano a usare gli stessi qubit (una risorsa scarsa). Inoltre, ciò garantisce che non ci siano entanglement con altri qubit nel sistema. Se la reimpostazione non viene eseguita alla fine di un blocco di allocazione use
, potrebbe essere generato un errore di runtime.
Aggiungere la riga seguente al file Q#:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
Operazione QFT completa
Il Q# programma è stato completato. Il file QFTcircuit.qs dovrebbe ora essere simile al seguente:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
Eseguire il circuito QFT
Per il momento, l'operazione Main
non restituisce alcun valore. L'operazione restituisce Unit
il valore. Successivamente, si modificherà l'operazione per restituire una matrice di risultati della misurazione (Result[]
).
- Prima di eseguire il programma, verificare nella barra di stato nella parte inferiore di VS Code che il target profilo sia impostato su Q#: Senza restrizioni. Per modificare il target profilo, selezionare il target profilo nella barra di stato e selezionare Senza restrizioni dal menu a discesa. Se il target profilo non è impostato su Senza restrizioni, verrà visualizzato un errore quando si esegue il programma.
- Per eseguire il programma, selezionare Esegui file dall'elenco a discesa dell'icona di riproduzione in alto a destra oppure premere CTRL+F5. Q# Il programma esegue l'operazione
Main()
nel simulatore predefinito. - Gli
Message
output eDumpMachine
vengono visualizzati nella console di debug.
Se si desidera sapere come sono interessati gli altri stati di input, è consigliabile sperimentarlo applicando altre operazioni di qubit prima della trasformazione.
Aggiungere misurazioni al circuito QFT
Quanto visualizzato dalla funzione DumpMachine
ha mostrato i risultati dell'operazione, ma purtroppo un elemento fondamentale della meccanica quantistica stabilisce che un sistema quantistico reale non può avere tale funzione DumpMachine
.
Al contrario, le informazioni vengono estratte tramite misurazioni, che in generale non solo non forniscono informazioni sullo stato quantistico completo, ma possono anche modificare drasticamente il sistema stesso.
Esistono molti tipi di misurazioni quantistiche, ma questo esempio è incentrato sulle misurazioni più semplici, quelle proiettive su singoli qubit. Al momento della misurazione in una determinata base (ad esempio, la base computazionale $ { \ket{0}, \ket{1} } $), lo stato del qubit viene proiettato su qualsiasi stato di base misurato, di conseguenza elimina qualsiasi sovrapposizione tra i due.
Modificare l'operazione QFT
Per implementare le misurazioni all'interno di un programma Q#, usare l'operazione M
, che restituisce un tipo Result
.
Prima modificare l'operazione Main
in modo che restituisca una matrice di risultati della misurazione (Result[]
invece di Unit
).
operation Main() : Result[] {
Definire e inizializzare una matrice Result[]
Prima di allocare i qubit, dichiarare e associare una matrice a tre elementi (una Result
per ogni qubit):
mutable resultArray = [Zero, size = 3];
La parola chiave mutable
che precede resultArray
consente di modificare la variabile in un secondo momento nel codice, ad esempio quando si aggiungono i risultati della misurazione.
Eseguire misurazioni in un ciclo for
e aggiungere i risultati alla matrice
Dopo le operazioni di trasformazione QFT, inserire il codice seguente:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
La funzione IndexRange
chiamata in una matrice (ad esempio, la matrice di qubit, qs
) restituisce un intervallo sugli indici della matrice.
In questo caso, viene usato nel ciclo for
per misurare in sequenza ogni qubit che usa l'istruzione M(qs[i])
.
Ogni tipo Result
misurato (Zero
o One
) viene quindi aggiunto alla posizione di indice corrispondente in resultArray
con un'istruzione update-and-reassign.
Nota
La sintassi di questa istruzione è univoca per Q#, ma corrisponde alla riassegnazione di variabili simili resultArray[i] <- M(qs[i])
vista in altri linguaggi, ad esempio F# e R.
La parola chiave set
viene sempre usata per riassegnare le variabili associate tramite mutable
.
Restituire resultArray
Con tutti e tre i qubit misurati e i risultati aggiunti a resultArray
, è possibile reimpostare e deallocare i qubit come prima in modo sicuro. Per restituire le misurazioni, inserire:
return resultArray;
Eseguire il circuito QFT con le misurazioni
Ora modificare la posizione delle funzioni DumpMachine
in modo da restituire lo stato prima e dopo le misurazioni.
Il codice Q# finale avrà un aspetto simile al seguente:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
Suggerimento
Ricordarsi di salvare il file ogni volta che si introduce una modifica al codice prima di eseguirlo di nuovo.
- Prima di eseguire il programma, verificare nella barra di stato nella parte inferiore di VS Code che il target profilo sia impostato su Q#: Senza restrizioni. Per modificare il target profilo, selezionare il target profilo nella barra di stato e selezionare Senza restrizioni dal menu a discesa. Se il target profilo non è impostato su Senza restrizioni, verrà visualizzato un errore quando si esegue il programma.
- Per eseguire il programma, selezionare Esegui Q# file dall'elenco a discesa dell'icona di riproduzione in alto a destra oppure premere CTRL+5. Il programma esegue l'operazione
Main()
nel simulatore predefinito. - Gli
Message
output eDumpMachine
vengono visualizzati nella console di debug.
L'output dovrebbe essere simile al seguente:
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
Questo output illustra alcuni aspetti diversi:
- Confrontando il risultato restituito con la pre-misurazione
DumpMachine
, è chiaro che non viene illustrata la sovrapposizione post-QFT agli stati di base. Una misurazione restituisce solo uno stato di base, con una probabilità determinata dall'ampiezza di tale stato nella funzione d'onda del sistema. - Dalla post-misurazione
DumpMachine
si può notare che la misurazione cambia lo stato stesso, proiettandolo dalla sovrapposizione iniziale agli stati di base al singolo stato di base corrispondente al valore misurato.
Ripetendo questa operazione più volte, si noterà che le statistiche dei risultati inizieranno a illustrare la sovrapposizione ugualmente ponderata dello stato post-QFT, che dà origine a un risultato casuale per ogni esecuzione. Tuttavia, oltre a essere inefficiente e comunque imperfetta, questa operazione riprodurrebbe solo le ampiezze relative degli stati di base, non le fasi relative tra di loro. Quest'ultimo non è un problema in questo esempio, ma si noterebbe che vengono visualizzate fasi relative se per il QFT venisse fornito un input più complesso rispetto a $\ket{000}$.
Usare le Q# operazioni per semplificare il circuito QFT
Come accennato nell'introduzione, gran parte della potenza di Q# è basata sul fatto che consente di astrarre i problemi di gestione dei singoli qubit.
In effetti, se si desidera sviluppare programmi quantistici applicabili e su larga scala, la preoccupazione che un'operazione H
venga eseguita prima o dopo una determinata rotazione potrebbe solo rallentare l'utente. Azure Quantum fornisce l'operazione ApplyQFT
, che è possibile usare e applicare per un numero qualsiasi di qubit.
Sostituire tutti gli elementi dalla prima
H
operazione all'operazioneSWAP
, inclusiva, con:ApplyQFT(qs);
Il codice dovrebbe ora essere simile al seguente
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { set resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }
Eseguire di nuovo il Q# programma e notare che l'output è uguale a quello precedente.
Per visualizzare il vantaggio reale dell'uso Q# delle operazioni, modificare il numero di qubit in un valore diverso da
3
:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
È quindi possibile applicare il QFT appropriato per qualsiasi numero di qubit, senza doversi preoccupare di aggiungere nuove H
operazioni e rotazioni in ogni qubit.
Contenuto correlato
Esplorare altre esercitazioni su Q#:
- Il generatore di numeri casuali quantistici mostra come scrivere un Q# programma che genera numeri casuali da qubit in sovrapposizione.
- L'algoritmo di ricerca di Grover mostra come scrivere un Q# programma che usa l'algoritmo di ricerca di Grover.
- L'entanglement quantistico mostra come scrivere un Q# programma che manipola e misura i qubit e illustra gli effetti della sovrapposizione e dell'entanglement.
- I kata quantistici sono esercitazioni e esercizi di programmazione auto-ritmo volti a insegnare contemporaneamente gli elementi del calcolo quantistico e Q# della programmazione.