Zelfstudie: kennismaken met verstrengeling met Q##

Deze zelfstudie laat zien hoe u een programma schrijft dat qubits bewerkt en meet en de effecten van Q# superpositie en verstrengeling demonstreert.

U schrijft een toepassing met de naam Bell om kwantumverstrengeling te demonstreren. De naam Bell is een verwijzing naar de Bell-toestanden, wat specifieke kwantumtoestanden van twee qubits zijn die worden gebruikt om de eenvoudigste voorbeelden van superpositie en kwantumverstrengeling voor te stellen.

Vereisten

U kunt het verhaal ook volgen zonder de QDK te installeren. Bekijk daarbij de overzichten van de programmeertaal en de eerste concepten Q# van kwantumcomputing.

In deze zelfstudie leert u het volgende:

  • Bewerkingen maken en combineren in Q # .
  • Maak bewerkingen om qubits in superpositie te zetten, verstrengelen en meten.
  • Kwantumverstrengeling demonstreren met een Q# programma dat wordt uitgevoerd in een simulator.

Gedrag van qubits demonstreren met de QDK

Waar klassieke bits één binaire waarde bevatten, zoals een 0 of een 1, kan de toestand van een qubit in een superpositie zijn van 0 en 1. Conceptueel gezien kan de toestand van een qubit worden gezien als een richting in een abstracte ruimte (ook wel een vector genoemd). Een qubittoestand kan zich in een van de mogelijke richtingen. De twee klassieke toestanden zijn de twee richtingen, die een 100% kans vertegenwoordigen dat de meting 0 oplevert en een 100% kans dat de meting 1 oplevert.

De handeling van het meten produceert een binair resultaat en verandert de toestand van een qubit. Meting produceert een binaire waarde, 0 of 1. De qubit gaat over van de toestand van superpositie (willekeurige richting) in een van de klassieke toestanden. Daarna levert herhaling van dezelfde meting zonder tussenliggende bewerkingen hetzelfde binaire resultaat op.

Meerdere qubits kunnen worden verstrengeld. Wanneer we een meting uitvoeren van één verstrengelde qubit, wordt onze kennis van de toestand van de andere qubit(s) ook bijgewerkt.

De Q-toepassing # schrijven

Het doel is om twee qubits voor te bereiden in een specifieke kwantumtoestand, om te laten zien hoe u op qubits kunt werken met om hun toestand te wijzigen en de effecten van superpositie en verstrengeling te Q# demonstreren. U bouwt dit stuk voor stuk op om qubit-staten, -bewerkingen en -metingen te introduceren.

qubit initialiseren met behulp van meting

In het onderstaande codefragment ziet u hoe u met qubits werkt in Q# . De code introduceert twee bewerkingen en die de toestand van een M X qubit transformeren.

Er wordt een bewerking gedefinieerd die als parameter een qubit en een andere parameter, , gebruikt die de gewenste status vertegenwoordigt waarin de SetQubitState desired qubit zich moet hebben. Met de bewerking SetQubitState wordt een meting uitgevoerd op de qubit met behulp van de bewerking M. In Q# retourneert een qubitmeting altijd Zero of One . Als de meting een waarde retourneert die niet gelijk is aan de gewenste waarde, 'spiegelt' de qubit. Dat wil zeggen dat er een bewerking wordt uitgevoerd, die de SetQubitState qubittoestand wijzigt in een nieuwe toestand waarin de waarschijnlijkheden van een meting worden X Zero One teruggedraaid. Op deze manier SetQubitState wordt de doel-qubit altijd in de gewenste toestand.

Vervang de inhoud van Program.qs door de volgende code:

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

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

Deze bewerking kan nu worden aangeroepen om een qubit in te stellen op een klassieke toestand, met in 100% van de gevallen Zero of One als resultaat. Zero en One zijn constanten die de enige twee mogelijke resultaten van een meting van een qubit vertegenwoordigen.

Met de bewerking SetQubitState wordt de qubit gemeten. Als de qubit de gewenste status heeft, laat u deze staan; anders verandert de qubittoestand door de bewerking uit SetQubitState te voeren in de X gewenste toestand.

Over Q# bewerkingen

Een Q# bewerking is een kwantumsubroutine. Dat wil zeggen dat het een aanroepbare routine is die aanroepen naar andere kwantumbewerkingen bevat.

De argumenten voor een bewerking worden tussen haakjes opgegeven als een tupel.

Het retourtype van de bewerking wordt na een dubbele punt opgegeven. In dit geval heeft de SetQubitState bewerking geen retourtype, dus wordt deze gemarkeerd als het retourneren Unit van . Dit is het equivalent van in F#, dat ongeveer vergelijkbaar is met in C# en een lege Q# unit void tuple in Python ( () , vertegenwoordigd door het type hint Tuple[()] ).

