From 4165ad2d4842544c627b30f684d9e923be48f649 Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Wed, 8 Jan 2020 10:23:25 -0800 Subject: [PATCH] Onboarding bit-flip code to samples browser. (#294) * Onboarding bit-flip code to samples browser. * Very slight improvements to C# side. * Addressing feedback --- .../bit-flip-code/BitFlipCode.csproj | 1 + .../bit-flip-code/BitFlipCode.qs | 144 +++++++++--------- .../bit-flip-code/{Driver.cs => Host.cs} | 2 +- .../error-correction/bit-flip-code/README.md | 60 +++++++- 4 files changed, 126 insertions(+), 81 deletions(-) rename samples/error-correction/bit-flip-code/{Driver.cs => Host.cs} (96%) diff --git a/samples/error-correction/bit-flip-code/BitFlipCode.csproj b/samples/error-correction/bit-flip-code/BitFlipCode.csproj index b99d02881cd1..b1c3611ce958 100644 --- a/samples/error-correction/bit-flip-code/BitFlipCode.csproj +++ b/samples/error-correction/bit-flip-code/BitFlipCode.csproj @@ -4,6 +4,7 @@ Exe netcoreapp3.0 x64 + 8.0 diff --git a/samples/error-correction/bit-flip-code/BitFlipCode.qs b/samples/error-correction/bit-flip-code/BitFlipCode.qs index efc7a1c922a8..b2c0e6ed052c 100644 --- a/samples/error-correction/bit-flip-code/BitFlipCode.qs +++ b/samples/error-correction/bit-flip-code/BitFlipCode.qs @@ -1,60 +1,62 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. + namespace Microsoft.Quantum.Samples.BitFlipCode { open Microsoft.Quantum.ErrorCorrection; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Math; - - + open Microsoft.Quantum.Arrays; + + ////////////////////////////////////////////////////////////////////////// // Introduction ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// - + // In this sample, we build on the discussion in the quantum error // correction section of the developers' guide: - - // https://docs.microsoft.com/en-us/quantum/libraries/error-correction - + + // https://docs.microsoft.com/quantum/libraries/error-correction + // In particular, we start by manually encoding into the bit-flip code. // We then show how operations and functions provided in the Q# canon // allow us to easily model error correction in a way that immediately // generalizes to other codes. - + ////////////////////////////////////////////////////////////////////////// // The Bit-Flip Code ///////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// - + // The bit-flip code protects against any one bit-flip (X) error on three // qubits by mapping |0〉 to |̅0〉 ≔ |000〉 and |1〉 to |̅1〉 ≔ |111〉. By // linearity, any other state |ψ〉 = α|0〉 + β|1〉 is represented by the // logical state - + // |̅ψ〉 ≔ α |̅0〉 + β |̅1〉 // = α |000〉 + β |111〉. - + // We start by defining an operation which implements an encoder for // this code. To do so, note that CNOT allows us to "copy" classical // information in the bitstrings used to label computational basis // elements: - + // CNOT |b0〉 = |bb〉, - + // where b ∈ {0, 1}. This is not the same as copying the state, since // CNOT acts linearly: - + // CNOT (α |0〉 + β |1〉) ⊗ |0〉 = α |00〉 + β |11〉. - + // That is, consistent with the no-cloning theorem, CNOT did not // copy our arbitrary input state. On the other hand, this is // precisely the transformation that we want here: - + // CNOT₀₂ · CNOT₀₁ (α |0〉 + β |1〉) ⊗ |00〉 // = α |000〉 + β |111〉 // = α |̅0〉 + β |̅1〉. - + // Thus, we can write out our encoder in a very simple form: - + /// # Summary /// Given a qubit representing a state to be protected and two auxiliary /// qubits initially in the |0〉 state, encodes the state into the @@ -66,7 +68,7 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { /// ## auxiliaryQubits /// Two qubits, initially in the |00〉 state, to be used in protecting /// the state of `data`. - operation EncodeIntoBitFlipCode (data : Qubit, auxiliaryQubits : Qubit[]) : Unit + operation EncodeIntoBitFlipCode (data : Qubit, auxiliaryQubits : Qubit[]) : Unit // Since decoding is the adjoint of encoding, we must // denote that this operation supports the Adjoint // functor. @@ -83,7 +85,7 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { // each pair of qubits is positive (corresponding to the Zero) Result, // such that we can learn syndrome information without revealing // the state of an encoded qubit. - + /// # Summary /// This operation encodes into a bit-flip code, and confirms that /// the parity measurements Z₀Z₁ and Z₁Z₂ both return positive eigenvalues @@ -95,27 +97,27 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { /// if run on a target machine which supports assertions, and thus /// can be used as a unit test of error-correction functionality. operation CheckBitFlipCodeStateParity () : Unit { - + // We start by preparing R_x(π / 3) |0〉 as our // test state, along with two auxiliary qubits in the |00〉 // state that we can use to encode. using ((data, auxiliaryQubits) = (Qubit(), Qubit[2])) { let register = [data] + auxiliaryQubits; Rx(PI() / 3.0, data); - + // Next, we encode our test state. EncodeIntoBitFlipCode(data, auxiliaryQubits); - + // At this point, register represents a code block // that protects the state R_x(π / 3) |0〉. // We should thus be able to measure Z₀Z₁ and Z₁Z₂ // without disturbing the code state. // To check this, we proceed in two steps: - + // • Use Assert to ensure that the measurement // will return Zero. // • Use M to actually perform the measurement. - + // If our target machine is a simulator, the first step // will cause our quantum program to crash if the assertion // fails. Since an assertion is not a physical operation, @@ -125,13 +127,13 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { // further effect. Assert([PauliZ, PauliZ, PauliI], register, Zero, "Z₀Z₁ was One!"); Assert([PauliI, PauliZ, PauliZ], register, Zero, "Z₁Z₂ was One!"); - + // The second step then actually performs the measurement, // showing that we can make parity measurements without // disturbing the state that we care about. let parity01 = Measure([PauliZ, PauliZ, PauliI], register); let parity12 = Measure([PauliI, PauliZ, PauliZ], register); - + // To check that we have not disturbed the state, we decode, // rotate back, and assert once more. Adjoint EncodeIntoBitFlipCode(data, auxiliaryQubits); @@ -139,22 +141,22 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { Assert([PauliZ], [data], Zero, "Didn't return to |0〉!"); } } - - + + // Now that we're assured we can measure Z₀Z₁ and Z₁Z₂ without disturbing // the state of interest, let's use that to actually extract a syndrome // and recover from a bit-flip error. - + // Starting with the previous operation as a template, we'll remove // the assertions for the parity checks and allow for an error operation // to be passed as an input, then will modify it to use `parity01` and // `parity12` to perform the correction. - + // To take an error operation as an argument, we declare an input // of type (Qubit[] => ()), representing something that can happen // to an array of qubits. That is, we take the error to be applied // in a black-box sense. - + /// # Summary /// This operation encodes into a bit-flip code, and confirms that /// it can correct a given error applied to the logical state @@ -172,31 +174,31 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { operation CheckBitFlipCodeCorrectsError(error : (Qubit[] => Unit)) : Unit { using ((data, auxiliaryQubits) = (Qubit(), Qubit[2])) { let register = [data] + auxiliaryQubits; - + // We start by proceeding the same way as above // in order to obtain the code block state |̅ψ〉. Rx(PI() / 3.0, data); EncodeIntoBitFlipCode(data, auxiliaryQubits); - + // Next, we apply the error that we've been given to the // entire register. error(register); - + // We measure the two parities Z₀Z₁ and Z₁z₂ as before // to obtain our syndrome. let parity01 = Measure([PauliZ, PauliZ, PauliI], register); let parity12 = Measure([PauliI, PauliZ, PauliZ], register); - + // To use the syndrome obtained above, we recall the table // from : - + // Error | Z₀Z₁ | Z₁Z₂ // =================== // 1 | Zero | Zero // X₀ | One | Zero // X₁ | One | One // X₂ | Zero | One - + // Since the recovery is a classical inference procedure, we // can represent it here by using if/elif statements: if (parity01 == One and parity12 == Zero) { @@ -208,7 +210,7 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { elif (parity01 == Zero and parity12 == One) { X(register[2]); } - + // To check that we have not disturbed the state, we decode, // rotate back, and assert once more. Adjoint EncodeIntoBitFlipCode(data, auxiliaryQubits); @@ -216,34 +218,34 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { Assert([PauliZ], [data], Zero, "Didn't return to |0〉!"); } } - - + + // Now that we have defined an operation which fails if the bit-flip // code fails to protect a state from a given error, we can call it // with the specific errors that the bit-flip code can correct. // To do so, it is helpful to use the ApplyPauli operation from // the canon, which takes an array of Pauli values and applies the // corresponding sequence of operation. - + // For example, - + // ApplyPauli([PauliX, PauliY, PauliZ, PauliI], register); - + // is equivalent to - + // X(register[0]); // Y(register[1]); // Z(register[2]); - + // If we partially apply ApplyPauli, we get an operation that // represents applying a specific multi-qubit Pauli operator. // For instance, - + // ApplyPauli([PauliX, PauliI, PauliI], _) - + // is an operation of type (Qubit[] => ()) that represents // the X₀ bit-flip error. - + /// # Summary /// For each single-qubit bit-flip error on three qubits, this operation /// encodes R_x(π / 3) |0〉 into the bit-flip code and asserts that the @@ -259,15 +261,15 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { let X0 = ApplyPauli([PauliX, PauliI, PauliI], _); let X1 = ApplyPauli([PauliI, PauliX, PauliI], _); let X2 = ApplyPauli([PauliI, PauliI, PauliX], _); - + // For each of these errors, we can then check // that the bit flip code corrects them appropriately. CheckBitFlipCodeCorrectsError(X0); CheckBitFlipCodeCorrectsError(X1); CheckBitFlipCodeCorrectsError(X2); } - - + + // Finally, we show how the logic described in this sample can be // generalized by using functionality from the canon. This will allow // us to consider much more involved error-correcting codes using the @@ -275,13 +277,13 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { // To underscore this point, we write our new operation to take a QECC // value as its input, where QECC is a type provided by the canon to // collect all of the relevant information about an error-correcting code. - + // The canon separates the role of the classical recovery process from // the rest of an error-correcting code, allowing for recovery functions // which use prior information about error models to improve code // performance. Thus, we take a separate input of type RecoveryFn, a // canon type used to denote functions which fulfill this role. - + /// # Summary /// This operation encodes into an arbitrary code, and confirms that /// it can correct a given error applied to the logical state @@ -297,7 +299,7 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { /// if run on a target machine which supports assertions, and thus /// can be used as a unit test of error-correction functionality. operation CheckCodeCorrectsError(code : QECC, nScratch : Int, fn : RecoveryFn, error : (Qubit[] => Unit)) : Unit { - + // We once again begin by allocating some qubits to use as data // and auxiliary qubits, and by preparing a test state on the // data qubit. @@ -307,14 +309,14 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { let register = [data] + auxiliaryQubits; Rx(PI() / 3.0, data); - + // We differ this time, however, in how we perform the // encoding. The code input provided to this operation // specifies an encoder, a decoder, and a syndrome // measurement. Deconstructing that tuple will give us access // to all three operations. let (encode, decode, syndMeas) = code!; - + // We can now encode as usual, with the slight exception // that the encoder returns a value of a new user-defined type // that marks the register as encoding a state. @@ -324,31 +326,31 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { // Note that we also pass data as an array of qubits, to // allow for codes which protect multiple qubits in one block. let codeBlock = encode!([data], auxiliaryQubits); - + // Next, we cause an error as usual. error(codeBlock!); - + // We can then ask the canon to perform the recovery, using // our classical recovery procedure along with the code of // interest. Recover(code, fn, codeBlock); - + // Having recovered, we can decode to obtain new qubit arrays // pointing to the decoded data and auxiliary qubits. - let (decodedData, decodedauxiliary) = decode!(codeBlock); - + let (decodedData, decodedAuxiliary) = decode!(codeBlock); + // Finally, we test that our test state was protected. Adjoint Rx(PI() / 3.0, data); Assert([PauliZ], [data], Zero, "Didn't return to |0〉!"); } } - - + + // We will now write one last test that calls the new operation with // the BitFlipCode and BitFlipRecoveryFn provided by the canon. // Try replacing these with calls to other codes provided by the // canon! - + /// # Summary /// For each single-qubit bit-flip error on three qubits, this operation /// encodes R_x(π / 3) |0〉 into the bit-flip code and asserts that the @@ -358,20 +360,18 @@ namespace Microsoft.Quantum.Samples.BitFlipCode { /// This operation will fail when error correction fails /// if run on a target machine which supports assertions, and thus /// can be used as a unit test of error-correction functionality. - operation CheckCanonBitFlipCodeCorrectsBitFlipErrors () : Unit { + operation CheckCanonBitFlipCodeCorrectsBitFlipErrors() : Unit { let code = BitFlipCode(); let recoveryFn = BitFlipRecoveryFn(); let X0 = ApplyPauli([PauliX, PauliI, PauliI], _); let X1 = ApplyPauli([PauliI, PauliX, PauliI], _); let X2 = ApplyPauli([PauliI, PauliI, PauliX], _); - + // For each of these errors, we can then check // that the bit flip code corrects them appropriately. - let check = CheckCodeCorrectsError(code, 2, recoveryFn, _); - let errors = [X0, X1, X2]; - check(errors[0]); - check(errors[1]); - check(errors[2]); + for (error in [X0, X1, X2]) { + CheckCodeCorrectsError(code, 2, recoveryFn, error); + } } - + } diff --git a/samples/error-correction/bit-flip-code/Driver.cs b/samples/error-correction/bit-flip-code/Host.cs similarity index 96% rename from samples/error-correction/bit-flip-code/Driver.cs rename to samples/error-correction/bit-flip-code/Host.cs index e24241e6e7bd..dc849adc0841 100644 --- a/samples/error-correction/bit-flip-code/Driver.cs +++ b/samples/error-correction/bit-flip-code/Host.cs @@ -23,7 +23,7 @@ static void Main(string[] args) // We begin by defining a quantum simulator to be our target // machine. - var sim = new QuantumSimulator(throwOnReleasingQubitsNotInZeroState: true); + using var sim = new QuantumSimulator(throwOnReleasingQubitsNotInZeroState: true); #endregion diff --git a/samples/error-correction/bit-flip-code/README.md b/samples/error-correction/bit-flip-code/README.md index 23198224e1a9..445fe427bb98 100644 --- a/samples/error-correction/bit-flip-code/README.md +++ b/samples/error-correction/bit-flip-code/README.md @@ -1,14 +1,58 @@ -# Bit-flip Quantum Code Sample # +--- +page_type: sample +languages: +- qsharp +- python +- csharp +products: +- qdk +description: "This sample uses the Q# standard libraries to implement a three-qubit bit-flip quantum error correction code." +urlFragment: bit-flip-code +--- -This sample describes a simple quantum code that encodes 1 qubit into 3 qubits and protects against a single bit-flip error. -## Running the Sample ## +# Bit-flip Quantum Code Sample -Open the `QsharpSamples.sln` solution in Visual Studio and set the .csproj file in the manifest as the startup project. +This sample demonstrates: + +- Using the Q# standard libraries to implement a simple quantum error correction code. + +## Prerequisites + +- The Microsoft [Quantum Development Kit](https://docs.microsoft.com/quantum/install-guide/). + +## Running the Sample + +This sample can be run in a number of different ways, depending on your preferred environment. + +### Python in Visual Studio Code or the Command Line + +At a terminal, run the following command: + +```bash +python host.py +``` + +### C# in Visual Studio Code or the Command Line + +At a terminal, run the following command: + +```dotnetcli +dotnet run +``` + +### C# in Visual Studio 2019 + +Open the folder containing this sample in Visual Studio ("Open a local folder" from the Getting Started screen or "File → Open → Folder..." from the menu bar) and set `BitFlipCode.csproj` as the startup project. Press Start in Visual Studio to run the sample. -## Manifest ## +## Manifest + +- [BitFlipCode.qs](https://github.com/microsoft/Quantum/blob/master/samples/error-correction/bit-flip-code/BitFlipCode.qs): Q# code implementing quantum operations for this sample. +- [Host.cs](https://github.com/microsoft/Quantum/blob/master/samples/error-correction/bit-flip-code/Host.cs): C# code to interact with and print out results of the Q# operations for this sample. +- [host.py](https://github.com/microsoft/Quantum/blob/master/samples/error-correction/bit-flip-code/host.py): Python host program to call into the Q# sample. +- [BitFlipCode.csproj](https://github.com/microsoft/Quantum/blob/master/samples/error-correction/bit-flip-code/BitFlipCode.csproj): Main C# project for the sample. + +## Further resources -- [BitFlipCode.qs](./BitFlipCode.qs): Q# code implementing quantum operations for this sample. -- [Driver.cs](./Driver.cs): C# code to interact with and print out results of the Q# operations for this sample. -- [BitFlipCode.csproj](./BitFlipCode.csproj): Main C# project for the sample. +- [Error correction library concepts](https://docs.microsoft.com/quantum/libraries/standard/error-correction)