From a153981617a6c8a2bb745a8d15176755d399af44 Mon Sep 17 00:00:00 2001 From: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:43:51 -0700 Subject: [PATCH] Add GHZ, Bit-Flip, and Phase-Flip Samples (#782) Adds GHZ, bit-flip, and phase-flip samples. --- samples/algorithms/BitFlipCode.qs | 136 ++++++++++++++++++++++++ samples/algorithms/GHZ.qs | 52 +++++++++ samples/algorithms/PhaseFlipCode.qs | 157 ++++++++++++++++++++++++++++ 3 files changed, 345 insertions(+) create mode 100644 samples/algorithms/BitFlipCode.qs create mode 100644 samples/algorithms/GHZ.qs create mode 100644 samples/algorithms/PhaseFlipCode.qs diff --git a/samples/algorithms/BitFlipCode.qs b/samples/algorithms/BitFlipCode.qs new file mode 100644 index 0000000000..19b2308661 --- /dev/null +++ b/samples/algorithms/BitFlipCode.qs @@ -0,0 +1,136 @@ +/// # Sample +/// Bit-Flip Code +/// +/// # Description +/// This sample demonstrates the three-qubit bit-flip code. This code is a +/// simple quantum error correction strategy for protecting against a single +/// bit-flip error by encoding a logical qubit into three physical qubits. A +/// single bit-flip error is when one of the three physical qubits has its +/// state changed erroneously in a way that is equivalent to applying the X +/// gate to it. +/// +/// The bit-flip correction code works by checking the parity of the physical +/// qubits. By measuring only their parity, the quantum superposition of the +/// qubits is preserved. Because all the physical qubits are supposed to have +/// the same state, when the parity checks detect a difference in state, the +/// erroneous qubit can be identified and corrected. +/// +/// This Q# program prepares a logical qubit encoded as three physical qubits +/// with one of the qubits being bit-flipped. It then identifies and corrects +/// the flipped qubit. +namespace Sample { + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Random; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Measurement; + + @EntryPoint() + operation Main() : Result { + use logicalQubit = Qubit[3]; + + // Set the initial state of the first physical qubit. + SetSampleState(logicalQubit[0]); + + // Using two additional qubits, encode the first physical qubit into a + // logical qubit. + EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]); + + // Induce a bit-flip error on a random qubit. + X(logicalQubit[DrawRandomInt(0, 2)]); + + // Show the logical qubit with the error state. + DumpMachine(); + + // Find and correct the bit-flip error. + CorrectError(logicalQubit); + + // Show the logical qubit with the corrected state. + DumpMachine(); + + // Decode the logical qubit back into a single physical qubit. + Adjoint EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]); + + // Measure and reset the physical qubit before releasing it. + let result = M(logicalQubit[0]); + Reset(logicalQubit[0]); + return result; + } + + /// # Summary + /// This operation sets the state of the given qubit such that + /// it will have a 20% likelihood of resulting in a `Zero` and + /// 80% likelihood of resulting in a `One` when measured in the + /// computational basis. The input qubit is expected to be in + /// the |0〉 state. + /// + /// # Input + /// ## q + /// The given qubit to be put into superposition. It is assumed that this + /// qubit is in its default |0〉 state. + operation SetSampleState(q : Qubit) : Unit { + let alpha = 0.20; + Ry(2.0 * ArcCos(Sqrt(alpha)), q); + } + + /// # Summary + /// This operation takes the given `physicalQubit` state, + /// (α|0〉 + β|1〉) / √2, and encodes it in the `aux` qubits. This + /// encodes all the qubits into a single logical qubit whose state reflects + /// the state of the given `physicalQubit`: (α|000〉 + β|111〉) / √2. + /// + /// # Input + /// ## physicalQubit + /// The qubit whose state, (α|0〉 + β|1〉) / √2, is to be encoded in the + /// logical qubit. + /// + /// ## aux + /// The auxiliary qubits that will be used as part of the encoding. These + /// should be grouped with the `physicalQubit` to form the logical qubit. + operation EncodeAsLogicalQubit(physicalQubit : Qubit, aux : Qubit[]) : Unit is Adj { + ApplyToEachA(CNOT(physicalQubit, _), aux); + } + + /// # Summary + /// This operation detects and corrects a single bit-flip error for a logical + /// qubit encoded as three physical qubits. When finished, the given register + /// of qubits will be in the state: (α|000〉 + β|111〉) / √2. + /// + /// # Input + /// ## logicalQubit + /// The given register of three physical qubits representing a single logical qubit + /// having superposition (α|0〉 + β|1〉) / √2. + /// This logical qubit can have up to one bit-flip error that will be corrected. + operation CorrectError(logicalQubit : Qubit[]) : Unit { + Fact(Length(logicalQubit) == 3, "`logicalQubit` must be length 3"); + + // Entangle the parity of the physical qubits into two auxillary qubits. + use aux = Qubit[2]; + CNOT(logicalQubit[0], aux[0]); + CNOT(logicalQubit[1], aux[0]); + CNOT(logicalQubit[1], aux[1]); + CNOT(logicalQubit[2], aux[1]); + + // Measure the parity information from the auxillary qubits. + let (parity01, parity12) = (M(aux[0]), M(aux[1])); + ResetAll(aux); + + // Determine which of the three qubits has the error based on the + // parity measurements. + let indexOfError = + if (parity01, parity12) == (One, Zero) { + 0 + } elif (parity01, parity12) == (One, One) { + 1 + } elif (parity01, parity12) == (Zero, One) { + 2 + } else { + -1 + }; + + // If an error was detected, correct that qubit. + if indexOfError > -1 { + X(logicalQubit[indexOfError]); + } + } +} diff --git a/samples/algorithms/GHZ.qs b/samples/algorithms/GHZ.qs new file mode 100644 index 0000000000..94c95e777f --- /dev/null +++ b/samples/algorithms/GHZ.qs @@ -0,0 +1,52 @@ +/// # Sample +/// GHZ +/// +/// # Description +/// The Greenberger–Horne–Zeilinger state, or GHZ state, is a state with 3 +/// qubits defined as: |GHZ〉 = (|000〉 + |111〉) / √2. +/// +/// The GHZ state is said to be a maximally entangled state, a multi-qubit +/// state where the state of any one qubit is not separable from the state +/// of any of the other qubits. +/// +/// The generalized form of the GHZ state across any number of qubits is +/// called a cat state, and the GHZ state is a special case of the cat +/// state where the number of qubits is 3. +/// +/// This Q# program prepares the GHZ state in a register of 3 qubits, then +/// returns the result of measuring those qubits. +namespace Sample { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Measurement; + + @EntryPoint() + operation Main() : Result[] { + use qs = Qubit[3]; + + // Prepare a GHZ state using the allocated register. + PrepareGHZState(qs); + + // Show the GHZ state. + DumpMachine(); + + // Measure and reset qubits before releasing them. + let results = MeasureEachZ(qs); + ResetAll(qs); + return results; + } + + /// # Summary + /// This operation prepares a generalized GHZ state across a register of qubits. + /// + /// # Input + /// ## qs + /// The given register of qubits to be transformed into the GHZ state. It is assumed + /// that these qubits are in their default |0〉 state. + operation PrepareGHZState(qs : Qubit[]) : Unit { + Fact(Length(qs) > 0, "`qs` length must be greater than zero"); + Fact(CheckAllZero(qs), "All qubits must be in the |0〉 state"); + + H(qs[0]); + ApplyToEach(CNOT(qs[0], _), qs[1...]); + } +} diff --git a/samples/algorithms/PhaseFlipCode.qs b/samples/algorithms/PhaseFlipCode.qs new file mode 100644 index 0000000000..b70ece981f --- /dev/null +++ b/samples/algorithms/PhaseFlipCode.qs @@ -0,0 +1,157 @@ +/// # Sample +/// Phase-Flip Code +/// +/// # Description +/// This sample demonstrates the three-qubit phase-flip code. This code is a +/// simple quantum error correction strategy for protecting against a single +/// phase-flip error by encoding a logical qubit into three physical qubits. A +/// single phase-flip error is when one of the three physical qubits has its +/// state changed erroneously in a way that is equivalent to applying the Z +/// gate to it. +/// +/// The phase-flip correction code works by checking the parity of the physical +/// qubits. By measuring only their parity, the quantum superposition of the +/// qubits is preserved. Because all the physical qubits are supposed to have +/// the same state, when the parity checks detect a difference in state, the +/// erroneous qubit can be identified and corrected. +/// +/// This Q# program prepares a logical qubit encoded as three physical qubits +/// with one of the qubits being phase-flipped. It then identifies and corrects +/// the flipped qubit. +namespace Sample { + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Random; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Measurement; + + @EntryPoint() + operation Main() : Result { + use logicalQubit = Qubit[3]; + + // Set the initial state of the first physical qubit. + SetSampleState(logicalQubit[0]); + + // Using two additional qubits, encode the first physical qubit into a + // logical qubit. + EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]); + + // Induce a phase-flip error on a random qubit. + Z(logicalQubit[DrawRandomInt(0, 2)]); + + // Show the logical qubit with the error state. + DumpMachine(); + + // Find and correct the phase-flip error. + CorrectError(logicalQubit); + + // Show the logical qubit with the corrected state. + DumpMachine(); + + // Decode the logical qubit back into a single physical qubit. + Adjoint EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]); + + // Measure and reset the physical qubit before releasing it. + let result = M(logicalQubit[0]); + Reset(logicalQubit[0]); + return result; + } + + /// # Summary + /// This operation sets the state of the given qubit such that + /// it will have a 20% likelihood of resulting in a `Zero` and + /// 80% likelihood of resulting in a `One` when measured in the + /// computational basis. The input qubit is expected to be in + /// the |0〉 state. + /// + /// # Input + /// ## q + /// The given qubit to be put into superposition. It is assumed that this + /// qubit is in its default |0〉 state. + operation SetSampleState(q : Qubit) : Unit { + let alpha = 0.20; + Ry(2.0 * ArcCos(Sqrt(alpha)), q); + } + + /// # Summary + /// This operation takes the given `physicalQubit` state, + /// (α|0〉 + β|1〉) / √2, and encodes it in the `aux` qubits. This + /// encodes all the qubits into a single logical qubit whose state reflects + /// the state of the given `physicalQubit`: (α|+++〉 + β|---〉) / √2. Note + /// that in this phase-flip example, the logical state |0〉 corresponds to + /// the physical state |+++〉, and the logical state |1〉 corresponds to the + /// physical state |---〉. + /// + /// # Input + /// ## physicalQubit + /// The qubit whose state, (α|0〉 + β|1〉) / √2, is to be encoded in the + /// logical qubit. + /// + /// ## aux + /// The auxiliary qubits that will be used as part of the encoding. These + /// should be grouped with the `physicalQubit` to form the logical qubit. + operation EncodeAsLogicalQubit(physicalQubit : Qubit, aux : Qubit[]) : Unit is Adj { + ApplyToEachA(CNOT(physicalQubit, _), aux); + + // We change the basis of the physical qubits so that the + // logical state |0〉 corresponds to the physical state |+++〉, + // and the logical state |1〉 corresponds to the physical state |---〉. + ChangeBasis([physicalQubit] + aux); + } + + /// # Summary + /// Changes the basis of the given qubits by applying a Hadamard operation + /// to each of them. + /// + /// # Input + /// ## qs + /// The given qubits to change the basis of. + operation ChangeBasis(qs : Qubit[]) : Unit is Adj { + ApplyToEachA(H, qs); + } + + /// # Summary + /// This operation detects and corrects a single phase-flip error for a logical + /// qubit encoded as three physical qubits. When finished, the given register + /// of qubits will be in the state: (α|000〉 + β|111〉) / √2. + /// + /// # Input + /// ## logicalQubit + /// The given register of three physical qubits representing a single logical qubit + /// having superposition (α|+++〉 + β|---〉) / √2. + /// This logical qubit can have up to one phase-flip error that will be corrected. + operation CorrectError(logicalQubit : Qubit[]) : Unit { + Fact(Length(logicalQubit) == 3, "`logicalQubit` must be length 3"); + + // Entangle the parity of the physical qubits into two auxillary qubits. + use aux = Qubit[2]; + ChangeBasis(logicalQubit); + CNOT(logicalQubit[0], aux[0]); + CNOT(logicalQubit[1], aux[0]); + CNOT(logicalQubit[1], aux[1]); + CNOT(logicalQubit[2], aux[1]); + ChangeBasis(logicalQubit); + + // Measure the parity information from the auxillary qubits. + let (parity01, parity12) = (M(aux[0]), M(aux[1])); + ResetAll(aux); + + // Determine which of the three qubits is has the error based on the + // parity measurements. + let indexOfError = + if (parity01, parity12) == (One, Zero) { + 0 + } elif (parity01, parity12) == (One, One) { + 1 + } elif (parity01, parity12) == (Zero, One) { + 2 + } else { + -1 + }; + + // If an error was detected, correct that qubit. + if indexOfError > -1 { + Z(logicalQubit[indexOfError]); + } + } +}