Självstudier: Utforska sammanflätning med Q#

Den här självstudien visar hur du skriver ett program som manipulerar och mäter qubits och visar effekterna av Q# superposition och sammanskarvningar.

Du kommer att skriva ett program som Bell heter för att demonstrera kvantentanglement. Namnet Bell syftar på Bell-tillstånd, vilket är specifika kvanttillstånd för två kvantbitar som används för att visa de enklaste exemplen på superposition och kvantsammanflätning.

Förutsättningar

Du kan också följa med i berättelsen utan att installera QDK och granska översikterna över programmeringsspråket och de första begreppen Q# inom kvantberäkning.

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

  • Skapa och kombinera åtgärder i Q # .
  • Skapa åtgärder för att placera qubitar i superposition, sammansnacka och mäta dem.
  • Demonstrera kvantseangling med ett Q# program som körs i en simulator.

Demonstrera qubitbeteende med QDK

Medan klassiska bitar innehåller ett enda binärt värde, 0 eller 1, kan tillståndet för en kvantbit vara i en superposition med 0 och 1. Begreppsmässigt kan tillståndet för en qubit ses som en riktning i ett abstrakt utrymme (även kallat en vektor). Ett qubittillstånd kan vara i vilken riktning som helst. De två klassiska tillstånden är två riktningar. De motsvarar en chans på 100 % att mäta 0 och en chans på 100 % att mäta 1.

Mätningen genererar ett binärt resultat och ändrar kvantbitens tillstånd. Mätningen genererar ett binärt värde, antingen 0 eller 1. Kvantbiten går från att vara i superposition (vilken riktning som helst) till ett av de klassiska tillstånden. När du därefter upprepar samma mätning utan några andra åtgärder får du samma binära resultat.

Flera kvantbitar kan vara sammanflätade. När vi mäter en sammanflätad kvantbit får vi även veta de andra kvantbitarnas tillstånd.

Skriva # Q-programmet

Målet är att förbereda två kvantbitar i ett visst kvanttillstånd, demonstrera hur de ska användas på qubitar med för att ändra deras tillstånd och demonstrera effekterna av Q# superposition och sammansvetsning. Du kommer att bygga upp detta bit för bit för att introducera qubit-tillstånd, åtgärder och mätning.

Initiera qubit med hjälp av mätning

Kodfragmentet nedan visar hur du arbetar med qubitar i Q# . Koden introducerar två åtgärder och M X transformerar tillståndet för en qubit.

En åtgärd definieras som tar som en parameter en qubit och en annan parameter, , som representerar det önskade tillståndet för SetQubitState desired den qubit som ska finnas i. Åtgärden SetQubitState utför en mätning på kvantbiten med åtgärden M. I Q# returnerar en qubit-mätning alltid antingen Zero eller One . Om mätningen returnerar ett värde som inte är lika med det önskade SetQubitState värdet", "vänder" qubiten. Det innebär att den kör en åtgärd som ändrar qubittillståndet till ett nytt tillstånd där sannolikheten för en mätning returneras och är X Zero One omvänd. På så sätt SetQubitState försätter alltid målkvarbiten i önskat tillstånd.

Ersätt innehållet i Program.qs med följande kod:

   namespace Bell {
       open Microsoft.Quantum.Intrinsic;
       open Microsoft.Quantum.Canon;

       operation SetQubitState(desired : Result, q1 : Qubit) : Unit {
           if desired != M(q1) {
               X(q1);
           }
       }
   }

Den här åtgärden kan nu anropas för att försätta en kvantbit i ett klassiskt tillstånd som antingen returnerar Zero 100 % av tiden eller One 100 % av tiden. Zero och One är konstanter som representerar de enda två möjliga resultaten från en kvantbitsmätning.

Åtgärden SetQubitState mäter kvantbiten. Om qubiten är i önskat tillstånd lämnar du den i sig. Annars ändras SetQubitState qubittillståndet till det önskade tillståndet genom att X köra åtgärden.

Om Q# åtgärder