U hebt in uw eerste bewerking twee kwantumbewerkingen Q# gebruikt:

  • De M bewerking, waarmee de status van de qubit wordt meten
  • De X bewerking, waarmee de toestand van een qubit wordt ge flipt

Een kwantumbewerking transformeert de toestand van een qubit. Soms worden kwantumbewerkingen ook wel kwantumpoorten genoemd, analoog aan klassieke logische poorten. Dit vindt zijn oorsprong in het begintijdperk van de kwantumcomputing, toen algoritmen niets meer waren dan een theoretische constructie en werden gevisualiseerd als schema's, vergelijkbaar met circuitschema's in klassieke computing.

Meetresultaten tellen

Om het effect van de bewerking SetQubitState te demonstreren, wordt er vervolgens een bewerking TestBellState toegevoegd. Deze bewerking krijgt als invoer een Zero of One en roept de bewerking SetQubitState een aantal keer aan met die invoer, en telt het aantal keren dat Zero het resultaat is van de meting van de qubit en het aantal keren dat One wordt geretourneerd. In deze eerste simulatie van de bewerking wordt natuurlijk verwacht dat de uitvoer laat zien dat alle metingen van de qubit die is ingesteld met als parameterinvoer, retourneren en dat alle metingen van een qubit die is ingesteld met als TestBellState Zero de parameterinvoer Zero One One retourneren. Verderop wordt code toegevoegd aan om TestBellState superpositie en verstrengeling te demonstreren.

Voeg na het einde van bewerking SetQubitState de volgende bewerking toe aan Program.qs bestand binnen de naamruimte:

   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);
   }

Houd er rekening mee dat er een regel vóór de is om een verklarend bericht in de console af te drukken met de functie return ( Message )[microsoft.quantum.intrinsic.message]

Deze bewerking (TestBellState) wordt gedurende count iteraties als een lus uitgevoerd, er wordt een opgegeven initial-waarde voor een qubit gedefinieerd en vervolgens wordt het resultaat, (M), gemeten. Er worden statistieken verzameld over het aantal nullen en enen dat we hebben gemeten, waarna ze naar de aanroepende functie worden geretourneerd. Er wordt nog een andere noodzakelijke bewerking uitgevoerd. De qubit wordt opnieuw ingesteld op een bekende toestand (Zero) voordat deze wordt geretourneerd, zodat anderen deze qubit een bekende toestand kunnen toewijzen. Dit wordt door de instructie use vereist.

Over variabelen in Q#

Standaard zijn variabelen in Q# onveranderbaar; de waarde ervan kan niet worden gewijzigd nadat ze zijn gebonden. Het sleutelwoord let wordt gebruikt om de binding van een onveranderlijke variabele aan te geven. Bewerkingsargumenten zijn altijd onveranderbaar.

Als u een variabele nodig hebt waarvan de waarde kan worden gewijzigd, zoals numOnes in het voorbeeld, kunt u de variabele declareren met het sleutelwoord mutable. De waarde van een veranderlijke variabele kan worden gewijzigd met behulp van een set-instructie.

In beide gevallen wordt het type variabele door de compiler afgeleid. Q# vereist geen typeaantekeningen voor variabelen.

Over use -instructies in Q#

De use instructie is ook speciaal voor Q# . Deze wordt gebruikt om qubits toe te wijzen voor gebruik in een codeblok. In worden alle qubits dynamisch toegewezen en vrijgegeven, in plaats van vaste resources te zijn die er voor de hele levensduur van een Q# complex algoritme zijn. Een use-instructie wijst aan het begin een set qubits toe en geeft deze qubits aan het einde van het blok vrij.

De code uitvoeren vanaf de opdrachtprompt

Als u de code wilt uitvoeren, moet u de compiler vertellen welke aanroepbare code moet worden uitgevoerd wanneer we de opdracht dotnet run geven. Dit wordt gedaan met een eenvoudige wijziging in het bestand door een regel toe te voegen met direct vóór de aanroepbare: Q# de bewerking in dit @EntryPoint() TestBellState geval. De volledige code moet zijn:

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);
    }
}

Als u het programma wilt uitvoeren, moeten we count argumenten en opgeven vanaf de initial opdrachtprompt. Laten we bijvoorbeeld en count = 1000 initial = One kiezen. Voer de volgende opdracht in:

dotnet run --count 1000 --initial One

En u moet de volgende uitvoer zien:

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

Als u het met initial = Zero probeert, moet u het volgende observeren:

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

Superpositie voorbereiden

