Självstudie: Implementera Quantum Fourier-transformering i Q#

Den här självstudien visar hur du skriver och simulerar ett grundläggande kvantprogram som fungerar på enskilda kvantbitar.

Även om Q# det främst skapades som ett högnivåprogrammeringsspråk för storskaliga kvantprogram, kan det också användas för att utforska den lägre nivån av kvantprogrammering, dvs. Mer specifikt tar den här självstudien en närmare titt på Quantum Fourier Transform (QFT), en underrutin som är integrerad i många större kvantalgoritmer.

I den här självstudien får du lära dig att:

  • Definiera kvantåtgärder i Q#.
  • Skriva Quantum Fourier Transform-kretsen
  • Simulera en kvantåtgärd från kvantbitsallokering till mätningsutdata.
  • Observera hur kvantsystemets simulerade vågfunktion utvecklas under hela åtgärden.

Kommentar

Den här lägre vyn av kvantinformationsbearbetning beskrivs ofta i termer av kvantkretsar, som representerar den sekventiella tillämpningen av grindar eller åtgärder för specifika kvantbitar i ett system. Därför kan de åtgärder med en och flera kvantbitar som du tillämpar sekventiellt enkelt representeras i kretsdiagram. Den fullständiga fourier-kvanttransformningen med tre kvantbitar som används i den här självstudien har till exempel följande representation som en krets: Diagram över en Quantum Fourier Transform-krets.

Dricks

Om du vill påskynda din kvantberäkningsresa kan du kolla in Kod med Azure Quantum, en unik funktion på Azure Quantum-webbplatsen. Här kan du köra inbyggda Q# exempel eller egna Q# program, generera ny Q# kod från dina frågor, öppna och köra koden i VS Code för webben med ett klick och ställa frågor till Copilot om kvantberäkning.

Förutsättningar

Skapa en ny Q# fil

  1. I VS Code väljer du Fil > Ny textfil
  2. Spara filen som QFTcircuit.qs. Den här filen innehåller Q# koden för ditt program.
  3. Öppna QFTcircuit.qs.

Skriva en QFT-krets i Q#

Den första delen av den här självstudien Q# består av att definiera åtgärden Main, som utför kvant-Fourier-transformering på tre kvantbitar. Funktionen DumpMachine används för att observera hur den simulerade vågfunktionen i tre-qubit-systemet utvecklas under hela åtgärden. I den andra delen av självstudien lägger du till måttfunktioner och jämför kvantbitarnas tillstånd före och efter mätning.

Du skapar åtgärden steg för steg. Kopiera och klistra in koden i följande avsnitt i QFTcircuit.qs-filen .

Du kan visa den fullständiga Q# koden för det här avsnittet som referens.

Importera nödvändiga Q# bibliotek

Q# Importera relevanta Microsoft.Quantum.* namnområden i filen.

import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;

// operations go here

Definiera åtgärder med argument och returer

Definiera sedan åtgärden Main :

operation Main() : Unit {
    // do stuff
}

Åtgärden Main() tar aldrig argument och returnerar för tillfället ett Unit -objekt, vilket är detsamma som att void returnera i C# eller en tom tuppl, Tuple[()], i Python. Senare ändrar du åtgärden för att returnera en matris med mätresultat.

Allokera kvantbitar

I åtgärden Q# allokerar du ett register med tre kvantbitar med nyckelordet use . Med useallokeras kvantbitarna automatiskt i tillståndet $\ket{0}$ .

use qs = Qubit[3]; // allocate three qubits

Message("Initial state |000>:");
DumpMachine();

Precis som i verkliga kvantberäkningar Q# kan du inte komma åt kvantbitstillstånd direkt. Åtgärden skriver dock DumpMachine ut target datorns aktuella tillstånd, så att den kan ge värdefull insikt för felsökning och inlärning när den används tillsammans med simulatorn för fullständigt tillstånd.

Tillämpa enkla kvantbitar och kontrollerade åtgärder

Därefter tillämpar du de åtgärder som utgör Main själva åtgärden. Q# innehåller redan många av dessa och andra grundläggande kvantåtgärder i Microsoft.Quantum.Intrinsic namnområdet.

Kommentar

Observera att Microsoft.Quantum.Intrinsic det inte importerades i det tidigare kodfragmentet med de andra namnrymderna, eftersom det läses in automatiskt av kompilatorn för alla Q# program.

Den första åtgärden som tillämpas är H (Hadamard)-åtgärden till den första qubiten:

Diagram som visar en krets för tre qubit QFT genom första Hadamard.

Om du vill tillämpa en åtgärd på en specifik kvantbit från ett register (till exempel en enskild Qubit från en matris Qubit[]) använder du standardindexanmälning. Så att tillämpa åtgärden på H den första kvantbiten i registret qs tar formuläret:

H(qs[0]);

Förutom att tillämpa åtgärden på H enskilda kvantbitar består QFT-kretsen främst av kontrollerade R1 rotationer. En R1(θ, <qubit>) åtgärd i allmänhet lämnar komponenten $\ket{0}$ i qubiten oförändrad när en rotation av $e^{i\theta}$ tillämpas på $\ket{1}$-komponenten.