En Q# åtgärd är en kvantunderrutin. Det innebär att det är en anropsbar rutin som innehåller anrop till andra kvantåtgärder.

Argumenten i en åtgärd anges som en tuppel inom parentes.

Returtypen för åtgärden anges efter ett kolon. I det här fallet har SetQubitState åtgärden ingen returtyp, så den markeras som att returnera Unit . Detta motsvarar i F#, vilket är ungefär detsamma som i C# och en tom tuppel i Python ( , som representeras Q# unit av void () Tuple[()] typtipset ).

Du har använt två kvantåtgärder i din första Q# åtgärd:

  • Åtgärden, M som mäter qubitens tillstånd
  • Åtgärden, X som vänder tillståndet för en qubit

En kvantåtgärd omvandlar tillståndet för en kvantbit. Ibland pratar man om kvantgrindar i stället för åtgärder, vilket motsvarar klassiska logiska grindar. Detta kommer från när man började använda kvantberäkning och algoritmerna bara var en teoretisk konstruktion som visualiserades som diagram, liknande kretsdiagram i klassisk databehandling.

Räkna mätresultat

En TestBellState-åtgärd läggs till för att demonstrera resultatet av åtgärden SetQubitState. Den här åtgärden tar Zero eller One som indata och anropar SetQubitState-åtgärden ett visst antal gånger med indatan. Därefter räknas det antal gånger som Zero returnerades från kvantbitsmätningen och antalet gånger som One returnerades. I den här första simuleringen av åtgärden förväntar man sig förstås att utdata visar att alla mätningar av qubituppsättningen med som parameterindata returnerar , och alla mätningar av en TestBellState qubituppsättning med som Zero Zero parameterindata One returnerar One . Dessutom läggs kod till i för att TestBellState demonstrera superposition och entanglement.

Lägg till följande åtgärd i Program.qs-filen (inuti namnområdet) efter att SetQubitState-åtgärden har slutförts:

   operation TestBellState(count : Int, initial : Result) : (Int, Int) {

       mutable numOnes = 0;
       use qubit = Qubit();
       for test in 1..count {
           SetQubitState(initial, qubit);
           let res = M(qubit);

           // Count the number of ones we saw:
           if res == One {       
               set numOnes += 1;
           }
       }

       SetQubitState(Zero, qubit);

       

       // Return number of times we saw a |0> and number of times we saw a |1>
       Message("Test results (# of 0s, # of 1s): ");
       return (count - numOnes, numOnes);
   }

Observera att det finns en rad före för att skriva ut ett förklarande meddelande i konsolen med funktionen return ( Message )[microsoft.quantum.intrinsic.message]

Åtgärden (TestBellState) kommer att upprepas för count iterationer, ange ett angivet initial-värde för en kvantbit och sedan mäta (M) resultatet. Den samlar in statistik om hur många nollor och ettor som vi har mätt och returnerar dem till anroparen. Den utför även en annan åtgärd som krävs. Den återställer kvantbiten till ett känt tillstånd (Zero) innan den returnerar, så att andra kan allokera kvantbiten i ett känt tillstånd. Detta krävs av use-instruktionen.

Om variabler i Q#

Som standard är variabler i Q# oföränderliga. Deras värde kanske inte ändras efter att de har bindats. Nyckelordet let används för att ange bindningen för en oföränderlig variabel. Åtgärdsargument är alltid oföränderliga.

Om du behöver en variabel vars värde kan ändras, till exempel numOnes i exemplet, kan du deklarera variabeln med nyckelordet mutable. Ett värde för en föränderlig variabel kan ändras med hjälp av en set-instruktion.

I båda fallen härleds variabeltypen av kompileraren. Q# kräver inte några typanteckningar för variabler.

Om use -instruktioner i Q#

use-instruktionen är också speciell för Q# . Den används till att allokera kvantbitar som ska användas i ett kodblock. I allokeras och frigörs alla qubitar dynamiskt, i stället för att vara fasta resurser som finns där under hela livslängden Q# för en komplex algoritm. En use-instruktion allokerar en uppsättning kvantbitar vid starten och frigör dem sedan i slutet av blocket.