Laten we nu eens kijken hoe we Q# manieren uitdrukken om qubits in superpositie te zetten. Zoals eerder gezegd, kan de toestand van een qubit een superpositie zijn van 0 en 1. Dit kan worden bereikt met behulp van Hadamard bewerking. Als de qubit zich in een van de klassieke toestanden bevindt (waarbij een meting altijd Zero of altijd One retourneert) wordt de qubit met de bewerking Hadamard of H in een toestand geplaatst waarin een meting van de qubit in 50% van de gevallen Zero retourneert en in 50% van de gevallen One. Conceptueel gezien kan de qubit worden gezien als halverwege de Zero en One . Wanneer de bewerking nu wordt gesimuleerd, retourneren de resultaten ongeveer een gelijk TestBellState aantal en na de Zero One meting.

X spiegelt de toestand van de qubit

Laten we eerst proberen om de qubit te spiegelen (als de qubit in staat is, wordt deze Zero ge flipt naar One en vice versa). Dit wordt bereikt door een bewerking X uit te voeren voordat we de qubit meten in TestBellState:

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

De resultaten worden nu omgekeerd:

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)

Laten we nu de kwantumeigenschappen van de qubits verkennen.

H bereidt superpositie voor

U hoeft alleen maar de bewerking in de vorige X run te vervangen door een - of H Hadamard-bewerking. In plaats van de qubit helemaal van 0 naar 1 te spiegelen, wordt deze slechts halverwege gespiegeld. De vervangen regels in TestBellState zien er nu als volgt uit:

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

De resultaten worden nu interessanter:

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)

Elke meting vraagt om een klassieke waarde, maar de qubit ligt halverwege tussen 0 en 1, dus krijgen we (statistisch) de helft van de tijd en de helft van de tijd 1. Dit staat bekend als superpositie en geeft ons de eerste echte blik op een kwantumtoestand.

Verstrengeling voorbereiden

Laten we nu eens kijken hoe we Q# manieren uitdrukken om qubits te verstrengelen. Stel eerst de eerste qubit in op de begintoestand en gebruik vervolgens de bewerking om H deze in superpositie te zetten. Voordat u de eerste qubit meet, gebruikt u vervolgens een nieuwe bewerking ( ), die CNOT staat voor Controlled-NOT. Het resultaat van het uitvoeren van deze bewerking op twee qubits is het spiegelen van de tweede qubit als de eerste qubit One is. Nu zijn de twee qubits verstrengeld. De statistieken voor de eerste qubit zijn niet gewijzigd (kans van 50 tot 50 op een of na meting), maar wanneer nu de tweede qubit wordt gemeten, is deze altijd hetzelfde als wat we hebben gemeten voor de eerste Zero One qubit. Onze CNOT-poort heeft de twee qubits verstrengeld, zodat wat er met de ene gebeurt, ook met de andere gebeurt. Als u de metingen omkeert (de tweede qubit vóór de eerste), zou zich hetzelfde voordoen. De eerste meting zal willekeurig zijn, waarna de tweede zich houdt aan wat voor de eerste is gedetecteerd.

Als u verstrengeling wilt voorbereiden, moet u eerst twee qubits toewijzen in plaats van één in TestBellState :

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

Hierdoor kunnen we een nieuwe bewerking ( CNOT ) toevoegen voordat we ( ) in M TestBellState meten:

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

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

Voeg nog SetQubitState een bewerking toe om de eerste qubit te initialiseren om ervoor te zorgen dat deze altijd de status Zero heeft.

En ten slotte moet u de tweede qubit opnieuw instellen voordat u deze vrij geeft.

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

De volledige routine ziet er nu als volgt uit:

    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);
    }

Als u dit doet, krijgt u precies dezelfde 50-50 resultaten die we eerder hebben gehad. Het interessante is echter hoe de tweede qubit reageert op de eerste die wordt gemeten. Deze statistiek wordt toegevoegd met een nieuwe versie van de TestBellState bewerking:

    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);
    }

In de nieuwe retourwaarde (agree) wordt telkens bijgehouden als de meting van de eerste qubit overeenkomt met de meting van de tweede qubit.

Het uitvoeren van de code resulteert in:

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)

Zoals vermeld in het overzicht, zijn de statistieken voor de eerste qubit niet gewijzigd (kans van 50-50 op een 0 of een 1), maar bij het meten van de tweede qubit is deze altijd hetzelfde als voor de eerste qubit, omdat ze verstrengeld zijn!

Volgende stappen

In de zelfstudie zoekalgoritmen van Grover ziet u hoe u grover-zoekalgoritmen kunt bouwen en uitvoeren, een van de populairste algoritmen voor kwantumcomputing. Het biedt een goed voorbeeld van een programma dat kan worden gebruikt om echte problemen met kwantumcomputing op te Q# lossen.

Het instellen Azure Quantum meer manieren om te leren Q# en kwantumprogrammeren aan te bevelen.