Q# gör det enkelt att villkora körningen av en åtgärd på en eller flera kontrollkvabitar. I allmänhet föregås anropet av Controlled, och åtgärdsargumenten ändras på följande sätt:

Op(<normal args>) $\to$ Controlled Op([<control qubits>], (<normal args>))

Observera att argumentet för kontrollkvabit måste vara en matris, även om det är för en enda qubit.

De kontrollerade åtgärderna i QFT är de R1 åtgärder som fungerar på den första kvantbiten (och styrs av den andra och tredje kvantbiten):

Diagram som visar en krets för tre kvantbitar Quantum Fourier-transformering genom den första kvantbiten.

I filen Q# anropar du dessa åtgärder med följande instruktioner:

Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));

Funktionen PI() används för att definiera rotationerna i termer av pi-radianer.

Tillämpa SWAP-åtgärden

När du har tillämpat relevanta H åtgärder och kontrollerade rotationer på den andra och tredje kvantbiten ser kretsen ut så här:

//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));

//third qubit:
H(qs[2]);

Slutligen tillämpar du en SWAP åtgärd på den första och tredje kvantbiten för att slutföra kretsen. Detta är nödvändigt eftersom typen av kvant fourier-transformering matar ut kvantbitarna i omvänd ordning, så växlingarna möjliggör sömlös integrering av subrutinen i större algoritmer.

SWAP(qs[2], qs[0]);

Nu har du skrivit klart kvantbitsåtgärden på kvant fourier-transformering till din Q# åtgärd:

Diagram som visar en krets för tre qubit Quantum Fourier Transform.

Frigör kvantbitar

Det sista steget är att anropa DumpMachine() igen för att se tillståndet efter åtgärden och frigöra kvantbitarna. Kvantbitarna var i tillståndet $\ket{0}$ när du allokerade dem och måste återställas till deras ursprungliga tillstånd med hjälp av åtgärden ResetAll .

Att kräva att alla kvantbitar uttryckligen återställs till $\ket{0}$ är en grundläggande funktion Q#i , eftersom andra åtgärder kan känna till deras tillstånd exakt när de börjar använda samma kvantbitar (en knapp resurs). Dessutom säkerställer detta att de inte är sammanflätade med andra kvantbitar i systemet. Om återställningen inte utförs i slutet av ett use allokeringsblock kan ett körningsfel uppstå.

Lägg till följande rader i Q# filen:

Message("After:");
DumpMachine();

ResetAll(qs); // deallocate qubits

Den fullständiga QFT-åtgärden

Programmet Q# har slutförts. Din QFTcircuit.qs-fil bör nu se ut så här:

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

}                                                                                                                                                                               

Kör QFT-kretsen

För tillfället returnerar inte åtgärden Main något värde – åtgärden returnerar Unit värdet. Senare ändrar du åtgärden för att returnera en matris med mätresultat (Result[]).

  1. Innan du kör programmet kontrollerar du i statusfältet längst ned i VS Code att profilen target är inställd på Q#: Obegränsad. Om du vill ändra profilen target väljer du profilen target i statusfältet och väljer Obegränsad på den nedrullningsbara menyn. Om profilen target inte är inställd på Obegränsad får du ett fel när du kör programmet.
  2. Om du vill köra programmet väljer du Kör fil i listrutan uppspelningsikon längst upp till höger eller trycker på Ctrl+F5. Q# Programmet kör Main() åtgärden på standardsimulatorn.
  3. Utdata Message och DumpMachine visas i felsökningskonsolen.

Om du är nyfiken på hur andra indatatillstånd påverkas rekommenderar vi att du experimenterar med att tillämpa andra qubit-åtgärder före transformeringen.

Lägga till mått i QFT-kretsen

Visningen från DumpMachine funktionen visade resultatet av åtgärden, men tyvärr säger en hörnsten i kvantmekaniken att ett verkligt kvantsystem inte kan ha en DumpMachine sådan funktion. I stället extraheras informationen genom mätningar, som i allmänhet inte bara misslyckas med att tillhandahålla information om det fullständiga kvanttillståndet, utan också kan ändra själva systemet drastiskt.

Det finns många typer av kvantmått, men exemplet här fokuserar på de mest grundläggande: projektiva mätningar på enskilda kvantbitar. Vid mätning i en viss bas (till exempel beräkningsbasen $ { \ket{0}, \ket{1} } $), projiceras qubittillståndet på det bastillstånd som mättes, vilket förstör eventuell superposition mellan de två.

Ändra QFT-åtgärden

Om du vill implementera mått i ett Q# program använder du åtgärden M , som returnerar en Result typ.

Main Ändra först åtgärden för att returnera en matris med mätresultat, , Result[]i stället för Unit.