Kör koden från kommandotolken

För att kunna köra koden måste vi berätta för kompilatorn vilken anropsbar som ska köras när vi anger dotnet run kommandot . Detta görs med en enkel ändring i filen genom att lägga till en rad med direkt före Q# @EntryPoint() anropningsbar: TestBellState åtgärden i det här fallet. Den fullständiga koden ska vara:

namespace Bell {
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;

    operation SetQubitState(desired : Result, target : Qubit) : Unit {
        if desired != M(target) {
            X(target);
        }
    }

    @EntryPoint()
    operation TestBellState(count : Int, initial : Result) : (Int, Int) {

        mutable numOnes = 0;
        use qubit = Qubit();
        for test in 1..count {
            SetQubitState(initial, qubit);
            let res = M(qubit);
            

            // Count the number of ones we saw:
            if res == One {
                  set numOnes += 1;
            }
        }

        SetQubitState(Zero, qubit);
        

    // Return number of times we saw a |0> and number of times we saw a |1>
    Message("Test results (# of 0s, # of 1s): ");
    return (count - numOnes, numOnes);
    }
}

För att köra programmet måste vi ange count initial argumenten och från kommandotolken. Vi väljer till exempel count = 1000 och initial = One . Ange följande kommando:

dotnet run --count 1000 --initial One

Och du bör se följande utdata:

Test results (# of 0s, # of 1s):
(0, 1000)

Om du försöker med initial = Zero bör du observera:

dotnet run --count 1000 --initial Zero
Test results (# of 0s, # of 1s):
(1000, 0)

Förbered superposition

Nu ska vi titta på hur Q# uttrycker sätt att placera qubitar i superposition. Kom ihåg att tillståndet för en kvantbit kan vara i en superposition på 0 och 1. Detta kan åstadkommas med hjälp av Hadamard åtgärden . Om kvantbiten finns i något av de klassiska tillstånden (där en mätning alltid returnerar Zero eller One), kommer Hadamard- eller H-åtgärden att försätta kvantbiten i ett tillstånd där en mätning returnerar Zero 50 % av tiden och One 50 % av tiden. Konceptuellt kan qubiten ses som halvvägs mellan Zero och One . När du nu i simuleringen av TestBellState åtgärden returnerar resultatet ungefär lika många och Zero One efter mätningen.

X vänder qubittillståndet

Först försöker vi bara vända qubiten (om qubiten är i tillståndet kommer den Zero att vändas till One och vice versa). Detta görs genom att köra en X-åtgärd innan den mäts i TestBellState:

X(qubit);
let res = M(qubit);

Nu är resultaten omvända:

dotnet run --count 1000 --initial One
Test results (# of 0s, # of 1s):
(1000, 0)
dotnet run --count 1000 --initial Zero
Test results (# of 0s, # of 1s):
(0, 1000)

Nu ska vi utforska kvantegenskaperna för kvantbitarna.

H förbereder superposition

Allt du behöver göra är att ersätta åtgärden X i föregående körning med en - eller H Hadamard-åtgärd. I stället för att vända qubiten hela vägen från 0 till 1 vänder det bara halvvägs. De ersatta raderna i TestBellState ser nu ut så här:

H(qubit);
let res = M(qubit);

Nu blir resultatet mer intressant:

dotnet run --count 1000 --initial One
Test results (# of 0s, # of 1s):
(496, 504)
dotnet run --count 1000 --initial Zero
Test results (# of 0s, # of 1s):
(506, 494)

Varje mått frågar efter ett klassiskt värde, men qubiten är halvvägs mellan 0 och 1, så vi får (statistiskt) 0 halva tiden och 1 halva tiden. Detta kallas för superposition och ger oss den första verkliga inblicken i ett kvanttillstånd.

Förbereda sammanflätning

Nu ska vi titta på Q# hur uttrycker sätt att sammansnacka qubitar. Ange först den första qubiten till det ursprungliga tillståndet och använd sedan åtgärden H för att placera den i superposition. Innan du mäter den första qubiten använder du sedan en ny åtgärd ( CNOT ), som står för Controlled-NOT. Resultatet av att köra den här åtgärden på två qubits är att vända den andra qubiten om den första qubiten är One . Nu är de två kvantbitarna sammanflätade. Statistiken för den första qubiten har inte ändrats (50–50 chansen för en eller efter mätningen), men när den andra qubiten mäts är den alltid samma som den vi mätte för den första Zero One qubiten. Vår CNOT har sammanflätat de två kvantbitarna, vilket innebär att det som händer med en av dem händer även för den andra. Om du skulle omvända mätningarna (den andra kvantbiten före den första), skulle samma sak inträffa. Det första måttet blir slumpmässigt och det andra blir låst med det värde som identifierades för den första mätningen.

För att förbereda samman samman sammanbrott allokerar du först två qubitar i stället för en i TestBellState :

use (q0, q1) = (Qubit(), Qubit());

På så sätt kan vi lägga till en ny åtgärd ( CNOT ) innan vi mäter ( ) i M TestBellState :

SetQubitState(initial, q0);
SetQubitState(Zero, q1);

H(q0);
CNOT(q0, q1);
let res = M(q0);

Lägg till SetQubitState ytterligare en åtgärd för att initiera den första qubiten för att se till att den alltid är i tillståndet Zero .

Återställ slutligen den andra qubiten innan du släpper den.

SetQubitState(Zero, q0);
SetQubitState(Zero, q1);

Den fullständiga rutinen ser nu ut så här:

    operation TestBellState(count : Int, initial : Result) : (Int, Int) {

        mutable numOnes = 0;
        use (q0, q1) = (Qubit(), Qubit());
        for test in 1..count {
            SetQubitState(initial, q0);
            SetQubitState(Zero, q1);

            H(q0);
            CNOT(q0,q1);
            let res = M(q0);

            // Count the number of ones we saw:
            if res == One {
                set numOnes += 1;
            }
        }

        SetQubitState(Zero, q0);
        SetQubitState(Zero, q1);
        

        // Return number of times we saw a |0> and number of times we saw a |1>
        return (count-numOnes, numOnes);
    }

Om du kör det här får du exakt samma 50–50 resultat som vi fick tidigare. Men det intressanta är hur den andra qubiten reagerar på den första mätningen. Den här statistiken läggs till med en ny version av TestBellState åtgärden:

    operation TestBellState(count : Int, initial : Result) : (Int, Int, Int) {
        mutable numOnes = 0;
        mutable agree = 0;
        use (q0, q1) = (Qubit(), Qubit());
        for test in 1..count {
            SetQubitState(initial, q0);
            SetQubitState(Zero, q1);

            H(q0);
            CNOT(q0, q1);
            let res = M(q0);

            if M(q1) == res {
                set agree += 1;
            }

            // Count the number of ones we saw:
            if res == One {
                set numOnes += 1;
            }
        }

        SetQubitState(Zero, q0);
        SetQubitState(Zero, q1);
        

        // Return times we saw |0>, times we saw |1>, and times measurements agreed
        Message("Test results (# of 0s, # of 1s, # of agreements)");
        return (count-numOnes, numOnes, agree);
    }

Det nya returvärdet (agree) registrerar varje gång mätningen av den första kvantbiten matchar mätningen av den andra kvantbiten.

Om du kör koden resulterar det i:

dotnet run --count 1000 --initial One
(505, 495, 1000)
dotnet run --count 1000 --initial Zero
Test results (# of 0s, # of 1s, # of agreements)
(507, 493, 1000)

Som vi angav i översikten har statistiken för den första qubiten inte ändrats (50–50 chans för 0 eller 1), men när du mäter den andra qubiten är det alltid samma som för den första qubiten, eftersom de är sammankopplade!

Nästa steg

Självstudien Grovers sökning visar hur du skapar och kör Grover-sökning, en av de mest populära kvantberäkningsalgoritmerna och är ett bra exempel på ett program som kan användas för att lösa verkliga problem med Q# kvantberäkning.

Konfigurera Azure Quantum fler sätt att lära sig Q# och kvantprogrammering.