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: Diagramma di un circuito Quantum Fourier Transform.

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

Creare un nuovo Q# file

  1. In VS Code selezionare File > nuovo file di testo
  2. Salvare il file come QFTcircuit.qs. Questo file conterrà il Q# codice per il programma.
  3. 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:

Diagramma che mostra un circuito per tre qubit QFT fino al primo hadamard.

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):

Diagramma che mostra un circuito per tre qubit Quantum Fourier Transform tramite il primo 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#:

Diagramma che mostra un circuito per tre qubit Quantum Fourier Transform.

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[]).

  1. 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.
  2. 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.
  3. Gli Message output e DumpMachine 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.

  1. 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.
  2. 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.
  3. Gli Message output e DumpMachine 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:

  1. 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.
  2. 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.

  1. Sostituire tutti gli elementi dalla prima H operazione all'operazione SWAP , inclusiva, con:

    ApplyQFT(qs);
    
  2. 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;
    
    }
    
  3. Eseguire di nuovo il Q# programma e notare che l'output è uguale a quello precedente.

  4. 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.

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.