operation Main() : Result[] {

Definiera och initiera Result[] matris

Innan du allokerar kvantbitar deklarerar och binder du en matris med tre element (en Result för varje qubit):

mutable resultArray = [Zero, size = 3];

Med mutable nyckelordsförhandsinställningen resultArray kan variabeln ändras senare i koden, till exempel när du lägger till dina mätresultat.

Utföra mått i en for loop och lägga till resultat i matrisen

Efter QFT-transformeringsåtgärderna infogar du följande kod:

for i in IndexRange(qs) {
    set resultArray w/= i <- M(qs[i]);
}

Funktionen IndexRange som anropas på en matris (till exempel matrisen med kvantbitar, qs) returnerar ett intervall över matrisens index. Här används den i loopen for för att sekventiellt mäta varje qubit med hjälp av -instruktionen M(qs[i]) . Varje uppmätt Result typ (antingen Zero eller One) läggs sedan till i motsvarande indexposition i resultArray med en uppdaterings- och omtilldelningsinstruktor.

Kommentar

Syntaxen för den här instruktionen är unik för Q#, men motsvarar den liknande variabelomtilldelning resultArray[i] <- M(qs[i]) som visas på andra språk som F# och R.

Nyckelordet set används alltid för att omtilldela variabler som är bundna med hjälp av mutable.

Återvända resultArray

När alla tre kvantbitarna har mätts och resultaten har lagts till resultArrayi är det säkert att återställa och frigöra kvantbitarna som tidigare. Om du vill returnera måtten infogar du:

return resultArray;

Kör QFT-kretsen med måtten

Ändra nu placeringen av DumpMachine funktionerna så att tillståndet matas ut före och efter måtten. Den slutliga Q# koden bör se ut så här:

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;

}

Dricks

Kom ihåg att spara filen varje gång du introducerar en ändring av koden innan du kör den igen.

  1. Innan du kör programmet kontrollerar du i statusfältet längst ned i VS Code att profilen target är inställd på Q#: Obegränsad. Om du vill ändra profilen target väljer du profilen target i statusfältet och väljer Obegränsad på den nedrullningsbara menyn. Om profilen target inte är inställd på Obegränsad får du ett fel när du kör programmet.
  2. Om du vill köra programmet väljer du Kör Q# fil i listrutan uppspelningsikon längst upp till höger eller trycker på Ctrl+5. Programmet kör Main() åtgärden på standardsimulatorn.
  3. Utdata Message och DumpMachine visas i felsökningskonsolen.

Dina utdata bör se ut ungefär så här:

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]

Dessa utdata illustrerar några olika saker:

  1. Om du jämför det returnerade resultatet med förmätningen DumpMachineillustrerar det uppenbarligen inte superpositionen efter QFT över bastillstånd. En mätning returnerar endast ett enskilt bastillstånd, med en sannolikhet som bestäms av amplituden för det tillståndet i systemets vågfunktion.
  2. Från eftermätningen DumpMachineser du att mätningen ändrar själva tillståndet och projicerar det från den första superpositionen över bastillstånd till det enskilda bastillståndet som motsvarar det uppmätta värdet.

Om du upprepar den här åtgärden många gånger ser du att resultatstatistiken börjar illustrera den lika viktade superpositionen av tillståndet efter QFT som ger upphov till ett slumpmässigt resultat på varje skott. Men förutom att vara ineffektiv och fortfarande ofullkomlig, skulle detta ändå bara reproducera de relativa amplituderna i bastillstånden, inte de relativa faserna mellan dem. Det senare är inte ett problem i det här exemplet, men du skulle se relativa faser visas om du får mer komplexa indata till QFT än $\ket{000}$.

Använda åtgärderna Q# för att förenkla QFT-kretsen

Som nämnts i inledningen vilar mycket av Q#"makt i det faktum att det gör att du kan abstrakta bort oron för att hantera enskilda kvantbitar. Om du vill utveckla fullskalig, tillämplig kvantprogram, skulle det bara göra dig långsammare att oroa dig för om en H åtgärd utförs före eller efter en viss rotation. Azure Quantum tillhandahåller åtgärden ApplyQFT som du kan använda och tillämpa för valfritt antal kvantbitar.

  1. Ersätt allt från den första H åtgärden till åtgärden SWAP , inklusive, med:

    ApplyQFT(qs);
    
  2. Koden bör nu se ut så här

    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. Kör programmet Q# igen och observera att utdata är samma som tidigare.

  4. Om du vill se den verkliga fördelen med att använda Q# åtgärder ändrar du antalet kvantbitar till något annat än 3:

mutable resultArray = [Zero, size = 4];

use qs = Qubit[4];
//...

Du kan därför använda rätt QFT för ett visst antal kvantbitar, utan att behöva oroa dig för att lägga till nya H åtgärder och rotationer på varje kvantbit.

Utforska andra Q# självstudier:

  • Slumptalsgenerator för kvant visar hur du skriver ett Q# program som genererar slumpmässiga tal av kvantbitar i superposition.
  • Grover sökalgoritm visar hur du skriver ett Q# program som använder Grover sökalgoritm.
  • Kvantsammanflätning visar hur du skriver ett Q# program som manipulerar och mäter kvantbitar och visar effekterna av superposition och sammanflätning.
  • Quantum Katas är självstudier och programmeringsövningar som syftar till att lära ut elementen i kvantberäkning och Q# programmering på samma gång.