From 63e4e52a4821ff10311ece2ed536527cd8ea8c07 Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Tue, 30 Oct 2018 08:48:56 -0700 Subject: [PATCH] Migrate the katas to QDK 0.3 and .NET Core 2.1 (#43) --- BasicGates/BasicGates.csproj | 8 +- BasicGates/ReferenceImplementation.qs | 201 +++-- BasicGates/Tasks.qs | 214 ++--- BasicGates/Tests.qs | 340 ++++---- .../DeutschJozsaAlgorithm.csproj | 8 +- .../ReferenceImplementation.qs | 373 ++++---- DeutschJozsaAlgorithm/Tasks.qs | 348 ++++---- DeutschJozsaAlgorithm/Tests.qs | 551 ++++++------ GroversAlgorithm/GroversAlgorithm.csproj | 8 +- GroversAlgorithm/ReferenceImplementation.qs | 181 ++-- GroversAlgorithm/Tasks.qs | 186 ++-- GroversAlgorithm/Tests.qs | 173 ++-- JointMeasurements/JointMeasurements.csproj | 8 +- JointMeasurements/ReferenceImplementation.qs | 136 ++- JointMeasurements/Tasks.qs | 124 ++- JointMeasurements/Tests.qs | 283 +++--- Measurements/Measurements.csproj | 8 +- Measurements/ReferenceImplementation.qs | 507 ++++++----- Measurements/Tasks.qs | 232 ++--- Measurements/Tests.qs | 809 +++++++++--------- QEC_BitFlipCode/QEC_BitFlipCode.csproj | 8 +- QEC_BitFlipCode/ReferenceImplementation.qs | 103 +-- QEC_BitFlipCode/Tasks.qs | 104 ++- QEC_BitFlipCode/Tests.qs | 446 +++++----- SimonsAlgorithm/ReferenceImplementation.qs | 170 ++-- SimonsAlgorithm/SimonsAlgorithm.csproj | 8 +- SimonsAlgorithm/Tasks.qs | 129 +-- SimonsAlgorithm/Tests.qs | 304 +++---- SuperdenseCoding/ReferenceImplementation.qs | 183 ++-- SuperdenseCoding/SuperdenseCoding.csproj | 8 +- SuperdenseCoding/Tasks.qs | 90 +- SuperdenseCoding/Tests.qs | 152 ++-- Superposition/ReferenceImplementation.qs | 317 +++---- Superposition/Superposition.csproj | 8 +- Superposition/Tasks.qs | 202 ++--- Superposition/Tests.qs | 401 ++++----- Teleportation/ReferenceImplementation.qs | 236 ++--- Teleportation/Tasks.qs | 230 +++-- Teleportation/Teleportation.csproj | 8 +- Teleportation/Tests.qs | 496 +++++------ 40 files changed, 3935 insertions(+), 4366 deletions(-) diff --git a/BasicGates/BasicGates.csproj b/BasicGates/BasicGates.csproj index 1b4d8989..9442c705 100644 --- a/BasicGates/BasicGates.csproj +++ b/BasicGates/BasicGates.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.BasicGates - - - + + + diff --git a/BasicGates/ReferenceImplementation.qs b/BasicGates/ReferenceImplementation.qs index ccc30be1..9c37a5e5 100644 --- a/BasicGates/ReferenceImplementation.qs +++ b/BasicGates/ReferenceImplementation.qs @@ -1,37 +1,39 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.BasicGates -{ +namespace Quantum.Kata.BasicGates { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Part I. Single-Qubit Gates ////////////////////////////////////////////////////////////////// - + // Task 1.1. State flip // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the state of the qubit to α |1⟩ + β |0⟩. - // Example: - // If the qubit is in state |0⟩, change its state to |1⟩. - // If the qubit is in state |1⟩, change its state to |0⟩. - operation StateFlip_Reference (q : Qubit) : () - { - body - { + // Example: + // If the qubit is in state |0⟩, change its state to |1⟩. + // If the qubit is in state |1⟩, change its state to |0⟩. + operation StateFlip_Reference (q : Qubit) : Unit { + + body (...) { X(q); } + adjoint self; } - + + // Task 1.2. Basis change: |0⟩ to |+⟩ and |1⟩ to |-⟩ (and vice versa) // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the state of the qubit as follows: @@ -39,195 +41,206 @@ namespace Quantum.Kata.BasicGates // If the qubit is in state |1⟩, change its state to |-⟩ = (|0⟩ - |1⟩) / sqrt(2). // If the qubit is in superposition, change its state according to the effect on basis vectors. // Note: |+⟩ and |-⟩ form a different basis for single-qubit states, called X basis. - operation BasisChange_Reference (q : Qubit) : () - { - body - { + operation BasisChange_Reference (q : Qubit) : Unit { + + body (...) { H(q); } + adjoint self; } - + + // Task 1.3. Sign flip: |+⟩ to |-⟩ and vice versa. // Inputs: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the qubit state to α |0⟩ - β |1⟩ (flip the sign of |1⟩ component of the superposition). - operation SignFlip_Reference (q : Qubit) : () - { - body - { + operation SignFlip_Reference (q : Qubit) : Unit { + + body (...) { Z(q); } + adjoint self; } - + + // Task 1.4*. Amplitude change (|0⟩ to cos(alpha)*|0⟩ + sin(alpha)*|1⟩). - // Inputs: + // Inputs: // 1) A qubit in state β|0⟩ + γ|1⟩. // 2) Angle alpha, in radians, represented as Double // Goal: Change the state of the qubit as follows: // If the qubit is in state |0⟩, change its state to cos(alpha)*|0⟩ + sin(alpha)*|1⟩. // If the qubit is in state |1⟩, change its state to -sin(alpha)*|0⟩ + cos(alpha)*|1⟩. // If the qubit is in superposition, change its state according to the effect on basis vectors. - operation AmplitudeChange_Reference (q : Qubit, alpha : Double) : () - { - body - { + operation AmplitudeChange_Reference (q : Qubit, alpha : Double) : Unit { + + body (...) { Ry(2.0 * alpha, q); } - adjoint auto; + + adjoint invert; } - + + // Task 1.5. Phase flip // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the qubit state to α |0⟩ + iβ |1⟩ (flip the phase of |1⟩ component of the superposition). - operation PhaseFlip_Reference (q : Qubit) : () - { - body - { + operation PhaseFlip_Reference (q : Qubit) : Unit { + + body (...) { S(q); // alternatively Rz(0.5 * PI(), q); } - adjoint auto; + + adjoint invert; } - + + // Task 1.6*. Phase change - // Inputs: + // Inputs: // 1) A qubit in state β|0⟩ + γ|1⟩. // 2) Angle alpha, in radians, represented as Double // Goal: Change the state of the qubit as follows: // If the qubit is in state |0⟩, don't change its state. // If the qubit is in state |1⟩, change its state to exp(i*alpha)|1⟩. // If the qubit is in superposition, change its state according to the effect on basis vectors. - operation PhaseChange_Reference (q : Qubit, alpha : Double) : () - { - body - { + operation PhaseChange_Reference (q : Qubit, alpha : Double) : Unit { + + body (...) { Rz(alpha, q); } - adjoint auto; + + adjoint invert; } - + + // Task 1.7. Bell state change - 1 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2). - operation BellStateChange1_Reference (qs : Qubit[]) : () - { - body - { + operation BellStateChange1_Reference (qs : Qubit[]) : Unit { + + body (...) { Z(qs[0]); // alternatively Z(qs[1]); } - adjoint auto; + + adjoint invert; } - + + // Task 1.8. Bell state change - 2 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2). - operation BellStateChange2_Reference (qs : Qubit[]) : () - { - body - { + operation BellStateChange2_Reference (qs : Qubit[]) : Unit { + + body (...) { X(qs[0]); // alternatively X(qs[1]); } - adjoint auto; + + adjoint invert; } - + + // Task 1.9. Bell state change - 3 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2). - operation BellStateChange3_Reference (qs : Qubit[]) : () - { - body - { + operation BellStateChange3_Reference (qs : Qubit[]) : Unit { + + body (...) { Y(qs[0]); } - adjoint auto; + + adjoint invert; } - - + + ////////////////////////////////////////////////////////////////// // Part II. Multi-Qubit Gates ////////////////////////////////////////////////////////////////// - + // Task 2.1. Two-qubit gate - 1 // Input: Two unentangled qubits (stored in an array of length 2). // The first qubit will be in state |ψ⟩ = α |0⟩ + β |1⟩, the second - in state |0⟩ // (this can be written as two-qubit state (α|0⟩ + β|1⟩) ⊕ |0⟩). // Goal: Change the two-qubit state to α |00⟩ + β |11⟩. // Note that unless the starting state of the first qubit was |0⟩ or |1⟩, - // the resulting two-qubit state can not be represented as a tensor product + // the resulting two-qubit state can not be represented as a tensor product // of the states of individual qubits any longer; thus the qubits become entangled. - operation TwoQubitGate1_Reference (qs : Qubit[]) : () - { - body - { + operation TwoQubitGate1_Reference (qs : Qubit[]) : Unit { + + body (...) { CNOT(qs[0], qs[1]); } + adjoint self; } - + + // Task 2.2. Two-qubit gate - 2 // Input: Two qubits (stored in an array of length 2) // in state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. // Goal: Change the two-qubit state to (|00⟩ + |01⟩ + |10⟩ - |11⟩) / 2. // Note that while the starting state can be represented as a tensor product of single-qubit states, // the resulting two-qubit state can not be represented in such a way. - operation TwoQubitGate2_Reference (qs : Qubit[]) : () - { - body - { - (Controlled Z)([qs[0]], qs[1]); + operation TwoQubitGate2_Reference (qs : Qubit[]) : Unit { + + body (...) { + Controlled Z([qs[0]], qs[1]); } + adjoint self; } - + + // Task 2.3. Two-qubit gate - 3 - // Input: Two qubits (stored in an array of length 2) in an arbitrary + // Input: Two qubits (stored in an array of length 2) in an arbitrary // two-qubit state α|00⟩ + β|01⟩ + γ|10⟩ + δ|11⟩. // Goal: Change the two-qubit state to α|00⟩ + γ|01⟩ + β|10⟩ + δ|11⟩. - operation TwoQubitGate3_Reference (qs : Qubit[]) : () - { - body - { + operation TwoQubitGate3_Reference (qs : Qubit[]) : Unit { + + body (...) { // Hint: this task can be solved using one primitive gate; // as an exercise, try to express the solution using several controlled Pauli gates. - CNOT(qs[0], qs[1]); CNOT(qs[1], qs[0]); CNOT(qs[0], qs[1]); } + adjoint self; } - + + // Task 2.4. Toffoli gate // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. // Goal: Flip the state of the third qubit if the state of the first two is |11⟩: // i.e., change the three-qubit state to // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + θ|110⟩ + η|111⟩. - operation ToffoliGate_Reference (qs : Qubit[]) : () - { - body - { + operation ToffoliGate_Reference (qs : Qubit[]) : Unit { + + body (...) { CCNOT(qs[0], qs[1], qs[2]); // alternatively (Controlled X)(qs[0..1], qs[2]); } + adjoint self; } - + + // Task 2.5. Fredkin gate // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. // Goal: Swap the states of second and third qubit if and only if the state of the first qubit is |1⟩: - // i.e., change the three-qubit state to + // i.e., change the three-qubit state to // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + η|101⟩ + ζ|110⟩ + θ|111⟩. - operation FredkinGate_Reference (qs : Qubit[]) : () - { - body - { - (Controlled SWAP)([qs[0]], (qs[1], qs[2])); + operation FredkinGate_Reference (qs : Qubit[]) : Unit { + + body (...) { + Controlled SWAP([qs[0]], (qs[1], qs[2])); } + adjoint self; } + } diff --git a/BasicGates/Tasks.qs b/BasicGates/Tasks.qs index ccb979c5..756f57a2 100644 --- a/BasicGates/Tasks.qs +++ b/BasicGates/Tasks.qs @@ -1,58 +1,60 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.BasicGates -{ +namespace Quantum.Kata.BasicGates { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Basic Gates" quantum kata is a series of exercises designed + + // "Basic Gates" quantum kata is a series of exercises designed // to get you familiar with the basic quantum gates in Q#. // It covers the following topics: // - basic single-qubit and multi-qubit gates, // - adjoint and controlled gates, // - using gates to modify the state of a qubit. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // + // Most tasks can be done using exactly one gate. // None of the tasks require measurement, and the tests are written so as to fail if qubit state is measured. - // - // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. - - + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + + ////////////////////////////////////////////////////////////////// // Part I. Single-Qubit Gates ////////////////////////////////////////////////////////////////// - + // Task 1.1. State flip: |0⟩ to |1⟩ and vice versa // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the state of the qubit to α |1⟩ + β |0⟩. - // Example: - // If the qubit is in state |0⟩, change its state to |1⟩. - // If the qubit is in state |1⟩, change its state to |0⟩. - // Note that this operation is self-adjoint: applying it for a second time + // Example: + // If the qubit is in state |0⟩, change its state to |1⟩. + // If the qubit is in state |1⟩, change its state to |0⟩. + // Note that this operation is self-adjoint: applying it for a second time // returns the qubit to the original state. - operation StateFlip (q : Qubit) : () - { - body - { + operation StateFlip (q : Qubit) : Unit { + + body (...) { // The Pauli X gate will change the |0⟩ state to the |1⟩ state and vice versa. // Type X(q); // Then rebuild the project and rerun the tests - T11_StateFlip_Test should now pass! // ... } + adjoint self; } - + + // Task 1.2. Basis change: |0⟩ to |+⟩ and |1⟩ to |-⟩ (and vice versa) // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the state of the qubit as follows: @@ -61,189 +63,201 @@ namespace Quantum.Kata.BasicGates // If the qubit is in superposition, change its state according to the effect on basis vectors. // Note: |+⟩ and |-⟩ form a different basis for single-qubit states, called X basis. // |0⟩ and |1⟩ are called Z basis. - operation BasisChange (q : Qubit) : () - { - body - { + operation BasisChange (q : Qubit) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 1.3. Sign flip: |+⟩ to |-⟩ and vice versa. // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the qubit state to α |0⟩ - β |1⟩ (flip the sign of |1⟩ component of the superposition). - operation SignFlip (q : Qubit) : () - { - body - { + operation SignFlip (q : Qubit) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 1.4*. Amplitude change: |0⟩ to cos(alpha)*|0⟩ + sin(alpha)*|1⟩. - // Inputs: + // Inputs: // 1) A qubit in state β|0⟩ + γ|1⟩. // 2) Angle alpha, in radians, represented as Double // Goal: Change the state of the qubit as follows: // If the qubit is in state |0⟩, change its state to cos(alpha)*|0⟩ + sin(alpha)*|1⟩. // If the qubit is in state |1⟩, change its state to -sin(alpha)*|0⟩ + cos(alpha)*|1⟩. // If the qubit is in superposition, change its state according to the effect on basis vectors. - operation AmplitudeChange (q : Qubit, alpha : Double) : () - { - body - { + operation AmplitudeChange (q : Qubit, alpha : Double) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.5. Phase flip // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. // Goal: Change the qubit state to α |0⟩ + iβ |1⟩ (flip the phase of |1⟩ component of the superposition). - operation PhaseFlip (q : Qubit) : () - { - body - { + operation PhaseFlip (q : Qubit) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.6*. Phase change - // Inputs: + // Inputs: // 1) A qubit in state β|0⟩ + γ|1⟩. // 2) Angle alpha, in radians, represented as Double // Goal: Change the state of the qubit as follows: // If the qubit is in state |0⟩, don't change its state. // If the qubit is in state |1⟩, change its state to exp(i*alpha)|1⟩. // If the qubit is in superposition, change its state according to the effect on basis vectors. - operation PhaseChange (q : Qubit, alpha : Double) : () - { - body - { + operation PhaseChange (q : Qubit, alpha : Double) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.7. Bell state change - 1 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2). - operation BellStateChange1 (qs : Qubit[]) : () - { - body - { + operation BellStateChange1 (qs : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.8. Bell state change - 2 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2). - operation BellStateChange2 (qs : Qubit[]) : () - { - body - { + operation BellStateChange2 (qs : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.9. Bell state change - 3 // Input: Two entangled qubits in Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). // Goal: Change the two-qubit state to |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2). - operation BellStateChange3 (qs : Qubit[]) : () - { - body - { + operation BellStateChange3 (qs : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - - + + ////////////////////////////////////////////////////////////////// // Part II. Multi-Qubit Gates ////////////////////////////////////////////////////////////////// - + // Task 2.1. Two-qubit gate - 1 // Input: Two unentangled qubits (stored in an array of length 2). // The first qubit will be in state |ψ⟩ = α |0⟩ + β |1⟩, the second - in state |0⟩ // (this can be written as two-qubit state (α|0⟩ + β|1⟩) ⊕ |0⟩). // Goal: Change the two-qubit state to α |00⟩ + β |11⟩. // Note that unless the starting state of the first qubit was |0⟩ or |1⟩, - // the resulting two-qubit state can not be represented as a tensor product + // the resulting two-qubit state can not be represented as a tensor product // of the states of individual qubits any longer; thus the qubits become entangled. - operation TwoQubitGate1 (qs : Qubit[]) : () - { - body - { + operation TwoQubitGate1 (qs : Qubit[]) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 2.2. Two-qubit gate - 2 // Input: Two qubits (stored in an array of length 2) // in state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. // Goal: Change the two-qubit state to (|00⟩ + |01⟩ + |10⟩ - |11⟩) / 2. // Note that while the starting state can be represented as a tensor product of single-qubit states, // the resulting two-qubit state can not be represented in such a way. - operation TwoQubitGate2 (qs : Qubit[]) : () - { - body - { + operation TwoQubitGate2 (qs : Qubit[]) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 2.3. Two-qubit gate - 3 - // Input: Two qubits (stored in an array of length 2) in an arbitrary + // Input: Two qubits (stored in an array of length 2) in an arbitrary // two-qubit state α|00⟩ + β|01⟩ + γ|10⟩ + δ|11⟩. // Goal: Change the two-qubit state to α|00⟩ + γ|01⟩ + β|10⟩ + δ|11⟩. - operation TwoQubitGate3 (qs : Qubit[]) : () - { - body - { + operation TwoQubitGate3 (qs : Qubit[]) : Unit { + + body (...) { // Hint: this task can be solved using one primitive gate; - // as an exercise, try to express the solution using several + // as an exercise, try to express the solution using several // (possibly controlled) Pauli gates. - + // ... } + adjoint self; } - + + // Task 2.4. Toffoli gate // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. // Goal: Flip the state of the third qubit if the state of the first two is |11⟩: // i.e., change the three-qubit state to // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + θ|110⟩ + η|111⟩. - operation ToffoliGate (qs : Qubit[]) : () - { - body - { + operation ToffoliGate (qs : Qubit[]) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 2.5. Fredkin gate // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. // Goal: Swap the states of second and third qubit if and only if the state of the first qubit is |1⟩: // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + η|101⟩ + ζ|110⟩ + θ|111⟩. - operation FredkinGate (qs : Qubit[]) : () - { - body - { + operation FredkinGate (qs : Qubit[]) : Unit { + + body (...) { // ... } + adjoint self; } + } diff --git a/BasicGates/Tests.qs b/BasicGates/Tests.qs index 0d9994bf..a5fd2787 100644 --- a/BasicGates/Tests.qs +++ b/BasicGates/Tests.qs @@ -1,268 +1,234 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.BasicGates -{ +namespace Quantum.Kata.BasicGates { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ // helper wrapper to represent operation on one qubit as an operation on an array of qubits - operation ArrayWrapperOperation (op : ((Qubit) => () : Adjoint), qs : Qubit[]) : () - { - body - { + operation ArrayWrapperOperation (op : (Qubit => Unit : Adjoint), qs : Qubit[]) : Unit { + + body (...) { op(qs[0]); } - adjoint - { - (Adjoint op)(qs[0]); + + adjoint (...) { + Adjoint op(qs[0]); } } - + + // ------------------------------------------------------ - operation T11_StateFlip_Test () : () - { - body - { - AssertOperationsEqualReferenced(ArrayWrapperOperation(StateFlip, _), ArrayWrapperOperation(StateFlip_Reference, _), 1); - } + operation T11_StateFlip_Test () : Unit { + AssertOperationsEqualReferenced(ArrayWrapperOperation(StateFlip, _), ArrayWrapperOperation(StateFlip_Reference, _), 1); } - + + // ------------------------------------------------------ - operation T12_BasisChange_Test () : () - { - body - { - AssertOperationsEqualReferenced(ArrayWrapperOperation(BasisChange, _), ArrayWrapperOperation(BasisChange_Reference, _), 1); - } + operation T12_BasisChange_Test () : Unit { + AssertOperationsEqualReferenced(ArrayWrapperOperation(BasisChange, _), ArrayWrapperOperation(BasisChange_Reference, _), 1); } - + + // ------------------------------------------------------ - operation T13_SignFlip_Test () : () - { - body - { - AssertOperationsEqualReferenced(ArrayWrapperOperation(SignFlip, _), ArrayWrapperOperation(SignFlip_Reference, _), 1); - } + operation T13_SignFlip_Test () : Unit { + AssertOperationsEqualReferenced(ArrayWrapperOperation(SignFlip, _), ArrayWrapperOperation(SignFlip_Reference, _), 1); } - + + // ------------------------------------------------------ - operation T14_AmplitudeChange_Test () : () - { - body - { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - AssertOperationsEqualReferenced(ArrayWrapperOperation(AmplitudeChange(_, alpha), _), ArrayWrapperOperation(AmplitudeChange_Reference(_, alpha), _), 1); - } + operation T14_AmplitudeChange_Test () : Unit { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + AssertOperationsEqualReferenced(ArrayWrapperOperation(AmplitudeChange(_, alpha), _), ArrayWrapperOperation(AmplitudeChange_Reference(_, alpha), _), 1); } } - + + // ------------------------------------------------------ - operation T15_PhaseFlip_Test () : () - { - body - { - AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseFlip, _), ArrayWrapperOperation(PhaseFlip_Reference, _), 1); - } + operation T15_PhaseFlip_Test () : Unit { + AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseFlip, _), ArrayWrapperOperation(PhaseFlip_Reference, _), 1); } - + + // ------------------------------------------------------ - operation T16_PhaseChange_Test () : () - { - body - { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseChange(_, alpha), _), ArrayWrapperOperation(PhaseChange_Reference(_, alpha), _), 1); - } + operation T16_PhaseChange_Test () : Unit { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseChange(_, alpha), _), ArrayWrapperOperation(PhaseChange_Reference(_, alpha), _), 1); } } - + + // ------------------------------------------------------ // 0 - |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) // 1 - |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2 - |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3 - |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation StatePrep_BellState (qs : Qubit[], state : Int) : () { - body { + operation StatePrep_BellState (qs : Qubit[], state : Int) : Unit { + + body (...) { H(qs[0]); CNOT(qs[0], qs[1]); + // now we have |00⟩ + |11⟩ - modify it based on state arg if (state % 2 == 1) { // negative phase Z(qs[1]); } + if (state / 2 == 1) { X(qs[1]); } } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ - operation VerifyBellStateConversion( - testOp : ((Qubit[]) => ()), - startState : Int, - targetState : Int) : () { - body { - using (qs = Qubit[2]) - { - // prepare Bell state startState - StatePrep_BellState(qs, startState); - - // apply operation that needs to be tested - testOp(qs); - - // verify the result by applying adjoint of state prep for target state - (Adjoint StatePrep_BellState)(qs, targetState); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + operation VerifyBellStateConversion (testOp : (Qubit[] => Unit), startState : Int, targetState : Int) : Unit { + using (qs = Qubit[2]) { + // prepare Bell state startState + StatePrep_BellState(qs, startState); + + // apply operation that needs to be tested + testOp(qs); + + // verify the result by applying adjoint of state prep for target state + Adjoint StatePrep_BellState(qs, targetState); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } + + // ------------------------------------------------------ - operation T17_BellStateChange1_Test () : () - { - body - { - VerifyBellStateConversion(BellStateChange1, 0, 1); - } + operation T17_BellStateChange1_Test () : Unit { + VerifyBellStateConversion(BellStateChange1, 0, 1); } - + + // ------------------------------------------------------ - operation T18_BellStateChange2_Test () : () - { - body - { - VerifyBellStateConversion(BellStateChange2, 0, 2); - } + operation T18_BellStateChange2_Test () : Unit { + VerifyBellStateConversion(BellStateChange2, 0, 2); } - + + // ------------------------------------------------------ - operation T19_BellStateChange3_Test () : () - { - body - { - VerifyBellStateConversion(BellStateChange3, 0, 3); - } + operation T19_BellStateChange3_Test () : Unit { + VerifyBellStateConversion(BellStateChange3, 0, 3); } - + + // ------------------------------------------------------ // prepare state |A⟩ = cos(α) * |0⟩ + sin(α) * |1⟩ - operation StatePrep_A (alpha : Double, q : Qubit) : () { - body { + operation StatePrep_A (alpha : Double, q : Qubit) : Unit { + + body (...) { Ry(2.0 * alpha, q); } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ - operation T21_TwoQubitGate1_Test () : () - { - body - { - // Note that the way the problem is formulated, we can't just compare two unitaries, - // we need to create an input state |A⟩ and check that the output state is correct - using (qs = Qubit[2]) - { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - - // prepare A state - StatePrep_A(alpha, qs[0]); - - // apply operation that needs to be tested - TwoQubitGate1(qs); - - // apply adjoint reference operation and adjoint of state prep - (Adjoint TwoQubitGate1_Reference)(qs); - (Adjoint StatePrep_A)(alpha, qs[0]); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } - } - } - } - - // ------------------------------------------------------ - // prepare state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. - operation StatePrep_PlusPlus (qs : Qubit[]) : () { - body { - ApplyToEachA(H, qs); - } - adjoint auto; - } - - // ------------------------------------------------------ - operation T22_TwoQubitGate2_Test () : () - { - body - { - using (qs = Qubit[2]) - { - // prepare |+⟩ ⊕ |+⟩ state - StatePrep_PlusPlus(qs); - + operation T21_TwoQubitGate1_Test () : Unit { + + // Note that the way the problem is formulated, we can't just compare two unitaries, + // we need to create an input state |A⟩ and check that the output state is correct + using (qs = Qubit[2]) { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + + // prepare A state + StatePrep_A(alpha, qs[0]); + // apply operation that needs to be tested - TwoQubitGate2(qs); - + TwoQubitGate1(qs); + // apply adjoint reference operation and adjoint of state prep - (Adjoint TwoQubitGate2_Reference)(qs); - (Adjoint StatePrep_PlusPlus)(qs); - + Adjoint TwoQubitGate1_Reference(qs); + Adjoint StatePrep_A(alpha, qs[0]); + // assert that all qubits end up in |0⟩ state AssertAllZero(qs); } } } - + + // ------------------------------------------------------ - operation SwapWrapper (qs : Qubit[]) : () - { - body - { + // prepare state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. + operation StatePrep_PlusPlus (qs : Qubit[]) : Unit { + + body (...) { + ApplyToEachA(H, qs); + } + + adjoint invert; + } + + + // ------------------------------------------------------ + operation T22_TwoQubitGate2_Test () : Unit { + using (qs = Qubit[2]) { + // prepare |+⟩ ⊕ |+⟩ state + StatePrep_PlusPlus(qs); + + // apply operation that needs to be tested + TwoQubitGate2(qs); + + // apply adjoint reference operation and adjoint of state prep + Adjoint TwoQubitGate2_Reference(qs); + Adjoint StatePrep_PlusPlus(qs); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); + } + } + + + // ------------------------------------------------------ + operation SwapWrapper (qs : Qubit[]) : Unit { + + body (...) { SWAP(qs[0], qs[1]); } + adjoint self; } - - operation T23_TwoQubitGate3_Test () : () - { - body - { - AssertOperationsEqualReferenced(SwapWrapper, TwoQubitGate3_Reference, 2); - AssertOperationsEqualReferenced(TwoQubitGate3, TwoQubitGate3_Reference, 2); - } + + + operation T23_TwoQubitGate3_Test () : Unit { + AssertOperationsEqualReferenced(SwapWrapper, TwoQubitGate3_Reference, 2); + AssertOperationsEqualReferenced(TwoQubitGate3, TwoQubitGate3_Reference, 2); } - + + // ------------------------------------------------------ - operation T24_ToffoliGate_Test () : () - { - body - { - AssertOperationsEqualReferenced(ToffoliGate, ToffoliGate_Reference, 3); - } + operation T24_ToffoliGate_Test () : Unit { + AssertOperationsEqualReferenced(ToffoliGate, ToffoliGate_Reference, 3); } - + + // ------------------------------------------------------ - operation T25_FredkinGate_Test () : () - { - body - { - AssertOperationsEqualReferenced(FredkinGate, FredkinGate_Reference, 3); - } + operation T25_FredkinGate_Test () : Unit { + AssertOperationsEqualReferenced(FredkinGate, FredkinGate_Reference, 3); } -} \ No newline at end of file + +} diff --git a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj index 0977f4c8..da03c611 100644 --- a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj +++ b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.DeutschJozsaAlgorithm - - - + + + diff --git a/DeutschJozsaAlgorithm/ReferenceImplementation.qs b/DeutschJozsaAlgorithm/ReferenceImplementation.qs index 3d78821b..fb82cf66 100644 --- a/DeutschJozsaAlgorithm/ReferenceImplementation.qs +++ b/DeutschJozsaAlgorithm/ReferenceImplementation.qs @@ -1,130 +1,135 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.DeutschJozsaAlgorithm -{ +namespace Quantum.Kata.DeutschJozsaAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Part I. Oracles ////////////////////////////////////////////////////////////////// - + // Task 1.1. f(x) = 0 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_Zero_Reference (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_Zero_Reference (x : Qubit[], y : Qubit) : Unit { + + body (...) { // Since f(x) = 0 for all values of x, |y ⊕ f(x)⟩ = |y⟩. // This means that the operation doesn't need to do any transformation to the inputs. // Build the project and run the tests to see that T01_Oracle_Zero_Test test passes. } - adjoint auto; + + adjoint invert; } - + + // Task 1.2. f(x) = 1 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_One_Reference (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_One_Reference (x : Qubit[], y : Qubit) : Unit { + + body (...) { // Since f(x) = 1 for all values of x, |y ⊕ f(x)⟩ = |y ⊕ 1⟩ = |NOT y⟩. // This means that the operation needs to flip qubit y (i.e. transform |0⟩ to |1⟩ and vice versa). X(y); } - adjoint auto; + + adjoint invert; } - + + // Task 1.3. f(x) = xₖ (the value of k-th qubit) - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) 0-based index of the qubit from input register (0 <= k < N) // Goal: transform state |x, y⟩ into state |x, y ⊕ xₖ⟩ (⊕ is addition modulo 2). - operation Oracle_Kth_Qubit_Reference (x : Qubit[], y : Qubit, k : Int) : () - { - body - { + operation Oracle_Kth_Qubit_Reference (x : Qubit[], y : Qubit, k : Int) : Unit { + + body (...) { AssertBoolEqual(0 <= k && k < Length(x), true, "k should be between 0 and N-1, inclusive"); CNOT(x[k], y); } - adjoint auto; + + adjoint invert; } - + + // Task 1.4. f(x) = 1 if x has odd number of 1s, and 0 otherwise - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_OddNumberOfOnes_Reference (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_OddNumberOfOnes_Reference (x : Qubit[], y : Qubit) : Unit { + + body (...) { // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) - for (i in 0..Length(x)-1) { + for (i in 0 .. Length(x) - 1) { CNOT(x[i], y); } // alternative solution: ApplyToEachA(CNOT(_, y), x); } - adjoint auto; + + adjoint invert; } - + + // Task 1.5. f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r (scalar product function) - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length N represented as Int[] // You are guaranteed that the qubit array and the bit vector have the same length. // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - // + // Note: the functions featured in tasks 1.1, 1.3 and 1.4 are special cases of this function. - operation Oracle_ProductFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : () - { - body - { + operation Oracle_ProductFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : Unit { + + body (...) { // The following line enforces the constraint on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); - - for (i in 0..Length(x)-1) { + + for (i in 0 .. Length(x) - 1) { if (r[i] == 1) { CNOT(x[i], y); } } } - adjoint auto; + + adjoint invert; } - + + // Task 1.6. f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for a given bit vector r - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length N represented as Int[] // You are guaranteed that the qubit array and the bit vector have the same length. // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_ProductWithNegationFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : () - { - body - { + operation Oracle_ProductWithNegationFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : Unit { + + body (...) { // The following line enforces the constraint on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); - - for (i in 0..Length(x)-1) { + + for (i in 0 .. Length(x) - 1) { if (r[i] == 1) { CNOT(x[i], y); } else { @@ -135,59 +140,65 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm } } } - adjoint auto; + + adjoint invert; } - + + // Task 1.7. f(x) = Σᵢ 𝑥ᵢ + (1 if prefix of x is equal to the given bit vector, and 0 otherwise) modulo 2 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length P represented as Int[] (1 <= P <= N) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - // + // A prefix of length k of a state |x⟩ = |x₁, ..., xₙ⟩ is the state of its first k qubits |x₁, ..., xₖ⟩. // For example, a prefix of length 2 of a state |0110⟩ is 01. - operation Oracle_HammingWithPrefix_Reference (x : Qubit[], y : Qubit, prefix : Int[]) : () - { - body - { + operation Oracle_HammingWithPrefix_Reference (x : Qubit[], y : Qubit, prefix : Int[]) : Unit { + + body (...) { // The following line enforces the constraint on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. let P = Length(prefix); AssertBoolEqual(1 <= P && P <= Length(x), true, "P should be between 1 and N, inclusive"); - + // Hint: the first part of the function is the same as in task 1.4 - for (i in 0..Length(x)-1) { + for (i in 0 .. Length(x) - 1) { CNOT(x[i], y); } - + // add check for prefix as a multicontrolled NOT // true bits of r correspond to 1-controls, false bits - to 0-controls - for (i in 0..P-1) { + for (i in 0 .. P - 1) { + if (prefix[i] == 0) { X(x[i]); } } - (Controlled X)(x[0..P-1], y); + + Controlled X(x[0 .. P - 1], y); + // uncompute changes done to input register - for (i in 0..P-1) { + for (i in 0 .. P - 1) { + if (prefix[i] == 0) { X(x[i]); } } } - adjoint auto; + + adjoint invert; } - + + // Task 1.8*. f(x) = 1 if x has two or three bits (out of three) set to 1, and 0 otherwise (majority function) - // Inputs: + // Inputs: // 1) 3 qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_MajorityFunction_Reference (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_MajorityFunction_Reference (x : Qubit[], y : Qubit) : Unit { + + body (...) { // The following line enforces the constraint on the input array. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertBoolEqual(3 == Length(x), true, "x should have exactly 3 qubits"); @@ -197,14 +208,15 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm CCNOT(x[0], x[2], y); CCNOT(x[1], x[2], y); } - adjoint auto; + + adjoint invert; } - - + + ////////////////////////////////////////////////////////////////// // Part II. Bernstein-Vazirani Algorithm ////////////////////////////////////////////////////////////////// - + // Task 2.1. State preparation for Bernstein-Vazirani algorithm // Inputs: // 1) N qubits in |0⟩ state (query register) @@ -213,17 +225,18 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // 1) create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ on query register // (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ) // 2) create |-⟩ state (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)) on answer register - operation BV_StatePrep_Reference (query : Qubit[], answer : Qubit) : () - { - body - { + operation BV_StatePrep_Reference (query : Qubit[], answer : Qubit) : Unit { + + body (...) { ApplyToEachA(H, query); X(answer); H(answer); } - adjoint auto; + + adjoint invert; } - + + // Task 2.2. Bernstein-Vazirani algorithm implementation // Inputs: // 1) the number of qubits in the input register N for the function f @@ -234,94 +247,91 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // You have implemented the oracle implementing the scalar product function in task 1.5. // Output: // A bit vector r reconstructed from the function - // - // Note: a trivial approach is to call the oracle N times: + + // Note: a trivial approach is to call the oracle N times: // |10...0⟩|0⟩ = |10...0⟩|r₀⟩, |010...0⟩|0⟩ = |010...0⟩|r₁⟩ and so on. // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. - operation BV_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] - { - body - { - mutable r = new Int[N]; - - // allocate N+1 qubits - using (qs = Qubit[N+1]) { - // split allocated qubits into input register and answer register - let x = qs[0..N-1]; - let y = qs[N]; - - // prepare qubits in the right state - BV_StatePrep_Reference(x, y); - - // apply oracle - Uf(x, y); - - // apply Hadamard to each qubit of the input register - ApplyToEach(H, x); - - // measure all qubits of the input register; - // the result of each measurement is converted to a Bool - for (i in 0..N-1) { - if (M(x[i]) != Zero) { - set r[i] = 1; - } + operation BV_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Int[] { + + mutable r = new Int[N]; + + // allocate N+1 qubits + using (qs = Qubit[N + 1]) { + + // split allocated qubits into input register and answer register + let x = qs[0 .. N - 1]; + let y = qs[N]; + + // prepare qubits in the right state + BV_StatePrep_Reference(x, y); + + // apply oracle + Uf(x, y); + + // apply Hadamard to each qubit of the input register + ApplyToEach(H, x); + + // measure all qubits of the input register; + // the result of each measurement is converted to a Bool + for (i in 0 .. N - 1) { + + if (M(x[i]) != Zero) { + set r[i] = 1; } - - // before releasing the qubits make sure they are all in |0⟩ state - ResetAll(qs); } - return r; + + // before releasing the qubits make sure they are all in |0⟩ state + ResetAll(qs); } + + return r; } - - + + ////////////////////////////////////////////////////////////////// // Part III. Deutsch-Jozsa Algorithm ////////////////////////////////////////////////////////////////// - + // Task 3.1. Deutsch-Jozsa algorithm implementation // Inputs: // 1) the number of qubits in the input register N for the function f // 2) a quantum operation which implements the oracle |x⟩|y⟩ -> |x⟩|y ⊕ f(x)⟩, where // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function - // You are guaranteed that the function f implemented by the oracle is either - // constant (returns 0 on all inputs or 1 on all inputs) or + // You are guaranteed that the function f implemented by the oracle is either + // constant (returns 0 on all inputs or 1 on all inputs) or // balanced (returns 0 on exactly one half of the input domain and 1 on the other half). // Output: - // true if the function f is constant + // true if the function f is constant // false if the function f is balanced - // - // Note: a trivial approach is to call the oracle multiple times: + + // Note: a trivial approach is to call the oracle multiple times: // if the values for more than half of the possible inputs are the same, the function is constant. // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. - operation DJ_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Bool - { - body - { - // Declare variable in which the result will be accumulated; - // this variable has to be mutable to allow updating it. - mutable isConstantFunction = true; - - // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions - // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), - // it can be expressed as running Bernstein-Vazirani algorithm - // and then post-processing the return value classically: - // the function is constant if and only if all elements of the returned array are false - - let r = BV_Algorithm_Reference(N, Uf); - for (i in 0..N-1) { - set isConstantFunction = isConstantFunction && (r[i] == 0); - } - - return isConstantFunction; + operation DJ_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Bool { + + // Declare variable in which the result will be accumulated; + // this variable has to be mutable to allow updating it. + mutable isConstantFunction = true; + + // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions + // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), + // it can be expressed as running Bernstein-Vazirani algorithm + // and then post-processing the return value classically: + // the function is constant if and only if all elements of the returned array are false + let r = BV_Algorithm_Reference(N, Uf); + + for (i in 0 .. N - 1) { + set isConstantFunction = isConstantFunction && r[i] == 0; } + + return isConstantFunction; } - - + + ////////////////////////////////////////////////////////////////// // Part IV. Come up with your own algorithm! ////////////////////////////////////////////////////////////////// - + // Task 4.1. Reconstruct the oracle from task 1.6 // Inputs: // 1) the number of qubits in the input register N for the function f @@ -332,42 +342,41 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // You have implemented the oracle implementing this function in task 1.6. // Output: // A bit vector r which generates the same oracle as the one you are given - operation Noname_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] - { - body - { - // Declare a Bool array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable r = new Int[N]; - - using (qs = Qubit[N+1]) { - // split allocated qubits into input register and answer register - let x = qs[0..N-1]; - let y = qs[N]; - - // apply oracle to qubits in all 0 state - Uf(x, y); - - // f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) = 2 Σᵢ 𝑟ᵢ 𝑥ᵢ + Σᵢ 𝑟ᵢ + Σᵢ 𝑥ᵢ + N = Σᵢ 𝑟ᵢ + N - // remove the N from the expression - if (N % 2 == 1) { - X(y); - } - - // now y = Σᵢ 𝑟ᵢ - - // measure the output register - let m = M(y); - if (m == One) { - // adjust parity of bit vector r - set r[0] = 1; - } - - // before releasing the qubits make sure they are all in |0⟩ state - ResetAll(qs); + operation Noname_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Int[] { + + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + using (qs = Qubit[N + 1]) { + + // split allocated qubits into input register and answer register + let x = qs[0 .. N - 1]; + let y = qs[N]; + + // apply oracle to qubits in all 0 state + Uf(x, y); + + // f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) = 2 Σᵢ 𝑟ᵢ 𝑥ᵢ + Σᵢ 𝑟ᵢ + Σᵢ 𝑥ᵢ + N = Σᵢ 𝑟ᵢ + N + // remove the N from the expression + if (N % 2 == 1) { + X(y); } - - return r; + + // now y = Σᵢ 𝑟ᵢ + + // measure the output register + let m = M(y); + if (m == One) { + // adjust parity of bit vector r + set r[0] = 1; + } + + // before releasing the qubits make sure they are all in |0⟩ state + ResetAll(qs); } + + return r; } + } diff --git a/DeutschJozsaAlgorithm/Tasks.qs b/DeutschJozsaAlgorithm/Tasks.qs index 89b81efc..60ba5d24 100644 --- a/DeutschJozsaAlgorithm/Tasks.qs +++ b/DeutschJozsaAlgorithm/Tasks.qs @@ -1,32 +1,33 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.DeutschJozsaAlgorithm -{ +namespace Quantum.Kata.DeutschJozsaAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Deutsch-Jozsa algorithm" quantum kata is a series of exercises designed + + // "Deutsch-Jozsa algorithm" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. // It covers the following topics: // - writing oracles (quantum operations which implement certain classical functions), // - Bernstein-Vazirani algorithm for recovering the parameters of a scalar product function, // - Deutsch-Jozsa algorithm for recognizing a function as constant or balanced, and // - writing tests in Q#. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - + ////////////////////////////////////////////////////////////////// // Part I. Oracles ////////////////////////////////////////////////////////////////// - + // In this section you will implement oracles defined by classical functions using the following rules: // - a function f(𝑥₀, …, 𝑥ₙ₋₁) with N bits of input x = (𝑥₀, …, 𝑥ₙ₋₁) and 1 bit of output y // defines an oracle which acts on N input qubits and 1 output qubit. @@ -34,73 +35,61 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // |x⟩ |y⟩ -> |x⟩ |y ⊕ f(x)⟩ (⊕ is addition modulo 2) // - the oracle effect on qubits in superposition is defined following the linearity of quantum operations. // - the oracle must act properly on qubits in all possible input states. - + // Task 1.1. f(x) = 0 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_Zero (x : Qubit[], y : Qubit) : () - { - body - { - // Since f(x) = 0 for all values of x, |y ⊕ f(x)⟩ = |y⟩. - // This means that the operation doesn't need to do any transformation to the inputs. - // Build the project and run the tests to see that T01_Oracle_Zero_Test test passes. - } + operation Oracle_Zero (x : Qubit[], y : Qubit) : Unit { + // Since f(x) = 0 for all values of x, |y ⊕ f(x)⟩ = |y⟩. + // This means that the operation doesn't need to do any transformation to the inputs. + // Build the project and run the tests to see that T01_Oracle_Zero_Test test passes. } - + + // Task 1.2. f(x) = 1 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_One (x : Qubit[], y : Qubit) : () - { - body - { - // Since f(x) = 1 for all values of x, |y ⊕ f(x)⟩ = |y ⊕ 1⟩ = |NOT y⟩. - // This means that the operation needs to flip qubit y (i.e. transform |0⟩ to |1⟩ and vice versa). + operation Oracle_One (x : Qubit[], y : Qubit) : Unit { + // Since f(x) = 1 for all values of x, |y ⊕ f(x)⟩ = |y ⊕ 1⟩ = |NOT y⟩. + // This means that the operation needs to flip qubit y (i.e. transform |0⟩ to |1⟩ and vice versa). - // ... - } + // ... } - + + // Task 1.3. f(x) = xₖ (the value of k-th qubit) - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) 0-based index of the qubit from input register (0 <= k < N) // Goal: transform state |x, y⟩ into state |x, y ⊕ xₖ⟩ (⊕ is addition modulo 2). - operation Oracle_Kth_Qubit (x : Qubit[], y : Qubit, k : Int) : () - { - body - { - // The following line enforces the constraints on the value of k that you are given. - // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. - AssertBoolEqual(0 <= k && k < Length(x), true, "k should be between 0 and N-1, inclusive"); + operation Oracle_Kth_Qubit (x : Qubit[], y : Qubit, k : Int) : Unit { + // The following line enforces the constraints on the value of k that you are given. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertBoolEqual(0 <= k && k < Length(x), true, "k should be between 0 and N-1, inclusive"); - // ... - } + // ... } - + + // Task 1.4. f(x) = 1 if x has odd number of 1s, and 0 otherwise - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_OddNumberOfOnes (x : Qubit[], y : Qubit) : () - { - body - { - // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) + operation Oracle_OddNumberOfOnes (x : Qubit[], y : Qubit) : Unit { + // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) - // ... - } + // ... } - + + // Task 1.5. f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r (scalar product function) - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length N represented as Int[] @@ -108,90 +97,77 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). // // Note: the functions featured in tasks 1.1, 1.3 and 1.4 are special cases of this function. - operation Oracle_ProductFunction (x : Qubit[], y : Qubit, r : Int[]) : () - { - body - { - // The following line enforces the constraint on the input arrays. - // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. - AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + operation Oracle_ProductFunction (x : Qubit[], y : Qubit, r : Int[]) : Unit { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); - // ... - } + // ... } - + + // Task 1.6. f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for a given bit vector r - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length N represented as Int[] // You are guaranteed that the qubit array and the bit vector have the same length. // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_ProductWithNegationFunction (x : Qubit[], y : Qubit, r : Int[]) : () - { - body - { - // The following line enforces the constraint on the input arrays. - // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. - AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + operation Oracle_ProductWithNegationFunction (x : Qubit[], y : Qubit, r : Int[]) : Unit { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); - // ... - } + // ... } - + + // Task 1.7. f(x) = Σᵢ 𝑥ᵢ + (1 if prefix of x is equal to the given bit vector, and 0 otherwise) modulo 2 - // Inputs: + // Inputs: // 1) N qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // 3) a bit vector of length P represented as Int[] (1 <= P <= N) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - // + // // A prefix of length k of a state |x⟩ = |x₁, ..., xₙ⟩ is the state of its first k qubits |x₁, ..., xₖ⟩. // For example, a prefix of length 2 of a state |0110⟩ is 01. - operation Oracle_HammingWithPrefix (x : Qubit[], y : Qubit, prefix : Int[]) : () - { - body - { - // The following line enforces the constraint on the input arrays. - // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. - let P = Length(prefix); - AssertBoolEqual(1 <= P && P <= Length(x), true, "P should be between 1 and N, inclusive"); + operation Oracle_HammingWithPrefix (x : Qubit[], y : Qubit, prefix : Int[]) : Unit { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + let P = Length(prefix); + AssertBoolEqual(1 <= P && P <= Length(x), true, "P should be between 1 and N, inclusive"); - // Hint: the first part of the function is the same as in task 1.4 + // Hint: the first part of the function is the same as in task 1.4 - // ... + // ... - // Hint: you can use Controlled functor to perform multicontrolled gates - // (gates with multiple control qubits). + // Hint: you can use Controlled functor to perform multicontrolled gates + // (gates with multiple control qubits). - // ... - } + // ... } - + + // Task 1.8*. f(x) = 1 if x has two or three bits (out of three) set to 1, and 0 otherwise (majority function) - // Inputs: + // Inputs: // 1) 3 qubits in arbitrary state |x⟩ (input register) // 2) a qubit in arbitrary state |y⟩ (output qubit) // Goal: transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2). - operation Oracle_MajorityFunction (x : Qubit[], y : Qubit) : () - { - body - { - // The following line enforces the constraint on the input array. - // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. - AssertBoolEqual(3 == Length(x), true, "x should have exactly 3 qubits"); - - // Hint: represent f(x) in terms of AND and ⊕ operations + operation Oracle_MajorityFunction (x : Qubit[], y : Qubit) : Unit { + // The following line enforces the constraint on the input array. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertBoolEqual(3 == Length(x), true, "x should have exactly 3 qubits"); - // ... - } + // Hint: represent f(x) in terms of AND and ⊕ operations + + // ... } - - + + ////////////////////////////////////////////////////////////////// // Part II. Bernstein-Vazirani Algorithm ////////////////////////////////////////////////////////////////// - + // Task 2.1. State preparation for Bernstein-Vazirani algorithm // Inputs: // 1) N qubits in |0⟩ state (query register) @@ -200,15 +176,16 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // 1) create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ on query register // (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ) // 2) create |-⟩ state (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)) on answer register - operation BV_StatePrep (query : Qubit[], answer : Qubit) : () - { - body - { + operation BV_StatePrep (query : Qubit[], answer : Qubit) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 2.2. Bernstein-Vazirani algorithm implementation // Inputs: // 1) the number of qubits in the input register N for the function f @@ -220,111 +197,99 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // Output: // A bit vector r reconstructed from the function // - // Note: a trivial approach is to call the oracle N times: + // Note: a trivial approach is to call the oracle N times: // |10...0⟩|0⟩ = |10...0⟩|r₀⟩, |010...0⟩|0⟩ = |010...0⟩|r₁⟩ and so on. // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. - operation BV_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] - { - body - { - // Declare a Bool array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable r = new Int[N]; + operation BV_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Int[] { + + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + // ... - // ... - - return r; - } + return r; } - + + // Task 2.3. Testing Bernstein-Vazirani algorithm - // Goal: use your implementation of Bernstein-Vazirani algorithm from task 2.2 to figure out + // Goal: use your implementation of Bernstein-Vazirani algorithm from task 2.2 to figure out // what bit vector the scalar product function oracle from task 1.5 was using. // As a reminder, this oracle creates an operation f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r, // and Bernstein-Vazirani algorithm recovers that bit vector given the operation. - operation BV_Test () : () - { - body - { - // Hint: use Oracle_ProductFunction to implement the scalar product function oracle passed to BV_Algorithm. - // Since Oracle_ProductFunction takes three arguments (Qubit[], Qubit and Int[]), - // and the operation passed to BV_Algorithm must take two arguments (Qubit[] and Qubit), - // you need to use partial application to fix the third argument (a specific value of a bit vector). - // - // You might want to use something like the following: - // let oracle = Oracle_ProductFunction(_, _, [...your bit vector here...]); + operation BV_Test () : Unit { + // Hint: use Oracle_ProductFunction to implement the scalar product function oracle passed to BV_Algorithm. + // Since Oracle_ProductFunction takes three arguments (Qubit[], Qubit and Int[]), + // and the operation passed to BV_Algorithm must take two arguments (Qubit[] and Qubit), + // you need to use partial application to fix the third argument (a specific value of a bit vector). + // + // You might want to use something like the following: + // let oracle = Oracle_ProductFunction(_, _, [...your bit vector here...]); - // Hint: use AssertIntArrayEqual function to assert that the return value of BV_Algorithm operation - // matches the expected value (i.e. the bit vector passed to Oracle_ProductFunction). + // Hint: use AssertIntArrayEqual function to assert that the return value of BV_Algorithm operation + // matches the expected value (i.e. the bit vector passed to Oracle_ProductFunction). - // BV_Test appears in the list of unit tests for the solution; run it to verify your code. + // BV_Test appears in the list of unit tests for the solution; run it to verify your code. - // ... - } + // ... } - - + + ////////////////////////////////////////////////////////////////// // Part III. Deutsch-Jozsa Algorithm ////////////////////////////////////////////////////////////////// - + // Task 3.1. Deutsch-Jozsa algorithm implementation // Inputs: // 1) the number of qubits in the input register N for the function f // 2) a quantum operation which implements the oracle |x⟩|y⟩ -> |x⟩|y ⊕ f(x)⟩, where // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function - // You are guaranteed that the function f implemented by the oracle is either - // constant (returns 0 on all inputs or 1 on all inputs) or + // You are guaranteed that the function f implemented by the oracle is either + // constant (returns 0 on all inputs or 1 on all inputs) or // balanced (returns 0 on exactly one half of the input domain and 1 on the other half). // Output: - // true if the function f is constant + // true if the function f is constant // false if the function f is balanced // - // Note: a trivial approach is to call the oracle multiple times: + // Note: a trivial approach is to call the oracle multiple times: // if the values for more than half of the possible inputs are the same, the function is constant. // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. - operation DJ_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Bool - { - body - { - // Declare Bool variable in which the result will be accumulated; - // this variable has to be mutable to allow updating it. - mutable isConstantFunction = true; + operation DJ_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Bool { + + // Declare Bool variable in which the result will be accumulated; + // this variable has to be mutable to allow updating it. + mutable isConstantFunction = true; + + // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions + // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), + // it can be expressed as running Bernstein-Vazirani algorithm + // and then post-processing the return value classically + + // ... - // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions - // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), - // it can be expressed as running Bernstein-Vazirani algorithm - // and then post-processing the return value classically - - // ... - - return isConstantFunction; - } + return isConstantFunction; } - + + // Task 3.2. Testing Deutsch-Jozsa algorithm - // Goal: use your implementation of Deutsch-Jozsa algorithm from task 3.1 to test + // Goal: use your implementation of Deutsch-Jozsa algorithm from task 3.1 to test // each of the oracles you've implemented in part I for being constant or balanced. - operation DJ_Test () : () - { - body - { - // Hint: you will need to use partial application to test ones such as Oracle_Kth_Qubit and Oracle_ProductFunction; - // see task 2.3 for a description of how to do that. + operation DJ_Test () : Unit { + // Hint: you will need to use partial application to test ones such as Oracle_Kth_Qubit and Oracle_ProductFunction; + // see task 2.3 for a description of how to do that. - // Hint: use AssertBoolEqual function to assert that the return value of DJ_Algorithm operation matches the expected value + // Hint: use AssertBoolEqual function to assert that the return value of DJ_Algorithm operation matches the expected value - // DJ_Test appears in the list of unit tests for the solution; run it to verify your code. + // DJ_Test appears in the list of unit tests for the solution; run it to verify your code. - // ... - } + // ... } - - + + ////////////////////////////////////////////////////////////////// // Part IV. Come up with your own algorithm! ////////////////////////////////////////////////////////////////// - + // Task 4.1. Reconstruct the oracle from task 1.6 // Inputs: // 1) the number of qubits in the input register N for the function f @@ -335,20 +300,17 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm // You have implemented the oracle implementing this function in task 1.6. // Output: // A bit vector r which generates the same oracle as the one you are given - operation Noname_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] - { - body - { - // Hint: The bit vector r does not need to be the same as the one used by the oracle, - // it just needs to produce equivalent results. - - // Declare a Bool array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable r = new Int[N]; - - // ... - - return r; - } + operation Noname_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => Unit)) : Int[] { + + // Hint: The bit vector r does not need to be the same as the one used by the oracle, + // it just needs to produce equivalent results. + + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + // ... + return r; } + } diff --git a/DeutschJozsaAlgorithm/Tests.qs b/DeutschJozsaAlgorithm/Tests.qs index 4ed49831..053e93ee 100644 --- a/DeutschJozsaAlgorithm/Tests.qs +++ b/DeutschJozsaAlgorithm/Tests.qs @@ -1,348 +1,315 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.DeutschJozsaAlgorithm -{ +namespace Quantum.Kata.DeutschJozsaAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ - operation ApplyOracle (qs : Qubit[], oracle : ((Qubit[], Qubit) => ())) : () - { - body - { - let N = Length(qs); - oracle(qs[0..N-2], qs[N-1]); - } + operation ApplyOracle (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit)) : Unit { + let N = Length(qs); + oracle(qs[0 .. N - 2], qs[N - 1]); } - + + // ------------------------------------------------------ - operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => () : Adjoint)) : () - { - body - { + operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + + body (...) { let N = Length(qs); - oracle(qs[0..N-2], qs[N-1]); + oracle(qs[0 .. N - 2], qs[N - 1]); } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ operation AssertTwoOraclesAreEqual (nQubits : Range, - oracle1 : ((Qubit[], Qubit) => ()), - oracle2 : ((Qubit[], Qubit) => () : Adjoint)) : () - { - body - { - let sol = ApplyOracle(_, oracle1); - let refSol = ApplyOracleA(_, oracle2); - for (i in nQubits) { - AssertOperationsEqualReferenced(sol, refSol, i+1); + oracle1 : ((Qubit[], Qubit) => Unit), + oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + let sol = ApplyOracle(_, oracle1); + let refSol = ApplyOracleA(_, oracle2); + + for (i in nQubits) { + AssertOperationsEqualReferenced(sol, refSol, i + 1); + } + } + + + // ------------------------------------------------------ + operation T11_Oracle_Zero_Test () : Unit { + AssertTwoOraclesAreEqual(1 .. 10, Oracle_Zero, Oracle_Zero_Reference); + } + + + // ------------------------------------------------------ + operation T12_Oracle_One_Test () : Unit { + AssertTwoOraclesAreEqual(1 .. 10, Oracle_One, Oracle_One_Reference); + } + + + // ------------------------------------------------------ + operation T13_Oracle_Kth_Qubit_Test () : Unit { + let maxQ = 6; + + // loop over index of the qubit to be used + for (k in 0 .. maxQ - 1) { + // number of qubits to try is from k+1 to 6 + AssertTwoOraclesAreEqual(k + 1 .. maxQ, Oracle_Kth_Qubit(_, _, k), Oracle_Kth_Qubit_Reference(_, _, k)); + } + } + + + // ------------------------------------------------------ + operation T14_Oracle_OddNumberOfOnes_Test () : Unit { + + // cross-test: for 1 qubit it's the same as Kth_Qubit for k = 0 + AssertTwoOraclesAreEqual(1 .. 1, Oracle_OddNumberOfOnes, Oracle_Kth_Qubit_Reference(_, _, 0)); + + AssertTwoOraclesAreEqual(1 .. 10, Oracle_OddNumberOfOnes, Oracle_OddNumberOfOnes_Reference); + } + + + // ------------------------------------------------------ + operation AssertTwoOraclesWithIntAreEqual (r : Int[], + oracle1 : ((Qubit[], Qubit, Int[]) => Unit), + oracle2 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint)) : Unit { + AssertTwoOraclesAreEqual(Length(r) .. Length(r), oracle1(_, _, r), oracle2(_, _, r)); + } + + + operation T15_Oracle_ProductFunction_Test () : Unit { + // cross-tests + // the mask for all 1's corresponds to Oracle_OddNumberOfOnes + mutable r = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + let L = Length(r); + + for (i in 2 .. L) { + AssertTwoOraclesAreEqual(i .. i, Oracle_ProductFunction(_, _, r[0 .. i - 1]), Oracle_OddNumberOfOnes_Reference); + } + + // the mask with all 0's corresponds to Oracle_Zero + for (i in 0 .. L - 1) { + set r[i] = 0; + } + + for (i in 2 .. L) { + AssertTwoOraclesAreEqual(i .. i, Oracle_ProductFunction(_, _, r[0 .. i - 1]), Oracle_Zero_Reference); + } + + // the mask with only the K-th element set to 1 corresponds to Oracle_Kth_Qubit + for (i in 0 .. L - 1) { + set r[i] = 1; + AssertTwoOraclesAreEqual(L .. L, Oracle_ProductFunction(_, _, r), Oracle_Kth_Qubit_Reference(_, _, i)); + set r[i] = 0; + } + + set r = [1, 0, 1, 0, 1, 0]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + + set r = [1, 0, 0, 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + + set r = [0, 0, 1, 1, 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + } + + + operation T16_Oracle_ProductWithNegationFunction_Test () : Unit { + // cross-tests + // the mask for all 1's corresponds to Oracle_OddNumberOfOnes + mutable r = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + let L = Length(r); + + for (i in 2 .. L) { + AssertTwoOraclesAreEqual(i .. i, Oracle_ProductWithNegationFunction(_, _, r[0 .. i - 1]), Oracle_OddNumberOfOnes_Reference); + } + + set r = [1, 0, 1, 0, 1, 0]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + + set r = [1, 0, 0, 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + + set r = [0, 0, 1, 1, 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + } + + + operation T17_Oracle_HammingWithPrefix_Test () : Unit { + mutable prefix = [1]; + AssertTwoOraclesAreEqual(1 .. 10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + + set prefix = [1, 0]; + AssertTwoOraclesAreEqual(2 .. 10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + + set prefix = [0, 0, 0]; + AssertTwoOraclesAreEqual(3 .. 10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + } + + + operation T18_Oracle_MajorityFunction_Test () : Unit { + AssertTwoOraclesAreEqual(3 .. 3, Oracle_MajorityFunction, Oracle_MajorityFunction_Reference); + } + + + // ------------------------------------------------------ + operation T21_BV_StatePrep_Test () : Unit { + + for (N in 1 .. 10) { + + using (qs = Qubit[N + 1]) { + // apply operation that needs to be tested + BV_StatePrep(qs[0 .. N - 1], qs[N]); + + // apply adjoint reference operation + Adjoint BV_StatePrep_Reference(qs[0 .. N - 1], qs[N]); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } } - // ------------------------------------------------------ - operation T11_Oracle_Zero_Test () : () - { - body - { - AssertTwoOraclesAreEqual(1..10, Oracle_Zero, Oracle_Zero_Reference); - } - } - - // ------------------------------------------------------ - operation T12_Oracle_One_Test () : () - { - body - { - AssertTwoOraclesAreEqual(1..10, Oracle_One, Oracle_One_Reference); - } - } - - // ------------------------------------------------------ - operation T13_Oracle_Kth_Qubit_Test () : () - { - body - { - let maxQ = 6; - // loop over index of the qubit to be used - for (k in 0..maxQ-1) { - // number of qubits to try is from k+1 to 6 - AssertTwoOraclesAreEqual(k+1..maxQ, Oracle_Kth_Qubit(_, _, k), Oracle_Kth_Qubit_Reference(_, _, k)); - } - } - } - - // ------------------------------------------------------ - operation T14_Oracle_OddNumberOfOnes_Test () : () - { - body - { - // cross-test: for 1 qubit it's the same as Kth_Qubit for k = 0 - AssertTwoOraclesAreEqual(1..1, Oracle_OddNumberOfOnes, Oracle_Kth_Qubit_Reference(_, _, 0)); - - AssertTwoOraclesAreEqual(1..10, Oracle_OddNumberOfOnes, Oracle_OddNumberOfOnes_Reference); - } - } - - // ------------------------------------------------------ - operation AssertTwoOraclesWithIntAreEqual (r : Int[], - oracle1 : ((Qubit[], Qubit, Int[]) => ()), - oracle2 : ((Qubit[], Qubit, Int[]) => () : Adjoint)) : () - { - body - { - AssertTwoOraclesAreEqual(Length(r)..Length(r), oracle1(_, _, r), oracle2(_, _, r)); - } - } - - operation T15_Oracle_ProductFunction_Test () : () - { - body - { - // cross-tests - // the mask for all 1's corresponds to Oracle_OddNumberOfOnes - mutable r = [1; 1; 1; 1; 1; 1; 1; 1; 1; 1]; - let L = Length(r); - for (i in 2..L) { - AssertTwoOraclesAreEqual(i..i, Oracle_ProductFunction(_, _, r[0..i-1]), Oracle_OddNumberOfOnes_Reference); - } - - // the mask with all 0's corresponds to Oracle_Zero - for (i in 0..L-1) { - set r[i] = 0; - } - for (i in 2..L) { - AssertTwoOraclesAreEqual(i..i, Oracle_ProductFunction(_, _, r[0..i-1]), Oracle_Zero_Reference); - } - - // the mask with only the K-th element set to 1 corresponds to Oracle_Kth_Qubit - for (i in 0..L-1) { - set r[i] = 1; - AssertTwoOraclesAreEqual(L..L, Oracle_ProductFunction(_, _, r), Oracle_Kth_Qubit_Reference(_, _, i)); - set r[i] = 0; - } - - set r = [1; 0; 1; 0; 1; 0]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); - - set r = [1; 0; 0; 1]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); - - set r = [0; 0; 1; 1; 1]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); - } - } - - operation T16_Oracle_ProductWithNegationFunction_Test () : () - { - body - { - // cross-tests - // the mask for all 1's corresponds to Oracle_OddNumberOfOnes - mutable r = [1; 1; 1; 1; 1; 1; 1; 1; 1; 1]; - let L = Length(r); - for (i in 2..L) { - AssertTwoOraclesAreEqual(i..i, Oracle_ProductWithNegationFunction(_, _, r[0..i-1]), Oracle_OddNumberOfOnes_Reference); - } - - set r = [1; 0; 1; 0; 1; 0]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); - - set r = [1; 0; 0; 1]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); - - set r = [0; 0; 1; 1; 1]; - AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); - } - } - - operation T17_Oracle_HammingWithPrefix_Test () : () - { - body - { - mutable prefix = [1]; - AssertTwoOraclesAreEqual(1..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); - - set prefix = [1; 0]; - AssertTwoOraclesAreEqual(2..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); - - set prefix = [0; 0; 0]; - AssertTwoOraclesAreEqual(3..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); - } - } - - operation T18_Oracle_MajorityFunction_Test () : () - { - body - { - AssertTwoOraclesAreEqual(3..3, Oracle_MajorityFunction, Oracle_MajorityFunction_Reference); - } - } - - // ------------------------------------------------------ - operation T21_BV_StatePrep_Test () : () - { - body - { - for (N in 1..10) { - using (qs = Qubit[N+1]) - { - // apply operation that needs to be tested - BV_StatePrep(qs[0..N-1], qs[N]); - - // apply adjoint reference operation - (Adjoint BV_StatePrep_Reference)(qs[0..N-1], qs[N]); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } - } - } - } - - // ------------------------------------------------------ - function AssertOracleCallsCount<'T>(count: Int, oracle: 'T) : () { } // ------------------------------------------------------ - function ResetOracleCallsCount() : () { } - + function AssertOracleCallsCount<'T> (count : Int, oracle : 'T) : Unit { } + + // ------------------------------------------------------ - function AssertIntArrayEqual (actual : Int[], expected : Int[], message : String) : () { - let n = Length(actual); + function ResetOracleCallsCount () : Unit { } + + + // ------------------------------------------------------ + function AssertIntArrayEqual (actual : Int[], expected : Int[], message : String) : Unit { + + let n = Length(actual); if (n != Length(expected)) { fail message; } - for (idx in 0..(n-1)) { - if( actual[idx] != expected[idx] ) { + + for (idx in 0 .. n - 1) { + if (actual[idx] != expected[idx]) { fail message; } - } + } } - + + // ------------------------------------------------------ function IntArrFromPositiveInt (n : Int, bits : Int) : Int[] { + let rbool = BoolArrFromPositiveInt(n, bits); mutable r = new Int[bits]; - for (i in 0..bits-1) { + + for (i in 0 .. bits - 1) { if (rbool[i]) { set r[i] = 1; } } + return r; } - + + // ------------------------------------------------------ - operation AssertBVAlgorithmWorks (r : Int[]) : () - { - body - { - let oracle = Oracle_ProductFunction_Reference(_, _, r); - AssertIntArrayEqual(BV_Algorithm(Length(r), oracle), r, "Bernstein-Vazirani algorithm failed"); - AssertOracleCallsCount(1, oracle); - } + operation AssertBVAlgorithmWorks (r : Int[]) : Unit { + let oracle = Oracle_ProductFunction_Reference(_, _, r); + AssertIntArrayEqual(BV_Algorithm(Length(r), oracle), r, "Bernstein-Vazirani algorithm failed"); + AssertOracleCallsCount(1, oracle); } - - operation T22_BV_Algorithm_Test () : () - { - body - { - ResetOracleCallsCount(); - - // test BV the way we suggest the learner to test it: - // apply the algorithm to reference oracles and check that the output is as expected - for (bits in 1..4) { - for (n in 0..2^bits-1) { - let r = IntArrFromPositiveInt(n, bits); - AssertBVAlgorithmWorks(r); - } + + + operation T22_BV_Algorithm_Test () : Unit { + ResetOracleCallsCount(); + + // test BV the way we suggest the learner to test it: + // apply the algorithm to reference oracles and check that the output is as expected + for (bits in 1 .. 4) { + for (n in 0 .. 2 ^ bits - 1) { + let r = IntArrFromPositiveInt(n, bits); + AssertBVAlgorithmWorks(r); } - AssertBVAlgorithmWorks([1; 1; 1; 0; 0]); - AssertBVAlgorithmWorks([1; 0; 1; 0; 1; 0]); } + + AssertBVAlgorithmWorks([1, 1, 1, 0, 0]); + AssertBVAlgorithmWorks([1, 0, 1, 0, 1, 0]); } - + + // ------------------------------------------------------ - operation AssertDJAlgorithmWorks(oracle: ((Qubit[], Qubit) => ()), expected : Bool, msg: String) : () - { - body - { - AssertBoolEqual(DJ_Algorithm(4, oracle), expected, msg); - AssertOracleCallsCount(1, oracle); - } + operation AssertDJAlgorithmWorks (oracle : ((Qubit[], Qubit) => Unit), expected : Bool, msg : String) : Unit { + AssertBoolEqual(DJ_Algorithm(4, oracle), expected, msg); + AssertOracleCallsCount(1, oracle); } + + + operation T31_DJ_Algorithm_Test () : Unit { - operation T31_DJ_Algorithm_Test () : () - { - body - { - ResetOracleCallsCount(); - - // test DJ the way we suggest the learner to test it: - // apply the algorithm to reference oracles and check that the output is as expected - AssertBoolEqual(DJ_Algorithm(4, Oracle_Zero_Reference), true, "f(x) = 0 not identified as constant"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_One_Reference), true, "f(x) = 1 not identified as constant"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_Kth_Qubit_Reference(_, _, 1)), false, "f(x) = x_k not identified as balanced"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_OddNumberOfOnes_Reference), false, "f(x) = sum of x_i not identified as balanced"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductFunction_Reference(_, _, [1; 0; 1; 1])), false, "f(x) = sum of r_i x_i not identified as balanced"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductWithNegationFunction_Reference(_, _, [1; 0; 1; 1])), false, "f(x) = sum of r_i x_i + (1 - r_i)(1 - x_i) not identified as balanced"); - AssertBoolEqual(DJ_Algorithm(4, Oracle_HammingWithPrefix_Reference(_, _, [0; 1])), false, "f(x) = sum of x_i + 1 if prefix equals given not identified as balanced"); - AssertBoolEqual(DJ_Algorithm(3, Oracle_MajorityFunction_Reference), false, "f(x) = majority function not identified as balanced"); - } + ResetOracleCallsCount(); + + // test DJ the way we suggest the learner to test it: + // apply the algorithm to reference oracles and check that the output is as expected + AssertBoolEqual(DJ_Algorithm(4, Oracle_Zero_Reference), true, "f(x) = 0 not identified as constant"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_One_Reference), true, "f(x) = 1 not identified as constant"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_Kth_Qubit_Reference(_, _, 1)), false, "f(x) = x_k not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_OddNumberOfOnes_Reference), false, "f(x) = sum of x_i not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductFunction_Reference(_, _, [1, 0, 1, 1])), false, "f(x) = sum of r_i x_i not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductWithNegationFunction_Reference(_, _, [1, 0, 1, 1])), false, "f(x) = sum of r_i x_i + (1 - r_i)(1 - x_i) not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_HammingWithPrefix_Reference(_, _, [0, 1])), false, "f(x) = sum of x_i + 1 if prefix equals given not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(3, Oracle_MajorityFunction_Reference), false, "f(x) = majority function not identified as balanced"); } - + + // ------------------------------------------------------ - operation AssertNonameAlgorithmWorks (r : Int[]) : () - { - body - { - let givenOracle = Oracle_ProductWithNegationFunction_Reference(_, _, r); - let res = Noname_Algorithm(Length(r), givenOracle); - - // check that the oracle was called once (later it will be called again by test harness) - AssertOracleCallsCount(1, givenOracle); - - // check that the oracle obtained from r - // is equivalent to the oracle obtained from return value - AssertIntEqual(Length(res), Length(r), "Returned bit vector must have the same length as the oracle input."); - let resOracle = Oracle_ProductWithNegationFunction_Reference(_, _, res); - AssertTwoOraclesAreEqual(Length(r)..Length(r), givenOracle, resOracle); - } + operation AssertNonameAlgorithmWorks (r : Int[]) : Unit { + + let givenOracle = Oracle_ProductWithNegationFunction_Reference(_, _, r); + let res = Noname_Algorithm(Length(r), givenOracle); + + // check that the oracle was called once (later it will be called again by test harness) + AssertOracleCallsCount(1, givenOracle); + + // check that the oracle obtained from r + // is equivalent to the oracle obtained from return value + AssertIntEqual(Length(res), Length(r), "Returned bit vector must have the same length as the oracle input."); + let resOracle = Oracle_ProductWithNegationFunction_Reference(_, _, res); + AssertTwoOraclesAreEqual(Length(r) .. Length(r), givenOracle, resOracle); } - - operation CallNonameAlgoOnInt (n : Int, bits : Int) : () - { - body - { - let r = IntArrFromPositiveInt(n, bits); - AssertNonameAlgorithmWorks(r); - } + + + operation CallNonameAlgoOnInt (n : Int, bits : Int) : Unit { + let r = IntArrFromPositiveInt(n, bits); + AssertNonameAlgorithmWorks(r); } - - operation T41_Noname_Algorithm_Test () : () - { - body - { - ResetOracleCallsCount(); - - // apply the algorithm to reference oracles and check that the output is as expected - // test all bit vectors of length 1..4 - for (bits in 1..4) { - for (n in 0..2^bits-1) { - CallNonameAlgoOnInt(n, bits); - } + + + operation T41_Noname_Algorithm_Test () : Unit { + + ResetOracleCallsCount(); + + // apply the algorithm to reference oracles and check that the output is as expected + // test all bit vectors of length 1..4 + for (bits in 1 .. 4) { + + for (n in 0 .. 2 ^ bits - 1) { + CallNonameAlgoOnInt(n, bits); } - // and a couple of random ones - AssertNonameAlgorithmWorks([1; 1; 1; 0; 0]); - AssertNonameAlgorithmWorks([1; 0; 1; 0; 1; 0]); } + + // and a couple of random ones + AssertNonameAlgorithmWorks([1, 1, 1, 0, 0]); + AssertNonameAlgorithmWorks([1, 0, 1, 0, 1, 0]); } -} \ No newline at end of file + +} diff --git a/GroversAlgorithm/GroversAlgorithm.csproj b/GroversAlgorithm/GroversAlgorithm.csproj index 5b7dfb99..eeded2c0 100755 --- a/GroversAlgorithm/GroversAlgorithm.csproj +++ b/GroversAlgorithm/GroversAlgorithm.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.GroversAlgorithm - - - + + + diff --git a/GroversAlgorithm/ReferenceImplementation.qs b/GroversAlgorithm/ReferenceImplementation.qs index 2764dc37..9296bca8 100755 --- a/GroversAlgorithm/ReferenceImplementation.qs +++ b/GroversAlgorithm/ReferenceImplementation.qs @@ -1,111 +1,114 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.GroversAlgorithm -{ +namespace Quantum.Kata.GroversAlgorithm { + open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Part I. Oracles for Grover's Search ////////////////////////////////////////////////////////////////// - + // Task 1.1. The |11...1〉 oracle - operation Oracle_AllOnes_Reference (queryRegister: Qubit[], target : Qubit) : () - { - body - { - (Controlled X)(queryRegister, target); + operation Oracle_AllOnes_Reference (queryRegister : Qubit[], target : Qubit) : Unit { + + body (...) { + Controlled X(queryRegister, target); } - adjoint auto; + + adjoint invert; } - + + // Task 1.2. The |1010...〉 oracle - operation Oracle_AlternatingBits_Reference (queryRegister: Qubit[], target : Qubit) : () - { - body - { - // flip the bits in odd (0-based positions), + operation Oracle_AlternatingBits_Reference (queryRegister : Qubit[], target : Qubit) : Unit { + + body (...) { + // flip the bits in odd (0-based positions), // so that the condition for flipping the state of the target qubit is "query register is in 1...1 state" FlipOddPositionBits_Reference(queryRegister); - (Controlled X)(queryRegister, target); - (Adjoint FlipOddPositionBits_Reference)(queryRegister); + Controlled X(queryRegister, target); + Adjoint FlipOddPositionBits_Reference(queryRegister); } - adjoint auto; + + adjoint invert; } - - operation FlipOddPositionBits_Reference (register : Qubit[]) : () - { - body - { - for (i in 0..Length(register) - 1) - { - if (i % 2 == 1) - { + + + operation FlipOddPositionBits_Reference (register : Qubit[]) : Unit { + + body (...) { + for (i in 0 .. Length(register) - 1) { + if (i % 2 == 1) { X(register[i]); } } } - adjoint auto; + + adjoint invert; } - + + // Task 1.3. Arbitrary bit pattern oracle - operation Oracle_ArbitraryPattern_Reference (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : () - { - body - { + operation Oracle_ArbitraryPattern_Reference (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : Unit { + + body (...) { (ControlledOnBitString(pattern, X))(queryRegister, target); } - adjoint auto; + + adjoint invert; } - + + // Task 1.4*. Oracle converter - operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => () : Adjoint), register : Qubit[]) : () - { - body - { - using (ancillae = Qubit[1]) - { + operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint), register : Qubit[]) : Unit { + + body (...) { + using (ancillae = Qubit[1]) { let target = ancillae[0]; - + // Put the target into the |-〉 state X(target); H(target); + // Apply the marking oracle; since the target is in the |-〉 state, // flipping the target if the register satisfies the oracle condition will apply a -1 factor to the state markingOracle(register, target); + // Put the target back into |0〉 so we can return it H(target); X(target); } } - adjoint auto; + + adjoint invert; } - - function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => () : Adjoint)) : ((Qubit[]) => () : Adjoint) - { + + + function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint)) : (Qubit[] => Unit : Adjoint) { return OracleConverterImpl_Reference(markingOracle, _); } - - + + ////////////////////////////////////////////////////////////////// // Part II. The Grover iteration ////////////////////////////////////////////////////////////////// - + // Task 2.1. The Hadamard transform - operation HadamardTransform_Reference (register: Qubit[]) : () - { - body - { + operation HadamardTransform_Reference (register : Qubit[]) : Unit { + + body (...) { ApplyToEachA(H, register); // ApplyToEach is a library routine that is equivalent to the following code: @@ -114,69 +117,71 @@ namespace Quantum.Kata.GroversAlgorithm // H(register[idxQubit]); // } } - adjoint auto; + + adjoint invert; } - - + + // Task 2.2. Conditional phase flip - operation ConditionalPhaseFlip_Reference (register : Qubit[]) : () - { - body - { + operation ConditionalPhaseFlip_Reference (register : Qubit[]) : Unit { + + body (...) { // Define a marking oracle which detects an all zero state let allZerosOracle = Oracle_ArbitraryPattern_Reference(_, _, new Bool[Length(register)]); + // Convert it into a phase-flip oracle and apply it let flipOracle = OracleConverter_Reference(allZerosOracle); flipOracle(register); } + adjoint self; } - - operation PhaseFlip_ControlledZ (register : Qubit[]) : () - { - body - { + + + operation PhaseFlip_ControlledZ (register : Qubit[]) : Unit { + + body (...) { // Alternative solution, described at https://quantumcomputing.stackexchange.com/questions/4268/how-to-construct-the-inversion-about-the-mean-operator/4269#4269 ApplyToEachA(X, register); - (Controlled Z)(Most(register), Tail(register)); + Controlled Z(Most(register), Tail(register)); ApplyToEachA(X, register); } + adjoint self; } - - + + // Task 2.3. The Grover iteration - operation GroverIteration_Reference (register : Qubit[], oracle : ((Qubit[]) => () : Adjoint)) : () - { - body - { + operation GroverIteration_Reference (register : Qubit[], oracle : (Qubit[] => Unit : Adjoint)) : Unit { + + body (...) { oracle(register); HadamardTransform_Reference(register); ConditionalPhaseFlip_Reference(register); HadamardTransform_Reference(register); } - adjoint auto; + + adjoint invert; } - - + + ////////////////////////////////////////////////////////////////// // Part III. Putting it all together: Grover's search algorithm ////////////////////////////////////////////////////////////////// - + // Task 3.1. Grover's search - operation GroversSearch_Reference (register : Qubit[], oracle : ((Qubit[], Qubit) => () : Adjoint), iterations : Int) : () - { - body - { + operation GroversSearch_Reference (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint), iterations : Int) : Unit { + + body (...) { let phaseOracle = OracleConverter_Reference(oracle); - HadamardTransform_Reference(register); - - for (i in 1..iterations) - { + + for (i in 1 .. iterations) { GroverIteration_Reference(register, phaseOracle); } } - adjoint auto; + + adjoint invert; } + } diff --git a/GroversAlgorithm/Tasks.qs b/GroversAlgorithm/Tasks.qs index 0f2b5530..5d2f2bd7 100755 --- a/GroversAlgorithm/Tasks.qs +++ b/GroversAlgorithm/Tasks.qs @@ -1,60 +1,62 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.GroversAlgorithm -{ +namespace Quantum.Kata.GroversAlgorithm { + open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // The "Grover's Search" quantum kata is a series of exercises designed + + // The "Grover's Search" quantum kata is a series of exercises designed // to get you familiar with Grover's search algorithm. // It covers the following topics: // - writing oracles for Grover's search, // - performing steps of the algorithm, and // - putting it all together: Grover's search algorithm. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // - // Within each section, tasks are given in approximate order of increasing difficulty; - // harder ones are marked with asterisks. - - + + // Within each section, tasks are given in approximate order of increasing difficulty; + // harder ones are marked with asterisks. + + ////////////////////////////////////////////////////////////////// // Part I. Oracles for Grover's Search ////////////////////////////////////////////////////////////////// - + // Task 1.1. The |11...1〉 oracle // Inputs: // 1) N qubits in an arbitrary state |x⟩ (input/query register) // 2) a qubit in an arbitrary state |y⟩ (target qubit) - // Goal: Flip the state of the target qubit (i.e., apply an X gate to it) + // Goal: Flip the state of the target qubit (i.e., apply an X gate to it) // if the query register is in the |11...1⟩ state, // and leave it unchanged if the query register is in any other state. // Leave the query register in the same state it started in. - // Example: + // Example: // If the query register is in state |00...0〉, leave the target qubit unchanged. // If the query register is in state |10...0〉, leave the target qubit unchanged. // If the query register is in state |11...1〉, flip the target qubit. // If the query register is in state (|00...0〉 + |11...1〉) / sqrt(2), and the target is in state |0〉, // the joint state of the query register and the target qubit should be (|00...00〉 + |11...11〉) / sqrt(2). - operation Oracle_AllOnes (queryRegister : Qubit[], target : Qubit) : () - { - body - { + operation Oracle_AllOnes (queryRegister : Qubit[], target : Qubit) : Unit { + + body (...) { // ... - } + } + adjoint self; } - + + // Task 1.2. The |1010...〉 oracle // Inputs: // 1) N qubits in an arbitrary state |x⟩ (input/query register) @@ -63,132 +65,135 @@ namespace Quantum.Kata.GroversAlgorithm // that is, the state with alternating 1 and 0 values, with any number of qubits in the register. // Leave the state of the target qubit unchanged if the query register is in any other state. // Leave the query register in the same state it started in. - // Example: + // Example: // If the register is in state |0000000〉, leave the target qubit unchanged. // If the register is in state |10101〉, flip the target qubit. - operation Oracle_AlternatingBits (queryRegister : Qubit[], target : Qubit) : () - { - body - { + operation Oracle_AlternatingBits (queryRegister : Qubit[], target : Qubit) : Unit { + + body (...) { // ... } + adjoint self; } - + + // Task 1.3. Arbitrary bit pattern oracle // Inputs: // 1) N qubits in an arbitrary state |x⟩ (input/query register) // 2) a qubit in an arbitrary state |y⟩ (target qubit) // 3) a bit pattern of length N represented as Bool[] // Goal: Flip the state of the target qubit if the query register is in the state described by the given bit pattern - // (true represents qubit state One, and false represents Zero). + // (true represents qubit state One, and false represents Zero). // Leave the state of the target qubit unchanged if the query register is in any other state. // Leave the query register in the same state it started in. - // Example: - // If the bit patterns is [true; false], you need to flip the target qubit if and only if the qubits are in the |10⟩ state. - operation Oracle_ArbitraryPattern (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : () - { - body - { + // Example: + // If the bit patterns is [true, false], you need to flip the target qubit if and only if the qubits are in the |10⟩ state. + operation Oracle_ArbitraryPattern (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : Unit { + + body (...) { // The following line enforces the constraint on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertIntEqual(Length(queryRegister), Length(pattern), "Arrays should have the same length"); - + // ... } + adjoint self; } - + + // Task 1.4*. Oracle converter - // Input: A marking oracle: an oracle that takes a register and a target qubit and + // Input: A marking oracle: an oracle that takes a register and a target qubit and // flips the target qubit if the register satisfies a certain condition - // Output: A phase-flipping oracle: an oracle that takes a register and + // Output: A phase-flipping oracle: an oracle that takes a register and // flips the phase of the register if it satisfies this condition // // Note: Grover's algorithm relies on the search condition implemented as a phase-flipping oracle, - // but it is often easier to write a marking oracle for a given condition. This transformation - // allows to convert one type of oracle into the other. The transformation is described at + // but it is often easier to write a marking oracle for a given condition. This transformation + // allows to convert one type of oracle into the other. The transformation is described at // https://en.wikipedia.org/wiki/Grover%27s_algorithm, section "Description of Uω". - - function OracleConverter (markingOracle : ((Qubit[], Qubit) => () : Adjoint)) : (Qubit[] => () : Adjoint) - { - // Hint: Remember that you can define auxiliary operations. - + function OracleConverter (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint)) : (Qubit[] => Unit : Adjoint) { + + // Hint: Remember that you can define auxiliary operations. + // ... - + // Currently this function returns a no-op operation for the sake of being able to compile the code. - // You will need to remove ApplyToEachA and return your own oracle instead. + // You will need to remove ApplyToEachA and return your own oracle instead. return ApplyToEachA(I, _); } - - + + ////////////////////////////////////////////////////////////////// // Part II. The Grover iteration ////////////////////////////////////////////////////////////////// - + // Task 2.1. The Hadamard transform // Input: A register of N qubits in an arbitrary state // Goal: Apply the Hadamard transform to each of the qubits in the register. - // - // Note: If the register started in the |0...0〉 state, this operation + // + // Note: If the register started in the |0...0〉 state, this operation // will prepare an equal superposition of all 2^N basis states. - operation HadamardTransform (register : Qubit[]) : () - { - body - { + operation HadamardTransform (register : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 2.2. Conditional phase flip // Input: A register of N qubits in an arbitrary state. // Goal: Flip the sign of the state of the register if it is not in the |0...0〉 state. - // Example: - // If the register is in state |0...0〉, leave it unchanged. - // If the register is in any other basis state, multiply its phase by -1. + // Example: + // If the register is in state |0...0〉, leave it unchanged. + // If the register is in any other basis state, multiply its phase by -1. // Note: This operation implements operator 2|0...0〉⟨0...0| - I. - operation ConditionalPhaseFlip (register : Qubit[]) : () - { - body - { - // Hint 1: Note that quantum states are defined up to a global phase. - // Thus the state obtained as a result of this operation is the same - // as the state obtained by flipping the sign of only the |0...0〉 state. - - // Hint 2: You can use the same trick as in the oracle converter task. + operation ConditionalPhaseFlip (register : Qubit[]) : Unit { + body (...) { + // Hint 1: Note that quantum states are defined up to a global phase. + // Thus the state obtained as a result of this operation is the same + // as the state obtained by flipping the sign of only the |0...0〉 state. + + // Hint 2: You can use the same trick as in the oracle converter task. + // ... } - adjoint auto; + + adjoint invert; } - + + // Task 2.3. The Grover iteration // Inputs: // 1) N qubits in an arbitrary state |x⟩ (input/query register) - // 2) a phase-flipping oracle that takes an N-qubit register and flips + // 2) a phase-flipping oracle that takes an N-qubit register and flips // the phase of the state if the register is in the desired state. // Goal: Perform one Grover iteration. - operation GroverIteration (register : Qubit[], oracle : (Qubit[] => () : Adjoint)) : () - { - body - { + operation GroverIteration (register : Qubit[], oracle : (Qubit[] => Unit : Adjoint)) : Unit { + + body (...) { // Hint: A Grover iteration consists of 4 steps: // 1) apply the oracle // 2) apply the Hadamard transform // 3) perform a conditional phase shift // 4) apply the Hadamard transform again - + // ... } - adjoint auto; + + adjoint invert; } - - + + ////////////////////////////////////////////////////////////////// // Part III. Putting it all together: Grover's search algorithm ////////////////////////////////////////////////////////////////// - + // Task 3.1. Grover's search // Inputs: // 1) N qubits in the |0...0〉 state, @@ -199,22 +204,16 @@ namespace Quantum.Kata.GroversAlgorithm // // Note: The number of iterations is passed as a parameter because it is defined by the nature of the problem // and is easier to configure/calculate outside the search algorithm itself (for example, in the driver). - operation GroversSearch (register : Qubit[], oracle : ((Qubit[], Qubit) => () : Adjoint), iterations : Int) : () - { - body - { + operation GroversSearch (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint), iterations : Int) : Unit { // ... - } } - + + // Task 3.2. Using Grover's search // Goal: Use your implementation of Grover's algorithm from task 3.1 and the oracles from part 1 // to find the marked elements of the search space. // This task is not covered by a test and allows you to experiment with running the algorithm. - operation E2E_GroversSearch_Test () : () - { - body - { + operation E2E_GroversSearch_Test () : Unit { // Hint 1: To check whether the algorithm found the correct answer (i.e., an answer marked as 1 by the oracle), // you can apply the oracle once more to the register after you've measured it and an ancilla qubit, // which will calculate the function of the answer found by the algorithm. @@ -225,7 +224,6 @@ namespace Quantum.Kata.GroversAlgorithm // Hint 3: You can use the Message function to write the results to the console. // ... - } } - + } diff --git a/GroversAlgorithm/Tests.qs b/GroversAlgorithm/Tests.qs index 7d3e9ddd..cba5feaa 100755 --- a/GroversAlgorithm/Tests.qs +++ b/GroversAlgorithm/Tests.qs @@ -1,140 +1,115 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.GroversAlgorithm -{ +namespace Quantum.Kata.GroversAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ // helper wrapper to represent oracle operation on input and output registers as an operation on an array of qubits - operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => () : Adjoint), qs : Qubit[]) : () - { - body - { + operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => Unit : Adjoint), qs : Qubit[]) : Unit { + + body (...) { op(Most(qs), Tail(qs)); } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ // helper wrapper to test for operation equality on various register sizes - operation AssertRegisterOperationsEqual (testOp : ((Qubit[]) => ()), refOp : ((Qubit[]) => () : Adjoint)) : () - { - body - { - for (n in 2..10) { - AssertOperationsEqualReferenced(testOp, refOp, n); - } + operation AssertRegisterOperationsEqual (testOp : (Qubit[] => Unit), refOp : (Qubit[] => Unit : Adjoint)) : Unit { + for (n in 2 .. 10) { + AssertOperationsEqualReferenced(testOp, refOp, n); } } - + + // ------------------------------------------------------ - operation T11_Oracle_AllOnes_Test () : () - { - body - { - let testOp = QubitArrayWrapperOperation(Oracle_AllOnes, _); - let refOp = QubitArrayWrapperOperation(Oracle_AllOnes_Reference, _); - AssertRegisterOperationsEqual(testOp, refOp); - } + operation T11_Oracle_AllOnes_Test () : Unit { + let testOp = QubitArrayWrapperOperation(Oracle_AllOnes, _); + let refOp = QubitArrayWrapperOperation(Oracle_AllOnes_Reference, _); + AssertRegisterOperationsEqual(testOp, refOp); } - + + // ------------------------------------------------------ - operation T12_Oracle_AlternatingBits_Test () : () - { - body - { - let testOp = QubitArrayWrapperOperation(Oracle_AlternatingBits, _); - let refOp = QubitArrayWrapperOperation(Oracle_AlternatingBits_Reference, _); - AssertRegisterOperationsEqual(testOp, refOp); - } + operation T12_Oracle_AlternatingBits_Test () : Unit { + let testOp = QubitArrayWrapperOperation(Oracle_AlternatingBits, _); + let refOp = QubitArrayWrapperOperation(Oracle_AlternatingBits_Reference, _); + AssertRegisterOperationsEqual(testOp, refOp); } - + + // ------------------------------------------------------ - operation T13_Oracle_ArbitraryPattern_Test () : () - { - body - { - for (n in 2..10) { - let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); - let testOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern(_, _, pattern), _); - let refOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern_Reference(_, _, pattern), _); - AssertOperationsEqualReferenced(testOp, refOp, n+1); - } + operation T13_Oracle_ArbitraryPattern_Test () : Unit { + for (n in 2 .. 10) { + let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); + let testOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern(_, _, pattern), _); + let refOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern_Reference(_, _, pattern), _); + AssertOperationsEqualReferenced(testOp, refOp, n + 1); } } - + + // ------------------------------------------------------ - operation T14_OracleConverter_Test () : () - { - body - { - for (n in 2..10) { - let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); - let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); - let phaseOracleRef = OracleConverter_Reference(markingOracle); - let phaseOracleSol = OracleConverter(markingOracle); - AssertOperationsEqualReferenced(phaseOracleSol, phaseOracleRef, n); - } + operation T14_OracleConverter_Test () : Unit { + for (n in 2 .. 10) { + let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); + let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); + let phaseOracleRef = OracleConverter_Reference(markingOracle); + let phaseOracleSol = OracleConverter(markingOracle); + AssertOperationsEqualReferenced(phaseOracleSol, phaseOracleRef, n); } } - + + // ------------------------------------------------------ - operation T21_HadamardTransform_Test () : () - { - body - { - AssertRegisterOperationsEqual(HadamardTransform, HadamardTransform_Reference); - } + operation T21_HadamardTransform_Test () : Unit { + AssertRegisterOperationsEqual(HadamardTransform, HadamardTransform_Reference); } - + + // ------------------------------------------------------ - operation T22_ConditionalPhaseFlip_Test () : () - { - body - { - AssertRegisterOperationsEqual(ConditionalPhaseFlip, ConditionalPhaseFlip_Reference); - } + operation T22_ConditionalPhaseFlip_Test () : Unit { + AssertRegisterOperationsEqual(ConditionalPhaseFlip, ConditionalPhaseFlip_Reference); } - + + // ------------------------------------------------------ - operation T23_GroverIteration_Test () : () - { - body - { - for (n in 2..10) { - let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); - let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); - let flipOracle = OracleConverter_Reference(markingOracle); - let testOp = GroverIteration(_, flipOracle); - let refOp = GroverIteration_Reference(_, flipOracle); - AssertOperationsEqualReferenced(testOp, refOp, n); - } + operation T23_GroverIteration_Test () : Unit { + for (n in 2 .. 10) { + let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); + let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); + let flipOracle = OracleConverter_Reference(markingOracle); + let testOp = GroverIteration(_, flipOracle); + let refOp = GroverIteration_Reference(_, flipOracle); + AssertOperationsEqualReferenced(testOp, refOp, n); } } - + + // ------------------------------------------------------ - operation T31_GroversSearch_Test () : () - { - body - { - for (n in 2..10) { - let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); - let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); - let testOp = GroversSearch(_, markingOracle, 4); - let refOp = GroversSearch_Reference(_, markingOracle, 4); - AssertOperationsEqualReferenced(testOp, refOp, n); - } + operation T31_GroversSearch_Test () : Unit { + for (n in 2 .. 10) { + let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); + let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); + let testOp = GroversSearch(_, markingOracle, 4); + let refOp = GroversSearch_Reference(_, markingOracle, 4); + AssertOperationsEqualReferenced(testOp, refOp, n); } } -} \ No newline at end of file + +} diff --git a/JointMeasurements/JointMeasurements.csproj b/JointMeasurements/JointMeasurements.csproj index c25d3c7f..d41164e4 100644 --- a/JointMeasurements/JointMeasurements.csproj +++ b/JointMeasurements/JointMeasurements.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.JointMeasurements - - - + + + diff --git a/JointMeasurements/ReferenceImplementation.qs b/JointMeasurements/ReferenceImplementation.qs index cea92267..e29be136 100644 --- a/JointMeasurements/ReferenceImplementation.qs +++ b/JointMeasurements/ReferenceImplementation.qs @@ -1,120 +1,103 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.JointMeasurements -{ +namespace Quantum.Kata.JointMeasurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - + open Microsoft.Quantum.Extensions.Math; + + // Task 1. Single-qubit measurement - operation SingleQubitMeasurement_Reference (qs : Qubit[]) : Int - { - body - { - // Hint: use two single-qubit measurements - if (M(qs[0]) == M(qs[1])) { - return 0; - } else { - return 1; - } + operation SingleQubitMeasurement_Reference (qs : Qubit[]) : Int { + // Hint: use two single-qubit measurements + if (M(qs[0]) == M(qs[1])) { + return 0; + } else { + return 1; } } - + + // Task 2. Parity measurement - operation ParityMeasurement_Reference (qs : Qubit[]) : Int - { - body - { - if (Measure([PauliZ; PauliZ], qs) == Zero) { - return 0; - } else { - return 1; - } + operation ParityMeasurement_Reference (qs : Qubit[]) : Int { + if (Measure([PauliZ, PauliZ], qs) == Zero) { + return 0; + } else { + return 1; } } - + + // Task 3. |0000⟩ + |1111⟩ or |0011⟩ + |1100⟩ ? - operation GHZOrGHZWithX_Reference (qs : Qubit[]) : Int - { - body - { - if (Measure([PauliZ; PauliZ], qs[1..2]) == Zero) { - return 0; - } else { - return 1; - } + operation GHZOrGHZWithX_Reference (qs : Qubit[]) : Int { + if (Measure([PauliZ, PauliZ], qs[1 .. 2]) == Zero) { + return 0; + } else { + return 1; } } - + + // Task 4. |0..0⟩ + |1..1⟩ or W state ? - operation GHZOrWState_Reference (qs : Qubit[]) : Int - { - body - { - if (MeasureAllZ(qs) == Zero) { - return 0; - } else { - return 1; - } + operation GHZOrWState_Reference (qs : Qubit[]) : Int { + if (MeasureAllZ(qs) == Zero) { + return 0; + } else { + return 1; } } - + + // Task 5. Parity measurement in different basis - operation DifferentBasis_Reference (qs : Qubit[]) : Int - { - body - { - // The first state is a superposition of |++⟩ and |--⟩, - // the second one - of |+-⟩ and |-+⟩ - if (Measure([PauliX; PauliX], qs) == Zero) { - return 0; - } else { - return 1; - } + operation DifferentBasis_Reference (qs : Qubit[]) : Int { + // The first state is a superposition of |++⟩ and |--⟩, + // the second one - of |+-⟩ and |-+⟩ + if (Measure([PauliX, PauliX], qs) == Zero) { + return 0; + } else { + return 1; } } - + + // Task 6. Controlled X gate with |0⟩ target - operation ControlledX_Reference (qs : Qubit[]) : () - { - body - { - H(qs[1]); - if (Measure([PauliZ; PauliZ], qs) == One) { - X(qs[1]); - } + operation ControlledX_Reference (qs : Qubit[]) : Unit { + H(qs[1]); + if (Measure([PauliZ, PauliZ], qs) == One) { + X(qs[1]); } } - + + // Task 7*. Controlled X gate with arbitrary target - operation ControlledX_General_Reference (qs : Qubit[]) : () - { - body - { + operation ControlledX_General_Reference (qs : Qubit[]) : Unit { + + body (...) { // This implementation follows the description at https://arxiv.org/pdf/1201.5734.pdf. - // Note the parity notation used in the table of fixups in the paper + // Note the parity notation used in the table of fixups in the paper // differs from the notation used in Q#. using (ans = Qubit[1]) { let c = qs[0]; let a = ans[0]; let t = qs[1]; H(a); - let p1 = MeasureAllZ([c; a]); + let p1 = MeasureAllZ([c, a]); H(a); H(t); - let p2 = MeasureAllZ([a; t]); + let p2 = MeasureAllZ([a, t]); H(a); H(t); let m = M(a); + // apply fixups if (p2 == One) { Z(c); @@ -122,12 +105,15 @@ namespace Quantum.Kata.JointMeasurements if (p1 != m) { X(t); } + // reset ancilla qubit if (m == One) { X(a); } } } + adjoint self; } + } diff --git a/JointMeasurements/Tasks.qs b/JointMeasurements/Tasks.qs index 4d3bbc51..dcb97fc6 100644 --- a/JointMeasurements/Tasks.qs +++ b/JointMeasurements/Tasks.qs @@ -1,111 +1,97 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.JointMeasurements -{ +namespace Quantum.Kata.JointMeasurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Joint Measurements" quantum kata is a series of exercises designed + + // "Joint Measurements" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. - // It covers the joint parity measurements and using them for distinguishing quantum states + // It covers the joint parity measurements and using them for distinguishing quantum states // or for performing multi-qubit gates. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // - // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. - - + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + + // Task 1. Single-qubit measurement - // Input: Two qubits (stored in an array) which are guaranteed to be + // Input: Two qubits (stored in an array) which are guaranteed to be // either in superposition of states |00⟩ and |11⟩ // or in superposition of states |01⟩ and |10⟩. // Output: 0 if qubits were in the first superposition, // 1 if they were in the second superposition. // The state of the qubits at the end of the operation does not matter. - operation SingleQubitMeasurement (qs : Qubit[]) : Int - { - body - { - // Hint: Use two single-qubit measurements. - // ... - return -1; - } + operation SingleQubitMeasurement (qs : Qubit[]) : Int { + // Hint: Use two single-qubit measurements. + // ... + return -1; } - + + // Task 2. Parity measurement - // Input: Two qubits (stored in an array) which are guaranteed to be + // Input: Two qubits (stored in an array) which are guaranteed to be // either in superposition of states |00⟩ and |11⟩ // or in superposition of states |01⟩ and |10⟩. // Output: 0 if qubits were in the first superposition, // 1 if they were in the second superposition. // The state of the qubits at the end of the operation should be the same as the starting state. - operation ParityMeasurement (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation ParityMeasurement (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 3. |0000⟩ + |1111⟩ or |0011⟩ + |1100⟩ ? - // Input: Four qubits (stored in an array) which are guaranteed to be + // Input: Four qubits (stored in an array) which are guaranteed to be // either in superposition of states |0000⟩ and |1111⟩ // or in superposition of states |0011⟩ and |1100⟩. // Output: 0 if qubits were in the first superposition, // 1 if they were in the second superposition. // The state of the qubits at the end of the operation should be the same as the starting state. - operation GHZOrGHZWithX (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation GHZOrGHZWithX (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 4. |0..0⟩ + |1..1⟩ or W state ? - // Input: An even number of qubits (stored in an array) which are guaranteed to be + // Input: An even number of qubits (stored in an array) which are guaranteed to be // either in superposition of states |0..0⟩ and |1..1⟩ // or in W state ( https://en.wikipedia.org/wiki/W_state ). // Output: 0 if qubits were in W state, // 1 if they were in the second superposition. // The state of the qubits at the end of the operation should be the same as the starting state. - operation GHZOrWState (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation GHZOrWState (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 5. Parity measurement in different basis - // Input: Two qubits (stored in an array) which are guaranteed to be + // Input: Two qubits (stored in an array) which are guaranteed to be // either in superposition α|00⟩ + β|01⟩ + β|10⟩ + α|11⟩ // or in superposition α|00⟩ - β|01⟩ + β|10⟩ - α|11⟩. // Output: 0 if qubits were in the first superposition, // 1 if they were in the second superposition. // The state of the qubits at the end of the operation should be the same as the starting state. - operation DifferentBasis (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation DifferentBasis (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 6. Controlled X gate with |0⟩ target // Input: Two unentangled qubits (stored in an array of length 2). // The first qubit will be in state |ψ⟩ = α |0⟩ + β |1⟩, the second - in state |0⟩ @@ -113,26 +99,24 @@ namespace Quantum.Kata.JointMeasurements // Goal: Change the two-qubit state to α |00⟩ + β |11⟩ using only single-qubit gates and joint measurements. // Do not use two-qubit gates. // You do not need to allocate extra qubits. - operation ControlledX (qs : Qubit[]) : () - { - body - { - // ... - } + operation ControlledX (qs : Qubit[]) : Unit { + // ... } - + + // Task 7*. Controlled X gate with arbitrary target - // Input: Two qubits (stored in an array of length 2) in an arbitrary + // Input: Two qubits (stored in an array of length 2) in an arbitrary // two-qubit state α|00⟩ + β|01⟩ + γ|10⟩ + δ|11⟩. // Goal: Change the two-qubit state to α|00⟩ + β|01⟩ + δ|10⟩ + γ|11⟩ using only single-qubit gates and joint measurements. // Do not use two-qubit gates. - operation ControlledX_General (qs : Qubit[]) : () - { - body - { + operation ControlledX_General (qs : Qubit[]) : Unit { + + body (...) { // Hint: You can use an extra qubit to perform this operation. // ... } + adjoint self; } + } diff --git a/JointMeasurements/Tests.qs b/JointMeasurements/Tests.qs index 120dcbea..1303cf4b 100644 --- a/JointMeasurements/Tests.qs +++ b/JointMeasurements/Tests.qs @@ -1,227 +1,228 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.JointMeasurements -{ +namespace Quantum.Kata.JointMeasurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits // with Int return - operation DistinguishStates_MultiQubit ( - Nqubit : Int, - Nstate : Int, - statePrep : ((Qubit[], Int, Double) => () : Adjoint), - testImpl : (Qubit[] => Int), - preserveState : Bool - ) : () - { - body - { - let nTotal = 100; - mutable nOk = 0; - using (qs = Qubit[Nqubit]) - { - for (i in 1..nTotal) - { - // get a random integer to define the state of the qubits - let state = RandomInt(Nstate); - - // get a random rotation angle to define the exact state of the qubits - let alpha = RandomReal(5) * PI(); - - // do state prep: convert |0...0⟩ to outcome with return equal to state - statePrep(qs, state, alpha); + operation DistinguishStates_MultiQubit (Nqubit : Int, Nstate : Int, statePrep : ((Qubit[], Int, Double) => Unit : Adjoint), testImpl : (Qubit[] => Int), preserveState : Bool) : Unit { + let nTotal = 100; + mutable nOk = 0; - // get the solution's answer and verify that it's a match - let ans = testImpl(qs); - if (ans == state) { - set nOk = nOk + 1; - } - - if (preserveState) { - // check that the state of the qubit after the operation is unchanged - (Adjoint statePrep)(qs, state, alpha); - AssertAllZero(qs); - } else { - // we're not checking the state of the qubit after the operation - ResetAll(qs); - } + using (qs = Qubit[Nqubit]) { + for (i in 1 .. nTotal) { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // get a random rotation angle to define the exact state of the qubits + let alpha = RandomReal(5) * PI(); + + // do state prep: convert |0...0⟩ to outcome with return equal to state + statePrep(qs, state, alpha); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs); + if (ans == state) { + set nOk = nOk + 1; + } + + if (preserveState) { + // check that the state of the qubit after the operation is unchanged + Adjoint statePrep(qs, state, alpha); + AssertAllZero(qs); + } else { + // we're not checking the state of the qubit after the operation + ResetAll(qs); } } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } + + AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } - + + // ------------------------------------------------------ - operation StatePrep_ParityMeasurement (qs : Qubit[], state : Int, alpha : Double) : () { - body { + operation StatePrep_ParityMeasurement (qs : Qubit[], state : Int, alpha : Double) : Unit { + + body (...) { // prep cos(alpha) * |0..0⟩ + sin(alpha) * |1..1⟩ Ry(2.0 * alpha, qs[0]); - for (i in 1..Length(qs)-1) { + for (i in 1 .. Length(qs) - 1) { CNOT(qs[0], qs[i]); } + if (state == 1) { // flip the state of the last half of the qubits - for (i in 0..Length(qs)/2-1) { + for (i in 0 .. Length(qs) / 2 - 1) { X(qs[i]); } } } - adjoint auto; - } - - // ------------------------------------------------------ - operation T01_SingleQubitMeasurement_Test () : () { - body { - DistinguishStates_MultiQubit(2, 2, StatePrep_ParityMeasurement, SingleQubitMeasurement, false); - } - } - - // ------------------------------------------------------ - operation T02_ParityMeasurement_Test () : () { - body { - DistinguishStates_MultiQubit(2, 2, StatePrep_ParityMeasurement, ParityMeasurement, true); - } + + adjoint invert; } + // ------------------------------------------------------ - operation T03_GHZOrGHZWithX_Test () : () { - body { - DistinguishStates_MultiQubit(4, 2, StatePrep_ParityMeasurement, GHZOrGHZWithX, true); - } + operation T01_SingleQubitMeasurement_Test () : Unit { + DistinguishStates_MultiQubit(2, 2, StatePrep_ParityMeasurement, SingleQubitMeasurement, false); } - + + // ------------------------------------------------------ - operation StatePrep_WState_Arbitrary (qs : Qubit[]) : () - { - body - { + operation T02_ParityMeasurement_Test () : Unit { + DistinguishStates_MultiQubit(2, 2, StatePrep_ParityMeasurement, ParityMeasurement, true); + } + + + // ------------------------------------------------------ + operation T03_GHZOrGHZWithX_Test () : Unit { + DistinguishStates_MultiQubit(4, 2, StatePrep_ParityMeasurement, GHZOrGHZWithX, true); + } + + + // ------------------------------------------------------ + operation StatePrep_WState_Arbitrary (qs : Qubit[]) : Unit { + + body (...) { let N = Length(qs); + if (N == 1) { // base case of recursion: |1⟩ X(qs[0]); - } else { + } + else { // |W_N> = |0⟩|W_(N-1)> + |1⟩|0...0⟩ // do a rotation on the first qubit to split it into |0⟩ and |1⟩ with proper weights // |0⟩ -> sqrt((N-1)/N) |0⟩ + 1/sqrt(N) |1⟩ let theta = ArcSin(1.0 / Sqrt(ToDouble(N))); Ry(2.0 * theta, qs[0]); + // do a zero-controlled W-state generation for qubits 1..N-1 X(qs[0]); - (Controlled StatePrep_WState_Arbitrary)(qs[0..0], qs[1..N-1]); + Controlled StatePrep_WState_Arbitrary(qs[0 .. 0], qs[1 .. N - 1]); X(qs[0]); } } - adjoint auto; - controlled auto; - adjoint controlled auto; + + adjoint invert; + controlled distribute; + controlled adjoint distribute; } - - operation StatePrep_GHZOrWState (qs : Qubit[], state : Int, alpha : Double) : () { - body { + + + operation StatePrep_GHZOrWState (qs : Qubit[], state : Int, alpha : Double) : Unit { + + body (...) { if (state == 0) { StatePrep_ParityMeasurement(qs, 0, alpha); } else { StatePrep_WState_Arbitrary(qs); } } - adjoint auto; + + adjoint invert; } - - operation T04_GHZOrWState_Test () : () { - body { - for (i in 1..5) { - DistinguishStates_MultiQubit(2*i, 2, StatePrep_GHZOrWState, GHZOrWState, true); - } + + + operation T04_GHZOrWState_Test () : Unit { + for (i in 1 .. 5) { + DistinguishStates_MultiQubit(2 * i, 2, StatePrep_GHZOrWState, GHZOrWState, true); } } - + + // ------------------------------------------------------ - operation StatePrep_DifferentBasis (qs : Qubit[], state : Int, alpha : Double) : () { - body { + operation StatePrep_DifferentBasis (qs : Qubit[], state : Int, alpha : Double) : Unit { + + body (...) { // prep cos(alpha) * |00⟩ + sin(alpha) * |11⟩ Ry(2.0 * alpha, qs[0]); CNOT(qs[0], qs[1]); + if (state == 1) { X(qs[1]); } + // convert to X basis ApplyToEachA(H, qs); } - adjoint auto; + + adjoint invert; } - - operation T05_DifferentBasis_Test () : () { - body { - DistinguishStates_MultiQubit(2, 2, StatePrep_DifferentBasis, DifferentBasis, true); - } + + + operation T05_DifferentBasis_Test () : Unit { + DistinguishStates_MultiQubit(2, 2, StatePrep_DifferentBasis, DifferentBasis, true); } - + + // ------------------------------------------------------ // prepare state |A⟩ = cos(α) * |0⟩ + sin(α) * |1⟩ - operation StatePrep_A (alpha : Double, q : Qubit) : () { - body { + operation StatePrep_A (alpha : Double, q : Qubit) : Unit { + + body (...) { Ry(2.0 * alpha, q); } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ - operation T06_ControlledX_Test () : () - { - body - { - // Note that the way the problem is formulated, we can't just compare two unitaries, - // we need to create an input state |A⟩ and check that the output state is correct - using (qs = Qubit[2]) - { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - - // prepare A state - StatePrep_A(alpha, qs[0]); - - // apply operation that needs to be tested - ControlledX(qs); - - // apply adjoint reference operation and adjoint of state prep - CNOT(qs[0], qs[1]); - (Adjoint StatePrep_A)(alpha, qs[0]); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + operation T06_ControlledX_Test () : Unit { + // Note that the way the problem is formulated, we can't just compare two unitaries, + // we need to create an input state |A⟩ and check that the output state is correct + using (qs = Qubit[2]) { + + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + + // prepare A state + StatePrep_A(alpha, qs[0]); + + // apply operation that needs to be tested + ControlledX(qs); + + // apply adjoint reference operation and adjoint of state prep + CNOT(qs[0], qs[1]); + Adjoint StatePrep_A(alpha, qs[0]); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } } - + + // ------------------------------------------------------ - operation CNOTWrapper (qs : Qubit[]) : () - { - body - { + operation CNOTWrapper (qs : Qubit[]) : Unit { + + body (...) { CNOT(qs[0], qs[1]); } + adjoint self; } - - operation T07_ControlledX_General_Test () : () - { - body - { - // In this task the gate is supposed to work on all inputs, so we can compare the unitary to CNOT. - AssertOperationsEqualReferenced(CNOTWrapper, ControlledX_General_Reference, 2); - AssertOperationsEqualReferenced(ControlledX_General, ControlledX_General_Reference, 2); - } + + + operation T07_ControlledX_General_Test () : Unit { + // In this task the gate is supposed to work on all inputs, so we can compare the unitary to CNOT. + AssertOperationsEqualReferenced(CNOTWrapper, ControlledX_General_Reference, 2); + AssertOperationsEqualReferenced(ControlledX_General, ControlledX_General_Reference, 2); } -} \ No newline at end of file + +} diff --git a/Measurements/Measurements.csproj b/Measurements/Measurements.csproj index a5ae8efc..44f2ee81 100644 --- a/Measurements/Measurements.csproj +++ b/Measurements/Measurements.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.Measurements - - - + + + diff --git a/Measurements/ReferenceImplementation.qs b/Measurements/ReferenceImplementation.qs index 1f3f5624..f43b27a9 100644 --- a/Measurements/ReferenceImplementation.qs +++ b/Measurements/ReferenceImplementation.qs @@ -1,132 +1,116 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Measurements -{ +namespace Quantum.Kata.Measurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - + open Microsoft.Quantum.Extensions.Math; + + ////////////////////////////////////////////////////////////////// // Part I. Single-Qubit Measurements ////////////////////////////////////////////////////////////////// - + // Task 1.1. |0⟩ or |1⟩ ? // Input: a qubit which is guaranteed to be in |0⟩ or |1⟩ state. // Output: true if qubit was in |1⟩ state, or false if it was in |0⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitOne_Reference (q : Qubit) : Bool - { - body - { - let res = M(q); - return res == One; - } + operation IsQubitOne_Reference (q : Qubit) : Bool { + let res = M(q); + return res == One; } - + + // Task 1.2. Set qubit to |0⟩ state - operation InitializeQubit_Reference (q : Qubit) : () - { - body - { - if (M(q) == One) { - X(q); - } - // Note: this can be accomplished using Reset library operation. - // Reset(q); + operation InitializeQubit_Reference (q : Qubit) : Unit { + if (M(q) == One) { + X(q); } } - + + // Task 1.3. |+⟩ or |-⟩ ? // Input: a qubit which is guaranteed to be in |+⟩ or |-⟩ state // (|+⟩ = (|0⟩ + |1⟩) / sqrt(2), |-⟩ = (|0⟩ - |1⟩) / sqrt(2)). // Output: true if qubit was in |+⟩ state, or false if it was in |-⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitPlus_Reference (q : Qubit) : Bool - { - body - { - H(q); - let res = M(q); - return res == Zero; - } + operation IsQubitPlus_Reference (q : Qubit) : Bool { + H(q); + let res = M(q); + return res == Zero; } - + + // Task 1.4. |A⟩ or |B⟩ ? // Inputs: // 1) angle alpha, in radians, represented as Double // 2) a qubit which is guaranteed to be in |A⟩ or |B⟩ state - // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, + // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, // |B⟩ = - sin(alpha) * |0⟩ + cos(alpha) * |1⟩. // Output: true if qubit was in |A⟩ state, or false if it was in |B⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitA_Reference (alpha : Double, q : Qubit) : Bool - { - body - { - // |0⟩ is converted into |A⟩ and |1⟩ into |B⟩ by Ry(2.0 * alpha) - // so |A⟩ is converted into |0⟩ by the opposite rotation - Ry(- 2.0 * alpha, q); - let res = M(q); - return res == Zero; - } + operation IsQubitA_Reference (alpha : Double, q : Qubit) : Bool { + // |0⟩ is converted into |A⟩ and |1⟩ into |B⟩ by Ry(2.0 * alpha) + // so |A⟩ is converted into |0⟩ by the opposite rotation + Ry(-2.0 * alpha, q); + let res = M(q); + return res == Zero; } - + + // Task 1.5. |00⟩ or |11⟩ ? // Input: two qubits (stored in an array) which are guaranteed to be in |00⟩ or |11⟩ state. // Output: 0 if qubits were in |00⟩ state, // 1 if they were in |11⟩ state. // The state of the qubits at the end of the operation does not matter. - operation ZeroZeroOrOneOne_Reference (qs : Qubit[]) : Int - { - body - { - // it's enough to do one measurement on any qubit - let res = M(qs[0]); - if (res == Zero) { - return 0; - } else { - return 1; - } + operation ZeroZeroOrOneOne_Reference (qs : Qubit[]) : Int { + // it's enough to do one measurement on any qubit + let res = M(qs[0]); + + if (res == Zero) { + return 0; + } else { + return 1; } } - + + // Task 1.6. Distinguish four basis states - // Input: two qubits (stored in an array) which are guaranteed to be + // Input: two qubits (stored in an array) which are guaranteed to be // in one of the four basis states (|00⟩, |01⟩, |10⟩ or |11⟩). // Output: 0 if qubits were in |00⟩ state, // 1 if they were in |01⟩ state, // 2 if they were in |10⟩ state, // 3 if they were in |11⟩ state. // The state of the qubits at the end of the operation does not matter. - operation BasisStateMeasurement_Reference (qs : Qubit[]) : Int - { - body - { - // measurement on the first qubit gives the higher bit of the answer, on the second - the lower - mutable m1 = 0; - if (M(qs[0]) == One) { - set m1 = 1; - } - mutable m2 = 0; - if (M(qs[1]) == One) { - set m2 = 1; - } - return m1 * 2 + m2; + operation BasisStateMeasurement_Reference (qs : Qubit[]) : Int { + // measurement on the first qubit gives the higher bit of the answer, on the second - the lower + mutable m1 = 0; + if (M(qs[0]) == One) { + set m1 = 1; } + + mutable m2 = 0; + if (M(qs[1]) == One) { + set m2 = 1; + } + + return m1 * 2 + m2; } - + + // Task 1.7. Distinguish two basis states given by bit strings // Inputs: - // 1) N qubits (stored in an array) which are guaranteed to be + // 1) N qubits (stored in an array) which are guaranteed to be // in one of the two basis states described by the given bit strings. // 2) two bit string represented as Bool[]s. // Output: 0 if qubits were in the basis state described by the first bit string, @@ -136,95 +120,92 @@ namespace Quantum.Kata.Measurements // You are guaranteed that the both bit strings have the same length as the qubit array, // and that the bit strings will differ in at least one bit. // You can use exactly one measurement. - // Example: for bit strings [false; true; false] and [false; false; true] + // Example: for bit strings [false, true, false] and [false, false, true] // return 0 corresponds to state |010⟩, and return 1 corresponds to state |001⟩. - - function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int - { + function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int { + mutable firstDiff = -1; - for (i in 0 .. Length(bits1)-1) { + for (i in 0 .. Length(bits1) - 1) { if (bits1[i] != bits2[i] && firstDiff == -1) { set firstDiff = i; } } + return firstDiff; } - - operation TwoBitstringsMeasurement_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int - { - body - { - // find the first index at which the bit strings are different and measure it - let firstDiff = FindFirstDiff_Reference(bits1, bits2); - let res = (M(qs[firstDiff]) == One); - if (res == bits1[firstDiff]) { - return 0; - } else { - return 1; - } + + + operation TwoBitstringsMeasurement_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int { + // find the first index at which the bit strings are different and measure it + let firstDiff = FindFirstDiff_Reference(bits1, bits2); + let res = M(qs[firstDiff]) == One; + + if (res == bits1[firstDiff]) { + return 0; + } else { + return 1; } } - + + // Task 1.8. |0...0⟩ state or W state ? - // Input: N qubits (stored in an array) which are guaranteed to be + // Input: N qubits (stored in an array) which are guaranteed to be // either in |0...0⟩ state // or in W state (https://en.wikipedia.org/wiki/W_state). // Output: 0 if qubits were in |0...0⟩ state, // 1 if they were in W state. // The state of the qubits at the end of the operation does not matter. - operation AllZerosOrWState_Reference (qs : Qubit[]) : Int - { - body - { - // measure all qubits; if there is exactly one One, it's W state, if there are no Ones, it's |0...0⟩ - // (and there should never be two or more Ones) - mutable countOnes = 0; - for (i in 0..Length(qs)-1) { - if (M(qs[i]) == One) { - set countOnes = countOnes + 1; - } + operation AllZerosOrWState_Reference (qs : Qubit[]) : Int { + // measure all qubits; if there is exactly one One, it's W state, if there are no Ones, it's |0...0⟩ + // (and there should never be two or more Ones) + mutable countOnes = 0; + + for (i in 0 .. Length(qs) - 1) { + if (M(qs[i]) == One) { + set countOnes = countOnes + 1; } - if (countOnes > 1) { - fail "Impossible to get multiple Ones when measuring W state"; - } - if (countOnes == 0) { - return 0; - } - return 1; } + + if (countOnes > 1) { + fail "Impossible to get multiple Ones when measuring W state"; + } + if (countOnes == 0) { + return 0; + } + return 1; } - + + // Task 1.9. GHZ state or W state ? - // Input: N >= 2 qubits (stored in an array) which are guaranteed to be + // Input: N >= 2 qubits (stored in an array) which are guaranteed to be // either in GHZ state (https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) // or in W state (https://en.wikipedia.org/wiki/W_state). // Output: 0 if qubits were in GHZ state, // 1 if they were in W state. // The state of the qubits at the end of the operation does not matter. - operation GHZOrWState_Reference (qs : Qubit[]) : Int - { - body - { - // measure all qubits; if there is exactly one One, it's W state, - // if there are no Ones or all are Ones, it's GHZ - // (and there should never be a different number of Ones) - let N = Length(qs); - mutable countOnes = 0; - for (i in 0..N-1) { - if (M(qs[i]) == One) { - set countOnes = countOnes + 1; - } + operation GHZOrWState_Reference (qs : Qubit[]) : Int { + // measure all qubits; if there is exactly one One, it's W state, + // if there are no Ones or all are Ones, it's GHZ + // (and there should never be a different number of Ones) + let N = Length(qs); + mutable countOnes = 0; + + for (i in 0 .. N - 1) { + if (M(qs[i]) == One) { + set countOnes = countOnes + 1; } - if (countOnes > 1 && countOnes < Length(qs)) { - fail $"Impossible to get {countOnes} Ones when measuring W state or GHZ state on {N} qubits"; - } - if (countOnes == 1) { - return 1; - } - return 0; } + + if (countOnes > 1 && countOnes < Length(qs)) { + fail $"Impossible to get {countOnes} Ones when measuring W state or GHZ state on {N} qubits"; + } + if (countOnes == 1) { + return 1; + } + return 0; } - + + // Task 1.10. Distinguish four Bell states // Input: two qubits (stored in an array) which are guaranteed to be in one of the four Bell states: // |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) @@ -236,26 +217,26 @@ namespace Quantum.Kata.Measurements // 2 if they were in |Ψ⁺⟩ state, // 3 if they were in |Ψ⁻⟩ state. // The state of the qubits at the end of the operation does not matter. - operation BellState_Reference (qs : Qubit[]) : Int - { - body - { - H(qs[0]); - H(qs[1]); - CNOT(qs[1], qs[0]); - H(qs[1]); - mutable m1 = 0; - if (M(qs[0]) == One) { - set m1 = 1; - } - mutable m2 = 0; - if (M(qs[1]) == One) { - set m2 = 1; - } - return m2 * 2 + m1; - } - } + operation BellState_Reference (qs : Qubit[]) : Int { + H(qs[0]); + H(qs[1]); + CNOT(qs[1], qs[0]); + H(qs[1]); + mutable m1 = 0; + if (M(qs[0]) == One) { + set m1 = 1; + } + + mutable m2 = 0; + if (M(qs[1]) == One) { + set m2 = 1; + } + + return m2 * 2 + m1; + } + + // Task 1.11*. Distinguish four orthogonal 2-qubit states // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: // |S0⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2 @@ -267,19 +248,16 @@ namespace Quantum.Kata.Measurements // 2 if they were in |S2⟩ state, // 3 if they were in |S3⟩ state. // The state of the qubits at the end of the operation does not matter. - operation TwoQubitState_Reference (qs : Qubit[]) : Int - { - body - { - // These states are produced by H ⊗ H, applied to four basis states. - // To measure them, apply H ⊗ H followed by basis state measurement - // implemented in BasisStateMeasurement_Reference. - H(qs[0]); - H(qs[1]); - return BasisStateMeasurement_Reference(qs); - } + operation TwoQubitState_Reference (qs : Qubit[]) : Int { + // These states are produced by H ⊗ H, applied to four basis states. + // To measure them, apply H ⊗ H followed by basis state measurement + // implemented in BasisStateMeasurement_Reference. + H(qs[0]); + H(qs[1]); + return BasisStateMeasurement_Reference(qs); } + // Task 1.12**. Distinguish four orthogonal 2-qubit states, part two // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: // |S0⟩ = ( |00⟩ - |01⟩ - |10⟩ - |11⟩) / 2 @@ -293,69 +271,69 @@ namespace Quantum.Kata.Measurements // The state of the qubits at the end of the operation does not matter. // Helper function to implement diag(-1, 1, 1, 1) - operation ApplyDiag (qs : Qubit[]) : () - { - body - { - ApplyToEach(X, qs); - (Controlled Z)([qs[0]], qs[1]); - ApplyToEach(X, qs); + operation ApplyDiag (qs : Qubit[]) : Unit { + + body (...) { + ApplyToEach(X, qs); + Controlled Z([qs[0]], qs[1]); + ApplyToEach(X, qs); } - adjoint self + + adjoint self; } + // The actual reference implementation for Task 1.11 - operation TwoQubitStatePartTwo_Reference (qs : Qubit[]) : Int - { - body - { - // Observe that the unitary matrix A formed by the columns |S0⟩, ..., |S3⟩ - // is up to permutations matrices and diagonal +1/-1 matrices equal to the - // tensor product H ⊗ H when multiplied from the left and the right. - // Specifically, A = diag(-1, 1, 1, 1) (H ⊗ H) diag(-1, 1, 1, 1) pi, - // where pi is the permutation (1,2) corresponding to a swap of 2 qubits. - SWAP(qs[0], qs[1]); // pi - With(ApplyDiag, ApplyToEach(H, _), qs); // diag(..) (H ⊗ H) diag(..) - return BasisStateMeasurement_Reference(qs); - } + operation TwoQubitStatePartTwo_Reference (qs : Qubit[]) : Int { + + // Observe that the unitary matrix A formed by the columns |S0⟩, ..., |S3⟩ + // is up to permutations matrices and diagonal +1/-1 matrices equal to the + // tensor product H ⊗ H when multiplied from the left and the right. + // Specifically, A = diag(-1, 1, 1, 1) (H ⊗ H) diag(-1, 1, 1, 1) pi, + // where pi is the permutation (1,2) corresponding to a swap of 2 qubits. + + // Apply permutation pi + SWAP(qs[0], qs[1]); + + // Apply diag(..) (H ⊗ H) diag(..) + With(ApplyDiag, ApplyToEach(H, _), qs); + return BasisStateMeasurement_Reference(qs); } - - + + ////////////////////////////////////////////////////////////////// // Part II*. Discriminating Nonorthogonal States ////////////////////////////////////////////////////////////////// - + // Task 2.1*. |0⟩ or |+⟩ ? // (quantum hypothesis testing or state discrimination with minimum error) // Input: a qubit which is guaranteed to be in |0⟩ or |+⟩ state with equal probability. // Output: true if qubit was in |0⟩ state, or false if it was in |+⟩ state. // The state of the qubit at the end of the operation does not matter. // Note: in this task you have to get accuracy of at least 80%. - operation IsQubitPlusOrZero_Reference (q : Qubit) : Bool - { - body - { - // Let {E_a, E_b} be a measurement with two outcomes a and b, which we identify with - // the answers, i.e., "a" = "state was |0⟩" and "b = state was |+⟩". Then we define - // P(a|0) = probability to observe first outcome given that the state was |0⟩ - // P(b|0) = probability to observe second outcome given that the state was |0⟩ - // P(a|+) = probability to observe first outcome given that the state was |+⟩ - // P(b|+) = probability to observe second outcome given that the state was |+⟩ - // the task is to maximize the probability to be correct on a single shot experiment - // which is the same as to minimize the probability to be wrong (on a single shot). - // Assuming uniform prior, i.e., P(+) = P(0) = 1/2, we get - // P_correct = P(0) P(a|0) + P(+) P(b|+). Assuming a von Neumann measurement of the - // form E_a = Ry(2*alpha) * (1,0) = (cos(alpha), sin(alpha)) and - // E_b = Ry(2*alpha) * (0,1) = (sin(alpha), -cos(alpha)), we get that - // P_correct = 1/2 + cos²(alpha) + cos(alpha) sin(alpha). Maximizing this for alpha, - // we get max P_success = 1/2 (1 + 1/sqrt(2)) = 0.8535.., which is attained for alpha = π/8. - - // Rotate the input state by π/8 means to apply Ry with angle 2π/8. - Ry(0.25*PI(), q); - return (M(q) == Zero); - } + operation IsQubitPlusOrZero_Reference (q : Qubit) : Bool { + + // Let {E_a, E_b} be a measurement with two outcomes a and b, which we identify with + // the answers, i.e., "a" = "state was |0⟩" and "b = state was |+⟩". Then we define + // P(a|0) = probability to observe first outcome given that the state was |0⟩ + // P(b|0) = probability to observe second outcome given that the state was |0⟩ + // P(a|+) = probability to observe first outcome given that the state was |+⟩ + // P(b|+) = probability to observe second outcome given that the state was |+⟩ + // the task is to maximize the probability to be correct on a single shot experiment + // which is the same as to minimize the probability to be wrong (on a single shot). + // Assuming uniform prior, i.e., P(+) = P(0) = 1/2, we get + // P_correct = P(0) P(a|0) + P(+) P(b|+). Assuming a von Neumann measurement of the + // form E_a = Ry(2*alpha) * (1,0) = (cos(alpha), sin(alpha)) and + // E_b = Ry(2*alpha) * (0,1) = (sin(alpha), -cos(alpha)), we get that + // P_correct = 1/2 + cos²(alpha) + cos(alpha) sin(alpha). Maximizing this for alpha, + // we get max P_success = 1/2 (1 + 1/sqrt(2)) = 0.8535.., which is attained for alpha = π/8. + + // Rotate the input state by π/8 means to apply Ry with angle 2π/8. + Ry(0.25 * PI(), q); + return M(q) == Zero; } - + + // Task 2.2**. |0⟩, |+⟩ or inconclusive? // (unambiguous state discrimination) // Input: a qubit which is guaranteed to be in |0⟩ or |+⟩ state with equal probability. @@ -364,64 +342,63 @@ namespace Quantum.Kata.Measurements // -1 if you can't decide, i.e., an "inconclusive" result. // Your solution: // - can never give 0 or 1 answer incorrectly (i.e., identify |0⟩ as 1 or |+⟩ as 0). - // - must give inconclusive (-1) answer at most 80% of the times. + // - must give inconclusive (-1) answer at most 80% of the times. // - must correctly identify |0⟩ state as 0 at least 10% of the times. // - must correctly identify |+⟩ state as 1 at least 10% of the times. // // The state of the qubit at the end of the operation does not matter. - // You are allowed to use ancilla qubit(s). - operation IsQubitPlusZeroOrInconclusiveSimpleUSD_Reference (q : Qubit) : Int - { - body - { - // A simple strategy that gives an inconclusive result with probability 0.75 - // and never errs in case it yields a conclusive result can be obtained from - // randomizing the choice of measurement basis between the computational basis (std) - // and the Hadamard basis (had). Observe that when measured in the standard basis, - // the state |0⟩ will always lead to the outcome "0", whereas the state |+⟩ - // will lead to outcomes "0" respectively "1" with probability 1/2. This means - // that upon measuring "1" we can with certainty conclude that the state was |+⟩. - // A similar argument applies to the scenario where we measure in the Hadamard - // basis, where |0⟩ can lead to both outcomes, whereas |+⟩ always leads to "0". - // Then upon measuring "1" we can with certainty conclude that the state was |0⟩. - // - // This leads to the following scenarios (shown are the conditional probabilities - // of the above scenarios and resulting answers). - // state | basis | output 0 | output 1 | output -1 - // ----------------------------------------------- - // |0⟩ | std | 0 | 0 | 1 - // |+⟩ | std | 0 | 1/2 | 1/2 - // |0⟩ | had | 1/2 | 0 | 1/2 - // |+⟩ | had | 0 | 0 | 1 + // You are allowed to use ancilla qubit(s). + operation IsQubitPlusZeroOrInconclusiveSimpleUSD_Reference (q : Qubit) : Int { + + // A simple strategy that gives an inconclusive result with probability 0.75 + // and never errs in case it yields a conclusive result can be obtained from + // randomizing the choice of measurement basis between the computational basis (std) + // and the Hadamard basis (had). Observe that when measured in the standard basis, + // the state |0⟩ will always lead to the outcome "0", whereas the state |+⟩ + // will lead to outcomes "0" respectively "1" with probability 1/2. This means + // that upon measuring "1" we can with certainty conclude that the state was |+⟩. + // A similar argument applies to the scenario where we measure in the Hadamard + // basis, where |0⟩ can lead to both outcomes, whereas |+⟩ always leads to "0". + // Then upon measuring "1" we can with certainty conclude that the state was |0⟩. + // + // This leads to the following scenarios (shown are the conditional probabilities + // of the above scenarios and resulting answers). + // state | basis | output 0 | output 1 | output -1 + // ----------------------------------------------- + // |0⟩ | std | 0 | 0 | 1 + // |+⟩ | std | 0 | 1/2 | 1/2 + // |0⟩ | had | 1/2 | 0 | 1/2 + // |+⟩ | had | 0 | 0 | 1 + mutable output = 0; + let basis = RandomInt(2); + + // randomize over std and had + if (basis == 0) { - mutable output = 0; - let basis = RandomInt(2); - // randomize over std and had - - if (basis == 0) { - // use standard basis - let result = M(q); - if (result == One) { - // this can only arise if the state was |+⟩ - set output = 1; - } - else { - set output = -1; - } + // use standard basis + let result = M(q); + if (result == One) { + // this can only arise if the state was |+⟩ + set output = 1; } - else { - // use Hadamard basis - H(q); - let result = M(q); - if (result == One) { - // this can only arise if the state was |0⟩ - set output = 0; - } - else { - set output = -1; - } + else { + set output = -1; } - return output; } + else { + // use Hadamard basis + H(q); + let result = M(q); + if (result == One) { + // this can only arise if the state was |0⟩ + set output = 0; + } + else { + set output = -1; + } + } + + return output; } + } diff --git a/Measurements/Tasks.qs b/Measurements/Tasks.qs index 2d6bee84..9f9d0384 100644 --- a/Measurements/Tasks.qs +++ b/Measurements/Tasks.qs @@ -1,126 +1,109 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.Measurements -{ +namespace Quantum.Kata.Measurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Measurements" quantum kata is a series of exercises designed + + // "Measurements" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. // It covers the following topics: // - single-qubit measurements, // - discriminating orthogonal and nonorthogonal states. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // - // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. - + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + ////////////////////////////////////////////////////////////////// // Part I. Single-Qubit Measurements ////////////////////////////////////////////////////////////////// - + // Task 1.1. |0⟩ or |1⟩ ? // Input: a qubit which is guaranteed to be in |0⟩ or |1⟩ state. // Output: true if qubit was in |1⟩ state, or false if it was in |0⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitOne (q : Qubit) : Bool - { - body - { - // ... - return false; - } + operation IsQubitOne (q : Qubit) : Bool { + // ... + return false; } - + + // Task 1.2. Set qubit to |0⟩ state // Input: a qubit in an arbitrary state. // Goal: change the state of the qubit to |0⟩. - operation InitializeQubit (q : Qubit) : () - { - body - { - // ... - } + operation InitializeQubit (q : Qubit) : Unit { + // ... } - + + // Task 1.3. |+⟩ or |-⟩ ? // Input: a qubit which is guaranteed to be in |+⟩ or |-⟩ state // (|+⟩ = (|0⟩ + |1⟩) / sqrt(2), |-⟩ = (|0⟩ - |1⟩) / sqrt(2)). // Output: true if qubit was in |+⟩ state, or false if it was in |-⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitPlus (q : Qubit) : Bool - { - body - { - // ... - return false; - } + operation IsQubitPlus (q : Qubit) : Bool { + // ... + return false; } - + + // Task 1.4. |A⟩ or |B⟩ ? // Inputs: // 1) angle alpha, in radians, represented as Double // 2) a qubit which is guaranteed to be in |A⟩ or |B⟩ state - // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, + // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, // |B⟩ = - sin(alpha) * |0⟩ + cos(alpha) * |1⟩. // Output: true if qubit was in |A⟩ state, or false if it was in |B⟩ state. // The state of the qubit at the end of the operation does not matter. - operation IsQubitA (alpha : Double, q : Qubit) : Bool - { - body - { - // ... - return false; - } + operation IsQubitA (alpha : Double, q : Qubit) : Bool { + // ... + return false; } - + + // Task 1.5. |00⟩ or |11⟩ ? // Input: two qubits (stored in an array) which are guaranteed to be in |00⟩ or |11⟩ state. // Output: 0 if qubits were in |00⟩ state, // 1 if they were in |11⟩ state. // The state of the qubits at the end of the operation does not matter. - operation ZeroZeroOrOneOne (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation ZeroZeroOrOneOne (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 1.6. Distinguish four basis states - // Input: two qubits (stored in an array) which are guaranteed to be + // Input: two qubits (stored in an array) which are guaranteed to be // in one of the four basis states (|00⟩, |01⟩, |10⟩ or |11⟩). // Output: 0 if qubits were in |00⟩ state, // 1 if they were in |01⟩ state, // 2 if they were in |10⟩ state, // 3 if they were in |11⟩ state. - // In this task and the subsequent ones the order of qubit states + // In this task and the subsequent ones the order of qubit states // in task description matches the order of qubits in the array // (i.e., |10⟩ state corresponds to qs[0] in state |1⟩ and qs[1] in state |0⟩). // The state of the qubits at the end of the operation does not matter. - operation BasisStateMeasurement (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation BasisStateMeasurement (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 1.7. Distinguish two basis states given by bit strings // Inputs: - // 1) N qubits (stored in an array) which are guaranteed to be + // 1) N qubits (stored in an array) which are guaranteed to be // in one of the two basis states described by the given bit strings. // 2) two bit string represented as Bool[]s. // Output: 0 if qubits were in the basis state described by the first bit string, @@ -130,49 +113,40 @@ namespace Quantum.Kata.Measurements // You are guaranteed that the both bit strings have the same length as the qubit array, // and that the bit strings will differ in at least one bit. // You can use exactly one measurement. - // Example: for bit strings [false; true; false] and [false; false; true] + // Example: for bit strings [false, true, false] and [false, false, true] // return 0 corresponds to state |010⟩, and return 1 corresponds to state |001⟩. - operation TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int - { - body - { - // ... - return -1; - } + operation TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int { + // ... + return -1; } - + + // Task 1.8. |0...0⟩ state or W state ? - // Input: N qubits (stored in an array) which are guaranteed to be + // Input: N qubits (stored in an array) which are guaranteed to be // either in |0...0⟩ state // or in W state (https://en.wikipedia.org/wiki/W_state). // Output: 0 if qubits were in |0...0⟩ state, // 1 if they were in W state. // The state of the qubits at the end of the operation does not matter. - operation AllZerosOrWState (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation AllZerosOrWState (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 1.9. GHZ state or W state ? - // Input: N >= 2 qubits (stored in an array) which are guaranteed to be + // Input: N >= 2 qubits (stored in an array) which are guaranteed to be // either in GHZ state (https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) // or in W state (https://en.wikipedia.org/wiki/W_state). // Output: 0 if qubits were in GHZ state, // 1 if they were in W state. // The state of the qubits at the end of the operation does not matter. - operation GHZOrWState (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation GHZOrWState (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 1.10. Distinguish four Bell states // Input: two qubits (stored in an array) which are guaranteed to be in one of the four Bell states: // |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) @@ -184,17 +158,14 @@ namespace Quantum.Kata.Measurements // 2 if they were in |Ψ⁺⟩ state, // 3 if they were in |Ψ⁻⟩ state. // The state of the qubits at the end of the operation does not matter. - operation BellState (qs : Qubit[]) : Int - { - body - { - // Hint: you need to use 2-qubit gates to solve this task - - // ... - return -1; - } + operation BellState (qs : Qubit[]) : Int { + // Hint: you need to use 2-qubit gates to solve this task + + // ... + return -1; } - + + // Task 1.11*. Distinguish four orthogonal 2-qubit states // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: // |S0⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2 @@ -206,15 +177,12 @@ namespace Quantum.Kata.Measurements // 2 if they were in |S2⟩ state, // 3 if they were in |S3⟩ state. // The state of the qubits at the end of the operation does not matter. - operation TwoQubitState (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation TwoQubitState (qs : Qubit[]) : Int { + // ... + return -1; } - + + // Task 1.12**. Distinguish four orthogonal 2-qubit states, part two // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: // |S0⟩ = ( |00⟩ - |01⟩ - |10⟩ - |11⟩) / 2 @@ -226,41 +194,34 @@ namespace Quantum.Kata.Measurements // 2 if they were in |S2⟩ state, // 3 if they were in |S3⟩ state. // The state of the qubits at the end of the operation does not matter. - operation TwoQubitStatePartTwo (qs : Qubit[]) : Int - { - body - { - // ... - return -1; - } + operation TwoQubitStatePartTwo (qs : Qubit[]) : Int { + // ... + return -1; } - - + + ////////////////////////////////////////////////////////////////// // Part II*. Discriminating Nonorthogonal States ////////////////////////////////////////////////////////////////// - + // The solutions for tasks in this section are validated using the following method. - // The solution is called on N input states, each of which is picked randomly, + // The solution is called on N input states, each of which is picked randomly, // with all possible input states equally likely to be generated. - // The accuracy of state discrimination is estimated as an average of + // The accuracy of state discrimination is estimated as an average of // discrimination correctness over all input states. - + // Task 2.1*. |0⟩ or |+⟩ ? // (quantum hypothesis testing or state discrimination with minimum error) // Input: a qubit which is guaranteed to be in |0⟩ or |+⟩ state with equal probability. // Output: true if qubit was in |0⟩ state, or false if it was in |+⟩ state. // The state of the qubit at the end of the operation does not matter. // Note: in this task you have to get accuracy of at least 80%. - operation IsQubitPlusOrZero (q : Qubit) : Bool - { - body - { - // ... - return true; - } + operation IsQubitPlusOrZero (q : Qubit) : Bool { + // ... + return true; } - + + // Task 2.2**. |0⟩, |+⟩ or inconclusive? // (unambiguous state discrimination) // Input: a qubit which is guaranteed to be in |0⟩ or |+⟩ state with equal probability. @@ -269,18 +230,15 @@ namespace Quantum.Kata.Measurements // -1 if you can't decide, i.e., an "inconclusive" result. // Your solution: // - can never give 0 or 1 answer incorrectly (i.e., identify |0⟩ as 1 or |+⟩ as 0). - // - must give inconclusive (-1) answer at most 80% of the times. + // - must give inconclusive (-1) answer at most 80% of the times. // - must correctly identify |0⟩ state as 0 at least 10% of the times. // - must correctly identify |+⟩ state as 1 at least 10% of the times. // // The state of the qubit at the end of the operation does not matter. - // You are allowed to use ancilla qubit(s). - operation IsQubitPlusZeroOrInconclusiveSimpleUSD (q : Qubit) : Int - { - body - { - // ... - return -2; - } + // You are allowed to use ancilla qubit(s). + operation IsQubitPlusZeroOrInconclusiveSimpleUSD (q : Qubit) : Int { + // ... + return -2; } + } diff --git a/Measurements/Tests.qs b/Measurements/Tests.qs index 8ccf686f..f1ef0780 100644 --- a/Measurements/Tests.qs +++ b/Measurements/Tests.qs @@ -1,284 +1,254 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Measurements -{ +namespace Quantum.Kata.Measurements { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + ////////////////////////////////////////////////////////////////// - + // "Framework" operation for testing single-qubit tasks for distinguishing states of one qubit // with Bool return - operation DistinguishTwoStates_OneQubit ( - statePrep : ((Qubit, Int) => ()), - testImpl : (Qubit => Bool) - ) : () - { - body - { - let nTotal = 100; - mutable nOk = 0; - using (qs = Qubit[1]) - { - for (i in 1..nTotal) - { - // get a random bit to define whether qubit will be in a state corresponding to true return (1) or to false one (0) - // state = 0 false return - // state = 1 true return - let state = RandomIntPow2(1); - - // do state prep: convert |0⟩ to outcome with false return or to outcome with true return depending on state - statePrep(qs[0], state); + operation DistinguishTwoStates_OneQubit (statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Bool)) : Unit { + let nTotal = 100; + mutable nOk = 0; - // get the solution's answer and verify that it's a match - let ans = testImpl(qs[0]); - if (ans == (state == 1)) { - set nOk = nOk + 1; - } - - // we're not checking the state of the qubit after the operation - Reset(qs[0]); + using (qs = Qubit[1]) { + for (i in 1 .. nTotal) { + // get a random bit to define whether qubit will be in a state corresponding to true return (1) or to false one (0) + // state = 0 false return + // state = 1 true return + let state = RandomIntPow2(1); + + // do state prep: convert |0⟩ to outcome with false return or to outcome with true return depending on state + statePrep(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + if (ans == (state == 1)) { + set nOk = nOk + 1; } + + // we're not checking the state of the qubit after the operation + Reset(qs[0]); } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + } + + AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + } + + + // ------------------------------------------------------ + operation StatePrep_IsQubitOne (q : Qubit, state : Int) : Unit { + if (state == 0) { + // convert |0⟩ to |0⟩ + } else { + // convert |0⟩ to |1⟩ + X(q); } } - + + + operation T101_IsQubitOne_Test () : Unit { + DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitOne); + } + + // ------------------------------------------------------ - operation StatePrep_IsQubitOne (q : Qubit, state : Int) : () { - body { - if (state == 0) { - // convert |0⟩ to |0⟩ - } else { - // convert |0⟩ to |1⟩ - X(q); + operation T102_InitializeQubit_Test () : Unit { + using (qs = Qubit[1]) { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + Ry(2.0 * alpha, qs[0]); + + // Test Task 1 + InitializeQubit(qs[0]); + + // Confirm that the state is |0⟩. + AssertAllZero(qs); } } } - - operation T101_IsQubitOne_Test () : () - { - body - { - DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitOne); - } - } - + + // ------------------------------------------------------ - operation T102_InitializeQubit_Test () : () - { - body - { - using (qs = Qubit[1]) - { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - Ry(2.0 * alpha, qs[0]); - - // Test Task 1 - InitializeQubit(qs[0]); - - // Confirm that the state is |0⟩. - AssertAllZero(qs); - } - } + operation StatePrep_IsQubitPlus (q : Qubit, state : Int) : Unit { + if (state == 0) { + // convert |0⟩ to |-⟩ + X(q); + H(q); + } else { + // convert |0⟩ to |+⟩ + H(q); } } - + + + operation T103_IsQubitPlus_Test () : Unit { + DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitPlus); + } + + // ------------------------------------------------------ - operation StatePrep_IsQubitPlus (q : Qubit, state : Int) : () { - body { - if (state == 0) { - // convert |0⟩ to |-⟩ - X(q); - H(q); - } else { - // convert |0⟩ to |+⟩ - H(q); - } - } - } - - operation T103_IsQubitPlus_Test () : () - { - body - { - DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitPlus); - } - } - - // ------------------------------------------------------ - // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, + // |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩, // |B⟩ = - sin(alpha) * |0⟩ + cos(alpha) * |1⟩. - operation StatePrep_IsQubitA (alpha : Double, q : Qubit, state : Int) : () { - body { - if (state == 0) { - // convert |0⟩ to |B⟩ - X(q); - Ry(2.0 * alpha, q); - } else { - // convert |0⟩ to |A⟩ - Ry(2.0 * alpha, q); - } + operation StatePrep_IsQubitA (alpha : Double, q : Qubit, state : Int) : Unit { + if (state == 0) { + // convert |0⟩ to |B⟩ + X(q); + Ry(2.0 * alpha, q); + } else { + // convert |0⟩ to |A⟩ + Ry(2.0 * alpha, q); } } - - operation T104_IsQubitA_Test () : () - { - body - { - // cross-test - // alpha = 0.0 or PI() => !isQubitOne - // alpha = PI() / 2.0 => isQubitOne - DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitA(PI() / 2.0, _)); - // alpha = PI() / 4.0 => isQubitPlus - DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitA(PI() / 4.0, _)); - - for (i in 0..10) { - let alpha = PI() * ToDouble(i) / 10.0; - DistinguishTwoStates_OneQubit(StatePrep_IsQubitA(alpha, _, _), IsQubitA(alpha, _)); - } + + + operation T104_IsQubitA_Test () : Unit { + // cross-test + // alpha = 0.0 or PI() => !isQubitOne + // alpha = PI() / 2.0 => isQubitOne + DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitA(PI() / 2.0, _)); + + // alpha = PI() / 4.0 => isQubitPlus + DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitA(PI() / 4.0, _)); + + for (i in 0 .. 10) { + let alpha = (PI() * ToDouble(i)) / 10.0; + DistinguishTwoStates_OneQubit(StatePrep_IsQubitA(alpha, _, _), IsQubitA(alpha, _)); } } - + + // ------------------------------------------------------ - + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits // with Int return - operation DistinguishStates_MultiQubit ( - Nqubit : Int, - Nstate : Int, - statePrep : ((Qubit[], Int) => ()), - testImpl : (Qubit[] => Int) - ) : () - { - body - { - let nTotal = 100; - mutable nOk = 0; - using (qs = Qubit[Nqubit]) - { - for (i in 1..nTotal) - { - // get a random integer to define the state of the qubits - let state = RandomInt(Nstate); - - // do state prep: convert |0...0⟩ to outcome with return equal to state - statePrep(qs, state); + operation DistinguishStates_MultiQubit (Nqubit : Int, Nstate : Int, statePrep : ((Qubit[], Int) => Unit), testImpl : (Qubit[] => Int)) : Unit { + let nTotal = 100; + mutable nOk = 0; - // get the solution's answer and verify that it's a match - let ans = testImpl(qs); - if (ans == state) { - set nOk = nOk + 1; - } - - // we're not checking the state of the qubit after the operation - ResetAll(qs); + using (qs = Qubit[Nqubit]) { + for (i in 1 .. nTotal) { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // do state prep: convert |0...0⟩ to outcome with return equal to state + statePrep(qs, state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs); + if (ans == state) { + set nOk = nOk + 1; } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } + + AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } - + + // ------------------------------------------------------ - operation StatePrep_ZeroZeroOrOneOne (qs : Qubit[], state : Int) : () { - body { - if (state == 1) { - // |11⟩ - X(qs[0]); - X(qs[1]); - } + operation StatePrep_ZeroZeroOrOneOne (qs : Qubit[], state : Int) : Unit { + if (state == 1) { + // |11⟩ + X(qs[0]); + X(qs[1]); } } - - operation T105_ZeroZeroOrOneOne_Test () : () { - body { - DistinguishStates_MultiQubit(2, 2, StatePrep_ZeroZeroOrOneOne, ZeroZeroOrOneOne); - } + + + operation T105_ZeroZeroOrOneOne_Test () : Unit { + DistinguishStates_MultiQubit(2, 2, StatePrep_ZeroZeroOrOneOne, ZeroZeroOrOneOne); } - + + // ------------------------------------------------------ - operation StatePrep_BasisStateMeasurement (qs : Qubit[], state : Int) : () { - body { - if (state / 2 == 1) { - // |10⟩ or |11⟩ - X(qs[0]); - } - if (state % 2 == 1) { - // |01⟩ or |11⟩ - X(qs[1]); - } + operation StatePrep_BasisStateMeasurement (qs : Qubit[], state : Int) : Unit { + + if (state / 2 == 1) { + // |10⟩ or |11⟩ + X(qs[0]); + } + + if (state % 2 == 1) { + // |01⟩ or |11⟩ + X(qs[1]); } } - - operation T106_BasisStateMeasurement_Test () : () { - body { - DistinguishStates_MultiQubit(2, 4, StatePrep_BasisStateMeasurement, BasisStateMeasurement); - } + + + operation T106_BasisStateMeasurement_Test () : Unit { + DistinguishStates_MultiQubit(2, 4, StatePrep_BasisStateMeasurement, BasisStateMeasurement); } - + + // ------------------------------------------------------ - operation StatePrep_Bitstring (qs : Qubit[], bits : Bool[]) : () { - body { - for (i in 0..Length(qs)-1) { - if (bits[i]) { - X(qs[i]); - } + operation StatePrep_Bitstring (qs : Qubit[], bits : Bool[]) : Unit { + for (i in 0 .. Length(qs) - 1) { + if (bits[i]) { + X(qs[i]); } } } - - operation StatePrep_TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[], state : Int) : () { - body { - if (state == 0) { - StatePrep_Bitstring(qs, bits1); - } else { - StatePrep_Bitstring(qs, bits2); - } + + + operation StatePrep_TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[], state : Int) : Unit { + if (state == 0) { + StatePrep_Bitstring(qs, bits1); + } else { + StatePrep_Bitstring(qs, bits2); } } - - operation T107_TwoBitstringsMeasurement_Test () : () { - body { - for (i in 1..1) { - let b1 = [false; true]; - let b2 = [true; false]; - DistinguishStates_MultiQubit(2, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); - } - for (i in 1..1) { - let b1 = [true; true; false]; - let b2 = [false; true; true]; - DistinguishStates_MultiQubit(3, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); - } - for (i in 1..1) { - let b1 = [false; true; true; false]; - let b2 = [false; true; true; true]; - DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); - } - for (i in 1..1) { - let b1 = [true; false; false; false]; - let b2 = [true; false; true; true]; - DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); - } + + + operation T107_TwoBitstringsMeasurement_Test () : Unit { + for (i in 1 .. 1) { + let b1 = [false, true]; + let b2 = [true, false]; + DistinguishStates_MultiQubit(2, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + + for (i in 1 .. 1) { + let b1 = [true, true, false]; + let b2 = [false, true, true]; + DistinguishStates_MultiQubit(3, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + + for (i in 1 .. 1) { + let b1 = [false, true, true, false]; + let b2 = [false, true, true, true]; + DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + + for (i in 1 .. 1) { + let b1 = [true, false, false, false]; + let b2 = [true, false, true, true]; + DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); } } - + + // ------------------------------------------------------ - operation WState_Arbitrary_Reference (qs : Qubit[]) : () - { - body - { + operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit { + + body (...) { let N = Length(qs); + if (N == 1) { // base case of recursion: |1⟩ X(qs[0]); @@ -288,267 +258,260 @@ namespace Quantum.Kata.Measurements // |0⟩ -> sqrt((N-1)/N) |0⟩ + 1/sqrt(N) |1⟩ let theta = ArcSin(1.0 / Sqrt(ToDouble(N))); Ry(2.0 * theta, qs[0]); + // do a zero-controlled W-state generation for qubits 1..N-1 X(qs[0]); - (Controlled WState_Arbitrary_Reference)(qs[0..0], qs[1..N-1]); + Controlled WState_Arbitrary_Reference(qs[0 .. 0], qs[1 .. N - 1]); X(qs[0]); } } - adjoint auto; - controlled auto; - adjoint controlled auto; + + adjoint invert; + controlled distribute; + controlled adjoint distribute; } - - operation StatePrep_AllZerosOrWState (qs : Qubit[], state : Int) : () { - body { - if (state == 1) { - // prep W state - WState_Arbitrary_Reference(qs); - } + + + operation StatePrep_AllZerosOrWState (qs : Qubit[], state : Int) : Unit { + + if (state == 1) { + // prep W state + WState_Arbitrary_Reference(qs); } } - - operation T108_AllZerosOrWState_Test () : () { - body { - for (i in 2..6) { - DistinguishStates_MultiQubit(i, 2, StatePrep_AllZerosOrWState, AllZerosOrWState); - } + + + operation T108_AllZerosOrWState_Test () : Unit { + + for (i in 2 .. 6) { + DistinguishStates_MultiQubit(i, 2, StatePrep_AllZerosOrWState, AllZerosOrWState); } } - + + // ------------------------------------------------------ - operation GHZ_State_Reference (qs : Qubit[]) : () - { - body - { + operation GHZ_State_Reference (qs : Qubit[]) : Unit { + + body (...) { H(qs[0]); - for (i in 1 .. Length(qs)-1) { + for (i in 1 .. Length(qs) - 1) { CNOT(qs[0], qs[i]); } } - adjoint auto; + + adjoint invert; } - - operation StatePrep_GHZOrWState (qs : Qubit[], state : Int) : () { - body { - if (state == 0) { - // prep GHZ state - GHZ_State_Reference(qs); - } else { - // prep W state - WState_Arbitrary_Reference(qs); - } + + + operation StatePrep_GHZOrWState (qs : Qubit[], state : Int) : Unit { + + if (state == 0) { + // prep GHZ state + GHZ_State_Reference(qs); + } else { + // prep W state + WState_Arbitrary_Reference(qs); } } - - operation T109_GHZOrWState_Test () : () { - body { - for (i in 2..6) { - DistinguishStates_MultiQubit(i, 2, StatePrep_GHZOrWState, GHZOrWState); - } + + + operation T109_GHZOrWState_Test () : Unit { + for (i in 2 .. 6) { + DistinguishStates_MultiQubit(i, 2, StatePrep_GHZOrWState, GHZOrWState); } } - + + // ------------------------------------------------------ // 0 - |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) // 1 - |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2 - |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3 - |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation StatePrep_BellState (qs : Qubit[], state : Int) : () { - body { - H(qs[0]); - CNOT(qs[0], qs[1]); - // now we have |00⟩ + |11⟩ - modify it based on state arg - if (state % 2 == 1) { - // negative phase - Z(qs[1]); - } - if (state / 2 == 1) { - X(qs[1]); - } + operation StatePrep_BellState (qs : Qubit[], state : Int) : Unit { + H(qs[0]); + CNOT(qs[0], qs[1]); + + // now we have |00⟩ + |11⟩ - modify it based on state arg + if (state % 2 == 1) { + // negative phase + Z(qs[1]); + } + if (state / 2 == 1) { + X(qs[1]); } } - - operation T110_BellState_Test () : () { - body { - DistinguishStates_MultiQubit(2, 4, StatePrep_BellState, BellState); - } + + + operation T110_BellState_Test () : Unit { + DistinguishStates_MultiQubit(2, 4, StatePrep_BellState, BellState); } - + + // ------------------------------------------------------ // 0 - (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2 // 1 - (|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2 // 2 - (|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2 // 3 - (|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2 - operation StatePrep_TwoQubitState (qs : Qubit[], state : Int) : () { - body { - // start with state prep of basis vectors - StatePrep_BasisStateMeasurement(qs, state); - H(qs[0]); - H(qs[1]); - } + operation StatePrep_TwoQubitState (qs : Qubit[], state : Int) : Unit { + // start with state prep of basis vectors + StatePrep_BasisStateMeasurement(qs, state); + H(qs[0]); + H(qs[1]); } + // ------------------------------------------------------ // 0 - ( |00⟩ - |01⟩ - |10⟩ - |11⟩) / 2 // 1 - (-|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2 // 2 - (-|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2 // 3 - (-|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2 - operation StatePrep_TwoQubitStatePartTwo (qs : Qubit[], state : Int) : () { - body { - // start with state prep of basis vectors - StatePrep_BasisStateMeasurement(qs, state); - // now apply all gates for unitary in reference impl (in reverse + adjoint) - ApplyToEach(X, qs); - (Controlled Z)([qs[0]], qs[1]); - ApplyToEach(X, qs); - ApplyToEach(H, qs); - ApplyToEach(X, qs); - (Controlled Z)([qs[0]], qs[1]); - ApplyToEach(X, qs); - SWAP(qs[0], qs[1]); - } + operation StatePrep_TwoQubitStatePartTwo (qs : Qubit[], state : Int) : Unit { + + // start with state prep of basis vectors + StatePrep_BasisStateMeasurement(qs, state); + + // now apply all gates for unitary in reference impl (in reverse + adjoint) + ApplyToEach(X, qs); + Controlled Z([qs[0]], qs[1]); + ApplyToEach(X, qs); + ApplyToEach(H, qs); + ApplyToEach(X, qs); + Controlled Z([qs[0]], qs[1]); + ApplyToEach(X, qs); + SWAP(qs[0], qs[1]); } - - operation T111_TwoQubitState_Test () : () { - body { - DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitState, TwoQubitState); - } + + + operation T111_TwoQubitState_Test () : Unit { + DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitState, TwoQubitState); } - - operation T112_TwoQubitStatePartTwo_Test () : () { - body { - DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitStatePartTwo, TwoQubitStatePartTwo); - } + + + operation T112_TwoQubitStatePartTwo_Test () : Unit { + DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitStatePartTwo, TwoQubitStatePartTwo); } - + + ////////////////////////////////////////////////////////////////// - - operation StatePrep_IsQubitZeroOrPlus (q : Qubit, state : Int) : () { - body { - if (state == 0) { - // convert |0⟩ to |0⟩ - } else { - // convert |0⟩ to |+⟩ - H(q); - } + operation StatePrep_IsQubitZeroOrPlus (q : Qubit, state : Int) : Unit { + + if (state == 0) { + // convert |0⟩ to |0⟩ + } else { + // convert |0⟩ to |+⟩ + H(q); } } - + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits // with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed. - operation DistinguishStates_MultiQubit_Threshold ( - Nqubit : Int, - Nstate : Int, - threshold : Double, - statePrep : ((Qubit, Int) => ()), - testImpl : (Qubit => Bool) - ) : () - { - body - { - let nTotal = 1000; - mutable nOk = 0; - using (qs = Qubit[Nqubit]) - { - for (i in 1..nTotal) - { - // get a random integer to define the state of the qubits - let state = RandomInt(Nstate); - - // do state prep: convert |0⟩ to outcome with return equal to state - statePrep(qs[0], state); - // get the solution's answer and verify that it's a match - let ans = testImpl(qs[0]); - if (ans == (state == 0)) { - set nOk = nOk + 1; - } - - // we're not checking the state of the qubit after the operation - ResetAll(qs); + operation DistinguishStates_MultiQubit_Threshold (Nqubit : Int, Nstate : Int, threshold : Double, statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Bool)) : Unit { + let nTotal = 1000; + mutable nOk = 0; + + using (qs = Qubit[Nqubit]) { + for (i in 1 .. nTotal) { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // do state prep: convert |0⟩ to outcome with return equal to state + statePrep(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + if (ans == (state == 0)) { + set nOk = nOk + 1; } - } - if (ToDouble(nOk) < threshold * ToDouble(nTotal)) { - fail $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state which does not meet the required threshold of at least {threshold*100}%."; + + // we're not checking the state of the qubit after the operation + ResetAll(qs); } } + + if (ToDouble(nOk) < threshold * ToDouble(nTotal)) { + fail $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state which does not meet the required threshold of at least {threshold * 100.0}%."; + } } - + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits // with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed. // Framework tests in the USD scenario, i.e., it is allowed to respond "inconclusive" (with some probability) - // up to given threshold, but it is never allowed to err if an actual conclusive response is given. - operation USD_DistinguishStates_MultiQubit_Threshold ( - Nqubit : Int, - Nstate : Int, - thresholdInconcl : Double, - thresholdConcl : Double, - statePrep : ((Qubit, Int) => ()), - testImpl : (Qubit => Int) - ) : () - { - body - { - let nTotal = 10000; - mutable nInconc = 0; // counts total inconclusive answers - mutable nConclOne = 0; // counts total conclusive |0⟩ state identifications - mutable nConclPlus = 0; // counts total conclusive |+> state identifications - - using (qs = Qubit[Nqubit]) - { - for (i in 1..nTotal) - { - // get a random integer to define the state of the qubits - let state = RandomInt(Nstate); - // do state prep: convert |0⟩ to outcome with return equal to state - statePrep(qs[0], state); - // get the solution's answer and verify that it's a match - let ans = testImpl(qs[0]); - // check that the answer is actually in allowed range - if (ans < -1 || ans > 1) { - fail $"state {state} led to invalid response {ans}."; - } - // keep track of the number of inconclusive answers given - if (ans == -1) { - set nInconc = nInconc + 1; - } - if (ans == 0 && state == 0) { - set nConclOne = nConclOne + 1; - } - if (ans == 1 && state == 1) { - set nConclPlus = nConclPlus + 1; - } - // check if upon conclusive result the answer is actually correct - if ((ans == 0) && (state == 1) || (ans == 1) && (state == 0)) { - fail $"state {state} led to incorrect conclusive response {ans}."; - } - // we're not checking the state of the qubit after the operation - ResetAll(qs); + // up to given threshold, but it is never allowed to err if an actual conclusive response is given. + operation USD_DistinguishStates_MultiQubit_Threshold (Nqubit : Int, Nstate : Int, thresholdInconcl : Double, thresholdConcl : Double, statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Int)) : Unit { + let nTotal = 10000; + + // counts total inconclusive answers + mutable nInconc = 0; + + // counts total conclusive |0⟩ state identifications + mutable nConclOne = 0; + + // counts total conclusive |+> state identifications + mutable nConclPlus = 0; + + using (qs = Qubit[Nqubit]) { + for (i in 1 .. nTotal) { + + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // do state prep: convert |0⟩ to outcome with return equal to state + statePrep(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + + // check that the answer is actually in allowed range + if (ans < -1 || ans > 1) { + fail $"state {state} led to invalid response {ans}."; } - } - if (ToDouble(nInconc) > thresholdInconcl * ToDouble(nTotal)) { - fail $"{nInconc} test runs out of {nTotal} returned inconclusive which does not meet the required threshold of at most {thresholdInconcl*100}%."; - } - if (ToDouble(nConclOne) < thresholdConcl * ToDouble(nTotal)) { - fail $"Only {nConclOne} test runs out of {nTotal} returned conclusive |0⟩ which does not meet the required threshold of at least {thresholdConcl*100}%."; - } - if (ToDouble(nConclPlus) < thresholdConcl * ToDouble(nTotal)) { - fail $"Only {nConclPlus} test runs out of {nTotal} returned conclusive |+> which does not meet the required threshold of at least {thresholdConcl*100}%."; + + // keep track of the number of inconclusive answers given + if (ans == -1) { + set nInconc = nInconc + 1; + } + + if (ans == 0 && state == 0) { + set nConclOne = nConclOne + 1; + } + + if (ans == 1 && state == 1) { + set nConclPlus = nConclPlus + 1; + } + + // check if upon conclusive result the answer is actually correct + if (ans == 0 && state == 1 || ans == 1 && state == 0) { + fail $"state {state} led to incorrect conclusive response {ans}."; + } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); } } - } - - operation T201_IsQubitZeroOrPlus_Test () : () { - body { - DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, StatePrep_IsQubitZeroOrPlus, IsQubitPlusOrZero); + + if (ToDouble(nInconc) > thresholdInconcl * ToDouble(nTotal)) { + fail $"{nInconc} test runs out of {nTotal} returned inconclusive which does not meet the required threshold of at most {thresholdInconcl * 100.0}%."; + } + + if (ToDouble(nConclOne) < thresholdConcl * ToDouble(nTotal)) { + fail $"Only {nConclOne} test runs out of {nTotal} returned conclusive |0⟩ which does not meet the required threshold of at least {thresholdConcl * 100.0}%."; + } + + if (ToDouble(nConclPlus) < thresholdConcl * ToDouble(nTotal)) { + fail $"Only {nConclPlus} test runs out of {nTotal} returned conclusive |+> which does not meet the required threshold of at least {thresholdConcl * 100.0}%."; } } - - operation T202_IsQubitZeroOrPlusSimpleUSD_Test () : () { - body { - USD_DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, 0.1, StatePrep_IsQubitZeroOrPlus, IsQubitPlusZeroOrInconclusiveSimpleUSD); - } + + + operation T201_IsQubitZeroOrPlus_Test () : Unit { + DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, StatePrep_IsQubitZeroOrPlus, IsQubitPlusOrZero); } -} \ No newline at end of file + + + operation T202_IsQubitZeroOrPlusSimpleUSD_Test () : Unit { + USD_DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, 0.1, StatePrep_IsQubitZeroOrPlus, IsQubitPlusZeroOrInconclusiveSimpleUSD); + } + +} diff --git a/QEC_BitFlipCode/QEC_BitFlipCode.csproj b/QEC_BitFlipCode/QEC_BitFlipCode.csproj index cdaaba2b..975a0329 100644 --- a/QEC_BitFlipCode/QEC_BitFlipCode.csproj +++ b/QEC_BitFlipCode/QEC_BitFlipCode.csproj @@ -1,6 +1,6 @@  - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.QEC_BitFlipCode @@ -10,9 +10,9 @@ - - - + + + diff --git a/QEC_BitFlipCode/ReferenceImplementation.qs b/QEC_BitFlipCode/ReferenceImplementation.qs index 790cf6b8..8c11e1b5 100644 --- a/QEC_BitFlipCode/ReferenceImplementation.qs +++ b/QEC_BitFlipCode/ReferenceImplementation.qs @@ -2,86 +2,89 @@ // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// namespace Quantum.Kata.QEC_BitFlipCode { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + // Task 1. Parity Measurements operation MeasureParity_Reference (register : Qubit[]) : Result { - body { - return Measure([PauliZ; PauliZ; PauliZ], register); - } + return Measure([PauliZ, PauliZ, PauliZ], register); } - + + // Task 2. Encoding Codewords - operation Encode_Reference(register : Qubit[]) : () { - body { + operation Encode_Reference (register : Qubit[]) : Unit { + + body (...) { ApplyToEachA(CNOT(Head(register), _), Rest(register)); } - adjoint auto; + + adjoint invert; } - + + // Task 3. Error Detection I operation DetectErrorOnLeftQubit_Reference (register : Qubit[]) : Result { - body { - return Measure([PauliZ; PauliZ], register[0..1]); - } + return Measure([PauliZ, PauliZ], register[0 .. 1]); } - + + // Task 4. Error Correction I - operation CorrectErrorOnLeftQubit_Reference (register : Qubit[]) : () { - body { - if (Measure([PauliZ; PauliZ], register[0..1]) == One) { - X(register[0]); - } + operation CorrectErrorOnLeftQubit_Reference (register : Qubit[]) : Unit { + if (Measure([PauliZ, PauliZ], register[0 .. 1]) == One) { + X(register[0]); } } - + + // Task 5. Error Detection II operation DetectErrorOnAnyQubit_Reference (register : Qubit[]) : Int { - body { - let m1 = Measure([PauliZ; PauliZ], register[0..1]); - let m2 = Measure([PauliZ; PauliZ], register[1..2]); - if (m1 == One && m2 == Zero) { - return 1; - } - if (m1 == One && m2 == One) { - return 2; - } - if (m1 == Zero && m2 == One) { - return 3; - } - return 0; + + let m1 = Measure([PauliZ, PauliZ], register[0 .. 1]); + let m2 = Measure([PauliZ, PauliZ], register[1 .. 2]); + + if (m1 == One && m2 == Zero) { + return 1; } + + if (m1 == One && m2 == One) { + return 2; + } + + if (m1 == Zero && m2 == One) { + return 3; + } + + return 0; } - + + // Task 6. Error Correction II - operation CorrectErrorOnAnyQubit_Reference (register : Qubit[]) : () { - body { - let idx = DetectErrorOnAnyQubit_Reference(register); - if (idx > 0) { - X(register[idx - 1]); - } + operation CorrectErrorOnAnyQubit_Reference (register : Qubit[]) : Unit { + let idx = DetectErrorOnAnyQubit_Reference(register); + if (idx > 0) { + X(register[idx - 1]); } } - + + // Task 7. Logical X Gate - operation LogicalX_Reference (register : Qubit[]) : () { - body { - ApplyToEach(X, register); - } + operation LogicalX_Reference (register : Qubit[]) : Unit { + ApplyToEach(X, register); } - + + // Task 8. Logical Z Gate - operation LogicalZ_Reference (register : Qubit[]) : () { - body { - ApplyToEach(Z, register); - } + operation LogicalZ_Reference (register : Qubit[]) : Unit { + ApplyToEach(Z, register); } + } diff --git a/QEC_BitFlipCode/Tasks.qs b/QEC_BitFlipCode/Tasks.qs index 14085c1d..399c992f 100644 --- a/QEC_BitFlipCode/Tasks.qs +++ b/QEC_BitFlipCode/Tasks.qs @@ -2,33 +2,35 @@ // Licensed under the MIT license. namespace Quantum.Kata.QEC_BitFlipCode { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // The "Quantum error correction - bit-flip code" quantum kata is - // a series of exercises designed to get you familiar with + + // The "Quantum error correction - bit-flip code" quantum kata is + // a series of exercises designed to get you familiar with // quantum error correction (QEC) and programming in Q#. // It introduces you to the simplest of QEC codes - the three-qubit bit-flip code, - // which encodes each logical qubit in three physical qubits + // which encodes each logical qubit in three physical qubits // and protects against single bit-flip error (equivalent to applying an X gate). - // In practice quantum systems can have other types of errors, + // In practice quantum systems can have other types of errors, // which will be considered in the following katas on quantum error correction. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // - // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. - - + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + + // Task 1. Parity Measurements // - // Input: three qubits (stored as an array of length 3) in an unknown basis state + // Input: three qubits (stored as an array of length 3) in an unknown basis state // or in a superposition of basis states of the same parity. // Output: the parity of this state using exactly one call to Measure // encoded as a value of Result type: Zero for parity 0 and One for parity 1. @@ -37,24 +39,22 @@ namespace Quantum.Kata.QEC_BitFlipCode { // Example: // |000⟩, |101⟩ and |011⟩ all have parity 0, while |010⟩ and |111⟩ have parity 1. operation MeasureParity (register : Qubit[]) : Result { - body { - // Fill in your code here and change the return statement. - // ... - return Zero; - } + // Fill in your code here and change the return statement. + // ... + return Zero; } - + + // Task 2. Encoding Codewords // // Input: three qubits in the state |ψ⟩ ⊗ |00⟩, where |ψ⟩ = α |0⟩ + β |1⟩ is // the state of the first qubit, i.e., register[0]. // Goal: create a state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ on these qubits. - operation Encode (register : Qubit[]) : () { - body { - // ... - } + operation Encode (register : Qubit[]) : Unit { + // ... } - + + // Task 3. Error Detection I // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ @@ -65,25 +65,23 @@ namespace Quantum.Kata.QEC_BitFlipCode { // One if the input is X𝟙𝟙|̅ψ⟩ (state with the error). // After applying the operation the state of the qubits should not change. operation DetectErrorOnLeftQubit (register : Qubit[]) : Result { - body { - // ... - return Zero; - } + // ... + return Zero; } - + + // Task 4. Error Correction I // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ // or in the state X𝟙𝟙|̅ψ⟩ = α |100⟩ + β |011⟩. // Goal: make sure that the qubits are returned to the state |̅ψ⟩ // (i.e., determine whether an X error has occurred, and if so, fix it). - operation CorrectErrorOnLeftQubit (register : Qubit[]) : () { - body { - // Hint: you can use task 3 to figure out which state you are given. - // ... - } + operation CorrectErrorOnLeftQubit (register : Qubit[]) : Unit { + // Hint: you can use task 3 to figure out which state you are given. + // ... } - + + // Task 5. Error Detection II // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ @@ -98,12 +96,11 @@ namespace Quantum.Kata.QEC_BitFlipCode { // 𝟙𝟙X | 3 // After applying the operation the state of the qubits should not change. operation DetectErrorOnAnyQubit (register : Qubit[]) : Int { - body { - // ... - return -1; - } + // ... + return -1; } - + + // Task 6. Error Correction II // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ @@ -111,22 +108,21 @@ namespace Quantum.Kata.QEC_BitFlipCode { // (i.e., the qubits start in state |̅ψ⟩ with an X error possibly applied to one of the qubits). // Goal: make sure that the qubits are returned to the state |̅ψ⟩ // (i.e., determine whether an X error has occurred on any qubit, and if so, fix it). - operation CorrectErrorOnAnyQubit (register : Qubit[]) : () { - body { - // ... - } + operation CorrectErrorOnAnyQubit (register : Qubit[]) : Unit { + // ... } - + + ////////////////////////////////////////////////////////////////// // All the tasks in this kata have been dealing with X errors on single qubit. // The bit-flip code doesn't allow one to detect or correct a Z error or multiple X errors. - // Indeed, a Z error on a logical state |ψ⟩ = α |0⟩ + β |1⟩ encoded using the bit-flip code + // Indeed, a Z error on a logical state |ψ⟩ = α |0⟩ + β |1⟩ encoded using the bit-flip code // would convert the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ into α |000⟩ - β |111⟩, // which is a correct code word for logical state α |0⟩ - β |1⟩. // Two X errors (say, on qubits 1 and 2) would convert |̅ψ⟩ to α |110⟩ + β |001⟩, // which is a code word for logical state β |0⟩ + α |1⟩ with one X error on qubit 3. ////////////////////////////////////////////////////////////////// - + // Task 7. Logical X Gate // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ @@ -136,12 +132,11 @@ namespace Quantum.Kata.QEC_BitFlipCode { // ̅X |̅ψ⟩ = β |000⟩ + α |111⟩ or one of the states that can be represented as // ̅X |̅ψ⟩ with an X error applied to one of the qubits (for example, β |010⟩ + α |101⟩). // If the state has an error, you can fix it, but this is not necessary. - operation LogicalX (register : Qubit[]) : () { - body { - // ... - } + operation LogicalX (register : Qubit[]) : Unit { + // ... } - + + // Task 8. Logical Z Gate // // Input: three qubits that are either in the state |̅ψ⟩ ≔ α |000⟩ + β |111⟩ @@ -151,9 +146,8 @@ namespace Quantum.Kata.QEC_BitFlipCode { // ̅Z |̅ψ⟩ = α |000⟩ - β |111⟩ or one of the states that can be represented as // ̅Z |̅ψ⟩ with an X error applied to one of the qubits (for example, α |010⟩ - β |101⟩). // If the state has an error, you can fix it, but this is not necessary. - operation LogicalZ (register : Qubit[]) : () { - body { - // ... - } + operation LogicalZ (register : Qubit[]) : Unit { + // ... } + } diff --git a/QEC_BitFlipCode/Tests.qs b/QEC_BitFlipCode/Tests.qs index 41c1c0c2..74d2f832 100644 --- a/QEC_BitFlipCode/Tests.qs +++ b/QEC_BitFlipCode/Tests.qs @@ -1,20 +1,22 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains parts of the testing harness. +// This file contains parts of the testing harness. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// namespace Quantum.Kata.QEC_BitFlipCode { + open Microsoft.Quantum.Canon; open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Extensions.Bitwise; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + ////////////////////////////////////////////////////////////////////////// // Task 01 ////////////////////////////////////////////////////////////////////////// @@ -25,43 +27,51 @@ namespace Quantum.Kata.QEC_BitFlipCode { let b3 = bits % 2; return $"{b1}{b2}{b3}"; } - - operation StatePrep_Bitmask (qs : Qubit[], bits : Int) : () { - body { + + + operation StatePrep_Bitmask (qs : Qubit[], bits : Int) : Unit { + + body (...) { if (bits / 4 == 1) { X(qs[0]); } + if ((bits / 2) % 2 == 1) { X(qs[1]); } + if (bits % 2 == 1) { X(qs[2]); } } - adjoint auto; + + adjoint invert; } - + + function FindFirstDiff_Reference (bits1 : Int[], bits2 : Int[]) : Int { mutable firstDiff = -1; - for (i in 0 .. Length(bits1)-1) { + for (i in 0 .. Length(bits1) - 1) { if (bits1[i] != bits2[i] && firstDiff == -1) { set firstDiff = i; } } return firstDiff; } - + + function IntToBoolArray (n : Int) : Int[] { - return [(n / 4) % 2; (n / 2) % 2; n % 2]; + return [(n / 4) % 2, (n / 2) % 2, n % 2]; } - - operation StatePrep_TwoBitmasks (qs : Qubit[], bits1 : Int[], bits2 : Int[]) : () { - body { + + + operation StatePrep_TwoBitmasks (qs : Qubit[], bits1 : Int[], bits2 : Int[]) : Unit { + + body (...) { let firstDiff = FindFirstDiff_Reference(bits1, bits2); - H(qs[firstDiff]); - - for (i in 0 .. Length(qs)-1) { + + for (i in 0 .. Length(qs) - 1) { if (bits1[i] == bits2[i]) { if (bits1[i] == 1) { X(qs[i]); @@ -76,280 +86,270 @@ namespace Quantum.Kata.QEC_BitFlipCode { } } } - adjoint auto; + + adjoint invert; } - - operation TestParityOnState ( - statePrep : (Qubit[] => () : Adjoint), - parity : Int, - stateStr : String - ) : () { - body { - using (register = Qubit[3]) { - // prepare basis state to test on - statePrep(register); - let res = MeasureParity(register); - - // check that the returned parity is correct - AssertBoolEqual(res == Zero, parity == 0, $"Failed on {stateStr}."); - - // check that the state has not been modified - (Adjoint statePrep)(register); - AssertAllZero(register); - } + + + operation TestParityOnState (statePrep : (Qubit[] => Unit : Adjoint), parity : Int, stateStr : String) : Unit { + + using (register = Qubit[3]) { + // prepare basis state to test on + statePrep(register); + let res = MeasureParity(register); + + // check that the returned parity is correct + AssertBoolEqual(res == Zero, parity == 0, $"Failed on {stateStr}."); + + // check that the state has not been modified + Adjoint statePrep(register); + AssertAllZero(register); } } - - operation T01_MeasureParity_Test () : () { - body { - // test on all basis states - for (bits in 0..7) { - let bitsStr = ToString_Bitmask(bits); - TestParityOnState(StatePrep_Bitmask(_, bits), Parity(bits), $"basis state |{bitsStr}⟩"); - } - - // test on all superpositions of two basis states of the same parity - for (b1 in 0..7) { - let bits1 = IntToBoolArray(b1); - let bitsStr1 = ToString_Bitmask(b1); - for (b2 in (b1 + 1)..7) { - if (Parity(b1) == Parity(b2)) { - let bits2 = IntToBoolArray(b2); - let bitsStr2 = ToString_Bitmask(b2); - let p = Parity(b1); - Message($"Testing on |{bitsStr1}⟩ + |{bitsStr2}⟩ with parity {p}"); - TestParityOnState(StatePrep_TwoBitmasks(_, bits1, bits2), Parity(b1), - $"state |{bitsStr1}⟩ + |{bitsStr2}⟩"); - } + + + operation T01_MeasureParity_Test () : Unit { + // test on all basis states + for (bits in 0 .. 7) { + let bitsStr = ToString_Bitmask(bits); + TestParityOnState(StatePrep_Bitmask(_, bits), Parity(bits), $"basis state |{bitsStr}⟩"); + } + + // test on all superpositions of two basis states of the same parity + for (b1 in 0 .. 7) { + let bits1 = IntToBoolArray(b1); + let bitsStr1 = ToString_Bitmask(b1); + + for (b2 in b1 + 1 .. 7) { + if (Parity(b1) == Parity(b2)) { + let bits2 = IntToBoolArray(b2); + let bitsStr2 = ToString_Bitmask(b2); + let p = Parity(b1); + Message($"Testing on |{bitsStr1}⟩ + |{bitsStr2}⟩ with parity {p}"); + TestParityOnState(StatePrep_TwoBitmasks(_, bits1, bits2), Parity(b1), $"state |{bitsStr1}⟩ + |{bitsStr2}⟩"); } } } } - + + ////////////////////////////////////////////////////////////////////////// // Task 02 ////////////////////////////////////////////////////////////////////////// - + operation AssertEqualOnZeroState ( - statePrep : (Qubit[] => () : Adjoint), - testImpl : (Qubit[] => ()), - refImpl : (Qubit[] => () : Adjoint) - ) : () { - body { - using (qs = Qubit[3]) { - // prepare state - statePrep(qs); - - // apply operation that needs to be tested - testImpl(qs); - - // apply adjoint reference operation and adjoint state prep - (Adjoint refImpl)(qs); - (Adjoint statePrep)(qs); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + statePrep : (Qubit[] => Unit : Adjoint), + testImpl : (Qubit[] => Unit), + refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + using (qs = Qubit[3]) { + // prepare state + statePrep(qs); + + // apply operation that needs to be tested + testImpl(qs); + + // apply adjoint reference operation and adjoint state prep + Adjoint refImpl(qs); + Adjoint statePrep(qs); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } - - operation StatePrep_Rotate (qs : Qubit[], alpha : Double) : () { - body { + + + operation StatePrep_Rotate (qs : Qubit[], alpha : Double) : Unit { + + body (...) { Ry(2.0 * alpha, qs[0]); } - adjoint auto; + + adjoint invert; } - - operation T02_Encode_Test () : () { - body { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - AssertEqualOnZeroState(StatePrep_Rotate(_, alpha), Encode, Encode_Reference); - } + + + operation T02_Encode_Test () : Unit { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + AssertEqualOnZeroState(StatePrep_Rotate(_, alpha), Encode, Encode_Reference); } } - + + ////////////////////////////////////////////////////////////////////////// // Task 03 ////////////////////////////////////////////////////////////////////////// - operation StatePrep_WithError (qs : Qubit[], alpha : Double, hasError : Bool) : () { - body { + operation StatePrep_WithError (qs : Qubit[], alpha : Double, hasError : Bool) : Unit { + + body (...) { StatePrep_Rotate(qs, alpha); Encode_Reference(qs); + if (hasError) { X(qs[0]); } } - adjoint auto; + + adjoint invert; } - - operation T03_DetectErrorOnLeftQubit_Test () : () { - body { - using (register = Qubit[3]) { - for (i in 0..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - StatePrep_WithError(register, alpha, false); - AssertResultEqual(DetectErrorOnLeftQubit(register), Zero, "Failed on a state without X error."); - (Adjoint StatePrep_WithError)(register, alpha, false); - AssertAllZero(register); - - StatePrep_WithError(register, alpha, true); - AssertResultEqual(DetectErrorOnLeftQubit(register), One, "Failed on a state with X error."); - (Adjoint StatePrep_WithError)(register, alpha, true); - AssertAllZero(register); - } + + + operation T03_DetectErrorOnLeftQubit_Test () : Unit { + using (register = Qubit[3]) { + for (i in 0 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + StatePrep_WithError(register, alpha, false); + AssertResultEqual(DetectErrorOnLeftQubit(register), Zero, "Failed on a state without X error."); + Adjoint StatePrep_WithError(register, alpha, false); + AssertAllZero(register); + StatePrep_WithError(register, alpha, true); + AssertResultEqual(DetectErrorOnLeftQubit(register), One, "Failed on a state with X error."); + Adjoint StatePrep_WithError(register, alpha, true); + AssertAllZero(register); } } } - + + ////////////////////////////////////////////////////////////////////////// // Task 04 ////////////////////////////////////////////////////////////////////////// - + operation BindErrorCorrectionRoundImpl ( - encoder : (Qubit[] => () : Adjoint), - error : Pauli[], - logicalOp : (Qubit[] => ()), - correction : (Qubit[] => ()), - - dataRegister : Qubit[] - ) : () { - body { - using (auxiliary = Qubit[2]) { - let register = dataRegister + auxiliary; - - // encode the logical qubit (dataRegister) into physical representation (register) - encoder(register); - - // apply error (or no error) - ApplyPauli(error, register); - - // perform logical operation on (possibly erroneous) state - logicalOp(register); - - // apply correction to get the state back to correct one - correction(register); - - // apply decoding to get back to 1-qubit state - (Adjoint encoder)(register); - - AssertAllZero(auxiliary); - } + encoder : (Qubit[] => Unit : Adjoint), + error : Pauli[], + logicalOp : (Qubit[] => Unit), + correction : (Qubit[] => Unit), + dataRegister : Qubit[]) : Unit { + + using (auxiliary = Qubit[2]) { + let register = dataRegister + auxiliary; + + // encode the logical qubit (dataRegister) into physical representation (register) + encoder(register); + + // apply error (or no error) + ApplyPauli(error, register); + + // perform logical operation on (possibly erroneous) state + logicalOp(register); + + // apply correction to get the state back to correct one + correction(register); + + // apply decoding to get back to 1-qubit state + Adjoint encoder(register); + AssertAllZero(auxiliary); } } - + + function BindErrorCorrectionRound ( - encoder : (Qubit[] => () : Adjoint), - error : Pauli[], - logicalOp : (Qubit[] => ()), - correction : (Qubit[] => ()) - ) : (Qubit[] => ()) { - + encoder : (Qubit[] => Unit : Adjoint), + error : Pauli[], + logicalOp : (Qubit[] => Unit), + correction : (Qubit[] => Unit)) : (Qubit[] => Unit) { + return BindErrorCorrectionRoundImpl(encoder, error, logicalOp, correction, _); } - + + // list of errors which can be corrected by the code (the first element corresponds to no error) - function PauliErrors() : Pauli[][] { - return [ - [PauliI; PauliI; PauliI]; - [PauliX; PauliI; PauliI]; - [PauliI; PauliX; PauliI]; - [PauliI; PauliI; PauliX] - ]; + function PauliErrors () : Pauli[][] { + return [[PauliI, PauliI, PauliI], + [PauliX, PauliI, PauliI], + [PauliI, PauliX, PauliI], + [PauliI, PauliI, PauliX]]; } - - operation T04_CorrectErrorOnLeftQubit_Test () : () { - body { - let partialBind = - BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnLeftQubit); - - let errors = PauliErrors(); - for (idxError in 0..1) { - AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); - } + + + operation T04_CorrectErrorOnLeftQubit_Test () : Unit { + let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnLeftQubit); + let errors = PauliErrors(); + + for (idxError in 0 .. 1) { + AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); } } - + + ////////////////////////////////////////////////////////////////////////// // Task 05 ////////////////////////////////////////////////////////////////////////// - operation T05_DetectErrorOnAnyQubit_Test () : () { - body { - let errors = PauliErrors(); - using (register = Qubit[3]) { - - for (idxError in 0..Length(errors) - 1) { - let θ = RandomReal(12); - let statePrep = BindA([H; Rz(θ, _)]); - mutable errorStr = "no error"; - if (idxError > 0) { - set errorStr = $"error on qubit {idxError}"; - } - Message($"Testing with {errorStr}."); - statePrep(Head(register)); - Encode_Reference(register); - ApplyPauli(errors[idxError], register); - AssertIntEqual(DetectErrorOnAnyQubit(register), idxError, $"Failed on state with {errorStr}."); - ApplyPauli(errors[idxError], register); - (Adjoint Encode_Reference)(register); - (Adjoint statePrep)(Head(register)); - - AssertAllZero(register); + operation T05_DetectErrorOnAnyQubit_Test () : Unit { + let errors = PauliErrors(); + + using (register = Qubit[3]) { + + for (idxError in 0 .. Length(errors) - 1) { + let θ = RandomReal(12); + let statePrep = BindCA([H, Rz(θ, _)]); + mutable errorStr = "no error"; + if (idxError > 0) { + set errorStr = $"error on qubit {idxError}"; } + + Message($"Testing with {errorStr}."); + statePrep(Head(register)); + Encode_Reference(register); + ApplyPauli(errors[idxError], register); + AssertIntEqual(DetectErrorOnAnyQubit(register), idxError, $"Failed on state with {errorStr}."); + ApplyPauli(errors[idxError], register); + Adjoint Encode_Reference(register); + Adjoint statePrep(Head(register)); + AssertAllZero(register); } } } - + + ////////////////////////////////////////////////////////////////////////// // Task 06 ////////////////////////////////////////////////////////////////////////// - operation T06_CorrectErrorOnAnyQubit_Test () : () { - body { - let partialBind = - BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnAnyQubit); - - let errors = PauliErrors(); - for (idxError in 0..Length(errors) - 1) { - Message($"Task 06: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); - } + operation T06_CorrectErrorOnAnyQubit_Test () : Unit { + + let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnAnyQubit); + let errors = PauliErrors(); + + for (idxError in 0 .. Length(errors) - 1) { + Message($"Task 06: Testing on {errors[idxError]}..."); + AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); } } - + + ////////////////////////////////////////////////////////////////////////// // Task 07 ////////////////////////////////////////////////////////////////////////// - operation T07_LogicalX_Test () : () { - body { - let partialBind = - BindErrorCorrectionRound(Encode_Reference, _, LogicalX, CorrectErrorOnAnyQubit_Reference); - - let errors = PauliErrors(); - for (idxError in 0..Length(errors) - 1) { - Message($"Task 07: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyPauli([PauliX], _), 1); - } + operation T07_LogicalX_Test () : Unit { + + let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalX, CorrectErrorOnAnyQubit_Reference); + let errors = PauliErrors(); + + for (idxError in 0 .. Length(errors) - 1) { + Message($"Task 07: Testing on {errors[idxError]}..."); + AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyPauli([PauliX], _), 1); } } - + + ////////////////////////////////////////////////////////////////////////// // Task 08 ////////////////////////////////////////////////////////////////////////// - operation T08_LogicalZ_Test () : () { - body { - let partialBind = - BindErrorCorrectionRound(Encode_Reference, _, LogicalZ, CorrectErrorOnAnyQubit_Reference); - - let errors = PauliErrors(); - for (idxError in 0..Length(errors) - 1) { - Message($"Task 08: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyToEachA(Z, _), 1); - } + operation T08_LogicalZ_Test () : Unit { + + let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalZ, CorrectErrorOnAnyQubit_Reference); + let errors = PauliErrors(); + + for (idxError in 0 .. Length(errors) - 1) { + Message($"Task 08: Testing on {errors[idxError]}..."); + AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyToEachA(Z, _), 1); } } -} \ No newline at end of file + +} diff --git a/SimonsAlgorithm/ReferenceImplementation.qs b/SimonsAlgorithm/ReferenceImplementation.qs index c0c15d4c..873919f3 100644 --- a/SimonsAlgorithm/ReferenceImplementation.qs +++ b/SimonsAlgorithm/ReferenceImplementation.qs @@ -1,139 +1,139 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.SimonsAlgorithm -{ +namespace Quantum.Kata.SimonsAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Part I. Oracles ////////////////////////////////////////////////////////////////// - + // Task 1.1. f(x) = 𝑥₀ ⊕ ... ⊕ xₙ₋₁ (parity of the number of bits set to 1) - operation Oracle_CountBits_Reference (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_CountBits_Reference (x : Qubit[], y : Qubit) : Unit { + + body (...) { let N = Length(x); - for (i in 0..N-1) - { + for (i in 0 .. N - 1) { CNOT(x[i], y); } } - adjoint auto; + + adjoint invert; } - + + // Task 1.2. Bitwise right shift - operation Oracle_BitwiseRightShift_Reference(x : Qubit[], y : Qubit[]) : () - { - body { + operation Oracle_BitwiseRightShift_Reference (x : Qubit[], y : Qubit[]) : Unit { + + body (...) { let N = Length(x); - for(i in 1..N-1) - { - CNOT(x[i-1], y[i]); + + for (i in 1 .. N - 1) { + CNOT(x[i - 1], y[i]); } } - adjoint auto; + + adjoint invert; } - + + // Task 1.3. Linear operator - operation Oracle_OperatorOutput_Reference (x : Qubit[], y : Qubit, A : Int[]) : () - { - body - { + operation Oracle_OperatorOutput_Reference (x : Qubit[], y : Qubit, A : Int[]) : Unit { + + body (...) { let N = Length(x); - - for (i in 0..N-1) - { - if (A[i] == 1) - { + + for (i in 0 .. N - 1) { + if (A[i] == 1) { CNOT(x[i], y); } } } - adjoint auto; + + adjoint invert; } - + + // Task 1.4. Multidimensional linear operator - operation Oracle_MultidimensionalOperatorOutput_Reference (x : Qubit[], y : Qubit[], A : Int[][]) : () - { - body - { + operation Oracle_MultidimensionalOperatorOutput_Reference (x : Qubit[], y : Qubit[], A : Int[][]) : Unit { + + body (...) { let N1 = Length(y); let N2 = Length(x); - - for (i in 0..N1-1) { - for (j in 0..N2-1) { - if (A[i][j] == 1) { + + for (i in 0 .. N1 - 1) { + for (j in 0 .. N2 - 1) { + if ((A[i])[j] == 1) { CNOT(x[j], y[i]); } } } } - adjoint auto; + + adjoint invert; } - + + ////////////////////////////////////////////////////////////////// // Part II. Simon's Algorithm ////////////////////////////////////////////////////////////////// - + // Task 2.1. State preparation for Simon's algorithm - operation SA_StatePrep_Reference (query : Qubit[]) : () - { - body - { + operation SA_StatePrep_Reference (query : Qubit[]) : Unit { + + body (...) { ApplyToEachA(H, query); } - adjoint auto; + + adjoint invert; } - + + // Task 2.2. Quantum part of Simon's algorithm - operation Simon_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit[]) => ())) : Int[] - { - body - { - mutable j = new Int[N]; - - // allocate N+N qubits - using (qs = Qubit[2*N]) - { - // split allocated qubits into input register and answer register - let x = qs[0..N-1]; - let y = qs[N..2*N-1]; - - // prepare qubits in the right state - SA_StatePrep_Reference(x); - - // apply oracle - Uf(x, y); - - // apply Hadamard to each qubit of the input register - ApplyToEach(H, x); - - // measure all qubits of the input register; - // the result of each measurement is converted to a Bool - for (i in 0..N-1) - { - if (M(x[i]) == One) - { - set j[i] = 1; - } + operation Simon_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit[]) => Unit)) : Int[] { + + mutable j = new Int[N]; + + // allocate N+N qubits + using (qs = Qubit[2 * N]) { + // split allocated qubits into input register and answer register + let x = qs[0 .. N - 1]; + let y = qs[N .. 2 * N - 1]; + + // prepare qubits in the right state + SA_StatePrep_Reference(x); + + // apply oracle + Uf(x, y); + + // apply Hadamard to each qubit of the input register + ApplyToEach(H, x); + + // measure all qubits of the input register; + // the result of each measurement is converted to a Bool + for (i in 0 .. N - 1) { + + if (M(x[i]) == One) { + set j[i] = 1; } - - // before releasing the qubits make sure they are all in |0⟩ states - ResetAll(qs); } - - return j; + + // before releasing the qubits make sure they are all in |0⟩ states + ResetAll(qs); } + + return j; } + } diff --git a/SimonsAlgorithm/SimonsAlgorithm.csproj b/SimonsAlgorithm/SimonsAlgorithm.csproj index d8d80eee..0b423042 100644 --- a/SimonsAlgorithm/SimonsAlgorithm.csproj +++ b/SimonsAlgorithm/SimonsAlgorithm.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.SimonsAlgorithm - - - + + + diff --git a/SimonsAlgorithm/Tasks.qs b/SimonsAlgorithm/Tasks.qs index 84ae14d2..83d5b01e 100644 --- a/SimonsAlgorithm/Tasks.qs +++ b/SimonsAlgorithm/Tasks.qs @@ -1,77 +1,81 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.SimonsAlgorithm -{ +namespace Quantum.Kata.SimonsAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Simon's Algorithm" kata is a series of exercises designed to teach a quantum algorithm for - // a problem of identifying a bit string that is implicitly defined (or, in other words, "hidden") by + + // "Simon's Algorithm" kata is a series of exercises designed to teach a quantum algorithm for + // a problem of identifying a bit string that is implicitly defined (or, in other words, "hidden") by // some oracle that satisfies certain conditions. It is arguably the most simple case of an (oracle) - // problem for which a quantum algorithm has a *provable* exponential advantage over any classical algorithm. - + // problem for which a quantum algorithm has a *provable* exponential advantage over any classical algorithm. + // Each task is wrapped in one operation preceded by the description of the task. Each task (except tasks in // which you have to write a test) has a unit test associated with it, which initially fails. Your goal is to // fill in the blank (marked with // ... comment) with some Q# code to make the failing test pass. - + ////////////////////////////////////////////////////////////////// // Part I. Oracles ////////////////////////////////////////////////////////////////// - + // Task 1.1. f(x) = 𝑥₀ ⊕ ... ⊕ xₙ₋₁ (i.e., the parity of a given bit string) // Inputs: - // 1) N qubits in an arbitrary state |x⟩ + // 1) N qubits in an arbitrary state |x⟩ // 2) a qubit in an arbitrary state |y⟩ // Goal: Transform state |x, y⟩ into |x, y ⊕ x_0 ⊕ x_1 ... ⊕ x_{n-1}⟩ (⊕ is addition modulo 2). - operation Oracle_CountBits (x : Qubit[], y : Qubit) : () - { - body - { + operation Oracle_CountBits (x : Qubit[], y : Qubit) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.2. Bitwise right shift // Inputs: - // 1) N qubits in an arbitrary state |x⟩ + // 1) N qubits in an arbitrary state |x⟩ // 2) N qubits in an arbitrary state |y⟩ // Goal: Transform state |x, y⟩ into |x, y ⊕ f(x)⟩, where f is bitwise right shift function, i.e., // |y ⊕ f(x)⟩ = |y_0, y_1 ⊕ x_0, y_2 ⊕ x_1, ..., y_{n-1} ⊕ x_{n-2}⟩ (⊕ is addition modulo 2). - operation Oracle_BitwiseRightShift (x : Qubit[], y : Qubit[]) : () - { - body - { + operation Oracle_BitwiseRightShift (x : Qubit[], y : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.3. Linear operator // Inputs: - // 1) N qubits in an arbitrary state |x⟩ + // 1) N qubits in an arbitrary state |x⟩ // 2) a qubit in an arbitrary state |y⟩ // 3) a 1xN binary matrix (represented as an Int[]) describing operator A // (see https://en.wikipedia.org/wiki/Transformation_matrix ) // Goal: Transform state |x, y⟩ into |x, y ⊕ A(x) ⟩ (⊕ is addition modulo 2). - operation Oracle_OperatorOutput (x : Qubit[], y : Qubit, A : Int[]) : () - { - body - { + operation Oracle_OperatorOutput (x : Qubit[], y : Qubit, A : Int[]) : Unit { + + body (...) { // The following line enforces the constraint on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertIntEqual(Length(x), Length(A), "Arrays x and A should have the same length"); - + // ... } - adjoint auto; + + adjoint invert; } - + + // Task 1.4. Multidimensional linear operator // Inputs: // 1) N1 qubits in an arbitrary state |x⟩ (input register) @@ -79,42 +83,43 @@ namespace Quantum.Kata.SimonsAlgorithm // 3) an N2 x N1 matrix (represented as an Int[][]) describing operator A // (see https://en.wikipedia.org/wiki/Transformation_matrix ). // The first dimension of the matrix (rows) corresponds to the output register, - // the second dimension (columns) - the input register, + // the second dimension (columns) - the input register, // i.e., A[r][c] (element in r-th row and c-th column) corresponds to x[c] and y[r]. // Goal: Transform state |x, y⟩ into |x, y ⊕ A(x) ⟩ (⊕ is addition modulo 2). - operation Oracle_MultidimensionalOperatorOutput (x : Qubit[], y : Qubit[], A : Int[][]) : () - { - body - { + operation Oracle_MultidimensionalOperatorOutput (x : Qubit[], y : Qubit[], A : Int[][]) : Unit { + + body (...) { // The following lines enforce the constraints on the input arrays. // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. AssertIntEqual(Length(x), Length(A[0]), "Arrays x and A[0] should have the same length"); AssertIntEqual(Length(y), Length(A), "Arrays y and A should have the same length"); - + // ... } - adjoint auto; + + adjoint invert; } - + ////////////////////////////////////////////////////////////////// // Part II. Simon's Algorithm ////////////////////////////////////////////////////////////////// - + // Task 2.1. State preparation for Simon's algorithm // Inputs: // 1) N qubits in |0⟩ state (query register) // Goal: create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ on query register // (i.e. the state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N)). - operation SA_StatePrep (query : Qubit[]) : () - { - body - { + operation SA_StatePrep (query : Qubit[]) : Unit { + + body (...) { // ... } - adjoint auto; + + adjoint invert; } - + + // Task 2.2. Quantum part of Simon's algorithm // Inputs: // 1) the number of qubits in the input register N for the function f @@ -122,30 +127,28 @@ namespace Quantum.Kata.SimonsAlgorithm // x is N-qubit input register, y is N-qubit answer register, and f is a function // from N-bit strings into N-bit strings // - // The function f is guaranteed to satisfy the following property: + // The function f is guaranteed to satisfy the following property: // there exists some N-bit string s such that for all N-bit strings b and c (b != c) // we have f(b) = f(c) if and only if b = c ⊕ s. In other words, f is a two-to-one function. // // An example of such function is bitwise right shift function from task 1.2; // the bit string s for it is [0, ..., 0, 1]. - // + // // Output: // Any bit string b such that Σᵢ cᵢ sᵢ = 0 modulo 2. // - // Note that the whole algorithm will reconstruct the bit string s itself, but the quantum part of the - // algorithm will only find some vector orthogonal to the bit string s. The classical post-processing + // Note that the whole algorithm will reconstruct the bit string s itself, but the quantum part of the + // algorithm will only find some vector orthogonal to the bit string s. The classical post-processing // part is already implemented, so once you implement the quantum part, the tests will pass. - operation Simon_Algorithm (N : Int, Uf : ((Qubit[], Qubit[]) => ())) : Int[] - { - body - { - // Declare an Int array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable b = new Int[N]; + operation Simon_Algorithm (N : Int, Uf : ((Qubit[], Qubit[]) => Unit)) : Int[] { + + // Declare an Int array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable b = new Int[N]; + + // ... - // ... - - return b; - } + return b; } + } diff --git a/SimonsAlgorithm/Tests.qs b/SimonsAlgorithm/Tests.qs index d266ab8a..d21b1c7e 100644 --- a/SimonsAlgorithm/Tests.qs +++ b/SimonsAlgorithm/Tests.qs @@ -1,213 +1,177 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains parts of the testing harness. +// This file contains parts of the testing harness. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.SimonsAlgorithm -{ +namespace Quantum.Kata.SimonsAlgorithm { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ - operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => () : Adjoint)) : () - { - body - { + operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + + body (...) { let N = Length(qs); - oracle(qs[0..N-2], qs[N-1]); + oracle(qs[0 .. N - 2], qs[N - 1]); } - adjoint auto; + + adjoint invert; } - - operation ApplyOracleWithOutputArrA (qs : Qubit[], oracle : ((Qubit[], Qubit[]) => () : Adjoint), outputSize : Int) : () - { - body - { + + + operation ApplyOracleWithOutputArrA (qs : Qubit[], oracle : ((Qubit[], Qubit[]) => Unit : Adjoint), outputSize : Int) : Unit { + + body (...) { let N = Length(qs); - oracle(qs[0..N-1-outputSize], qs[N-outputSize..N-1]); + oracle(qs[0 .. (N - 1) - outputSize], qs[N - outputSize .. N - 1]); } - adjoint auto; + + adjoint invert; } - + + // ------------------------------------------------------ operation AssertTwoOraclesAreEqual ( - nQubits : Range, - oracle1 : ((Qubit[], Qubit) => () : Adjoint), - oracle2 : ((Qubit[], Qubit) => () : Adjoint)) : () - { - body - { - let sol = ApplyOracleA(_, oracle1); - let refSol = ApplyOracleA(_, oracle2); - for (i in nQubits) { - AssertOperationsEqualReferenced(sol, refSol, i+1); - } + nQubits : Range, + oracle1 : ((Qubit[], Qubit) => Unit : Adjoint), + oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + let sol = ApplyOracleA(_, oracle1); + let refSol = ApplyOracleA(_, oracle2); + + for (i in nQubits) { + AssertOperationsEqualReferenced(sol, refSol, i + 1); } } - + + operation AssertTwoOraclesWithOutputArrAreEqual ( - inputSize : Int, - outputSize : Int, - oracle1 : ((Qubit[], Qubit[]) => () : Adjoint), - oracle2 : ((Qubit[], Qubit[]) => () : Adjoint)) : () - { - body - { - let sol = ApplyOracleWithOutputArrA(_, oracle1, outputSize); - let refSol = ApplyOracleWithOutputArrA(_, oracle2, outputSize); - AssertOperationsEqualReferenced(sol, refSol, inputSize + outputSize); + inputSize : Int, + outputSize : Int, + oracle1 : ((Qubit[], Qubit[]) => Unit : Adjoint), + oracle2 : ((Qubit[], Qubit[]) => Unit : Adjoint)) : Unit { + let sol = ApplyOracleWithOutputArrA(_, oracle1, outputSize); + let refSol = ApplyOracleWithOutputArrA(_, oracle2, outputSize); + AssertOperationsEqualReferenced(sol, refSol, inputSize + outputSize); + } + + + // ------------------------------------------------------ + operation Q11_Oracle_CountBits_Test () : Unit { + AssertTwoOraclesAreEqual(1 .. 10, Oracle_CountBits, Oracle_CountBits_Reference); + } + + + // ------------------------------------------------------ + operation Q12_Oracle_BitwiseRightShift_Test () : Unit { + for (n in 2 .. 6) { + AssertTwoOraclesWithOutputArrAreEqual(n, n, Oracle_BitwiseRightShift, Oracle_BitwiseRightShift_Reference); } } - // ------------------------------------------------------ - operation Q11_Oracle_CountBits_Test () : () - { - body - { - AssertTwoOraclesAreEqual(1..10, Oracle_CountBits, Oracle_CountBits_Reference); - } - } - - // ------------------------------------------------------ - operation Q12_Oracle_BitwiseRightShift_Test () : () - { - body - { - for (n in 2..6) { - AssertTwoOraclesWithOutputArrAreEqual(n, n, Oracle_BitwiseRightShift, Oracle_BitwiseRightShift_Reference); - } - } - } // ------------------------------------------------------ - operation AssertTwoOraclesWithIntArrAreEqual ( - A : Int[], - oracle1 : ((Qubit[], Qubit, Int[]) => () : Adjoint), - oracle2 : ((Qubit[], Qubit, Int[]) => () : Adjoint)) : () - { - body - { - AssertTwoOraclesAreEqual(Length(A)..Length(A), oracle1(_, _, A), oracle2(_, _, A)); - } + operation AssertTwoOraclesWithIntArrAreEqual (A : Int[], oracle1 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint), oracle2 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint)) : Unit { + AssertTwoOraclesAreEqual(Length(A) .. Length(A), oracle1(_, _, A), oracle2(_, _, A)); } - - operation Q13_Oracle_OperatorOutput_Test () : () - { - body - { - // cross-tests - // the mask for all 1's should behave the same as Oracle_CountBits - mutable A = [1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1]; - let L = Length(A); - for (i in 2..L) - { - AssertTwoOraclesAreEqual(i..i, Oracle_OperatorOutput(_, _, A[0..i-1]), Oracle_OperatorOutput_Reference(_, _, A[0..i-1])); - } - - set A = [1; 1; 0; 0]; - AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); - - set A = [0; 0; 0; 0; 0]; - AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); - - set A = [1; 0; 1; 1; 1]; - AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); - - set A = [0; 1; 0; 0]; - AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); + + + operation Q13_Oracle_OperatorOutput_Test () : Unit { + // cross-tests + // the mask for all 1's should behave the same as Oracle_CountBits + mutable A = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + let L = Length(A); + + for (i in 2 .. L) { + AssertTwoOraclesAreEqual(i .. i, Oracle_OperatorOutput(_, _, A[0 .. i - 1]), Oracle_OperatorOutput_Reference(_, _, A[0 .. i - 1])); } - } + + set A = [1, 1, 0, 0]; + AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); + set A = [0, 0, 0, 0, 0]; + AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); + + set A = [1, 0, 1, 1, 1]; + AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); + + set A = [0, 1, 0, 0]; + AssertTwoOraclesWithIntArrAreEqual(A, Oracle_OperatorOutput, Oracle_OperatorOutput_Reference); + } + + // ------------------------------------------------------ operation AssertTwoOraclesWithIntMatrixAreEqual ( - A : Int[][], - oracle1 : ((Qubit[], Qubit[], Int[][]) => () : Adjoint), - oracle2 : ((Qubit[], Qubit[], Int[][]) => () : Adjoint)) : () - { - body - { - let inputSize = Length(A[0]); - let outputSize = Length(A); - AssertTwoOraclesWithOutputArrAreEqual(inputSize, outputSize, oracle1(_, _, A), oracle2(_, _, A)); - } + A : Int[][], + oracle1 : ((Qubit[], Qubit[], Int[][]) => Unit : Adjoint), + oracle2 : ((Qubit[], Qubit[], Int[][]) => Unit : Adjoint)) : Unit { + let inputSize = Length(A[0]); + let outputSize = Length(A); + AssertTwoOraclesWithOutputArrAreEqual(inputSize, outputSize, oracle1(_, _, A), oracle2(_, _, A)); } - + + operation AssertTwoOraclesWithDifferentOutputsAreEqual ( - inputSize : Int, - oracle1 : ((Qubit[], Qubit[]) => () : Adjoint), - oracle2 : ((Qubit[], Qubit) => () : Adjoint)) : () - { - body - { - let sol = ApplyOracleWithOutputArrA(_, oracle1, 1); - let refSol = ApplyOracleA(_, oracle2); - AssertOperationsEqualReferenced(sol, refSol, inputSize+1); - } + inputSize : Int, + oracle1 : ((Qubit[], Qubit[]) => Unit : Adjoint), + oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + let sol = ApplyOracleWithOutputArrA(_, oracle1, 1); + let refSol = ApplyOracleA(_, oracle2); + AssertOperationsEqualReferenced(sol, refSol, inputSize + 1); } + + + operation Q14_Oracle_MultidimensionalOperatorOutput_Test () : Unit { + + mutable A = [[1, 1], [0, 0]]; + AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); + set A = [[1, 0], [0, 1], [1, 1]]; + AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); - operation Q14_Oracle_MultidimensionalOperatorOutput_Test () : () - { - body - { - mutable A = [[1; 1]; [0; 0]]; - AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); - - set A = [[1; 0]; [0; 1]; [1; 1]]; - AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); - - set A = [[0; 1; 0]; [1; 0; 1]]; - AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); - - // cross-test for bitwise right shift oracle - set A = [[0; 0; 0; 0]; - [1; 0; 0; 0]; - [0; 1; 0; 0]; - [0; 0; 1; 0]]; - AssertTwoOraclesWithOutputArrAreEqual(4, 4, Oracle_MultidimensionalOperatorOutput(_, _, A), Oracle_BitwiseRightShift_Reference); - - // cross-test for 1-dimensional output - mutable B = [1; 0; 1; 0; 1]; - AssertTwoOraclesWithDifferentOutputsAreEqual(5, Oracle_MultidimensionalOperatorOutput(_, _, [B]), Oracle_OperatorOutput_Reference(_, _, B)); - - // cross-test for bit counting oracle - set B = [1; 1; 1; 1; 1]; - AssertTwoOraclesWithDifferentOutputsAreEqual(5, Oracle_MultidimensionalOperatorOutput(_, _, [B]), Oracle_CountBits_Reference); - } + set A = [[0, 1, 0], [1, 0, 1]]; + AssertTwoOraclesWithIntMatrixAreEqual(A, Oracle_MultidimensionalOperatorOutput, Oracle_MultidimensionalOperatorOutput_Reference); + + // cross-test for bitwise right shift oracle + set A = [[0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]]; + AssertTwoOraclesWithOutputArrAreEqual(4, 4, Oracle_MultidimensionalOperatorOutput(_, _, A), Oracle_BitwiseRightShift_Reference); + + // cross-test for 1-dimensional output + mutable B = [1, 0, 1, 0, 1]; + AssertTwoOraclesWithDifferentOutputsAreEqual(5, Oracle_MultidimensionalOperatorOutput(_, _, [B]), Oracle_OperatorOutput_Reference(_, _, B)); + + // cross-test for bit counting oracle + set B = [1, 1, 1, 1, 1]; + AssertTwoOraclesWithDifferentOutputsAreEqual(5, Oracle_MultidimensionalOperatorOutput(_, _, [B]), Oracle_CountBits_Reference); } - - operation Q21_StatePrep_Test () : () - { - body - { - for (N in 1..10) { - using (qs = Qubit[N]) - { - // apply operation that needs to be tested - SA_StatePrep(qs[0..N-1]); - - // apply adjoint reference operation - (Adjoint SA_StatePrep_Reference)(qs[0..N-1]); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + + + operation Q21_StatePrep_Test () : Unit { + for (N in 1 .. 10) { + using (qs = Qubit[N]) { + // apply operation that needs to be tested + SA_StatePrep(qs[0 .. N - 1]); + + // apply adjoint reference operation + Adjoint SA_StatePrep_Reference(qs[0 .. N - 1]); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } } - + + // ------------------------------------------------------ - operation cs_helper(N: Int, Matrix: Int[][]) : (Int[], ((Qubit[], Qubit[]) => ())) - { - body - { - let Uf = Oracle_MultidimensionalOperatorOutput_Reference(_, _, Matrix); - return (Simon_Algorithm(N, Uf), Uf); - } + operation cs_helper (N : Int, Matrix : Int[][]) : (Int[], ((Qubit[], Qubit[]) => Unit)) { + let Uf = Oracle_MultidimensionalOperatorOutput_Reference(_, _, Matrix); + return (Simon_Algorithm(N, Uf), Uf); } -} \ No newline at end of file + +} diff --git a/SuperdenseCoding/ReferenceImplementation.qs b/SuperdenseCoding/ReferenceImplementation.qs index 70975f33..a6a73579 100644 --- a/SuperdenseCoding/ReferenceImplementation.qs +++ b/SuperdenseCoding/ReferenceImplementation.qs @@ -1,126 +1,123 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.SuperdenseCoding -{ +namespace Quantum.Kata.SuperdenseCoding { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + // Task 1. Entangled pair - operation CreateEntangledPair_Reference (qs : Qubit[]) : () - { - body - { - // The easiest way to create an entangled pair is to start with + operation CreateEntangledPair_Reference (qs : Qubit[]) : Unit { + + body (...) { + // The easiest way to create an entangled pair is to start with // applying a Hadamard transformation to one of the qubits: H(qs[0]); - + // This has left us in state: // ((|0⟩ + |1⟩) / sqrt(2)) ⊗ |0⟩ - + // Now, if we flip the second qubit conditioned on the state // of the first one, we get that the states of the two qubits will always match. CNOT(qs[0], qs[1]); - // So we ended up in the state: // (|00⟩ + |11⟩) / sqrt(2) // // Which is the required Bell pair |Φ⁺⟩ } - adjoint auto; + + adjoint invert; } - + + // Task 2. Send the message (Alice's task) - operation EncodeMessageInQubit_Reference (qAlice : Qubit, message : Bool[]) : () - { - body - { - // We are starting this step with the entangled pair in state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). - // By doing operations on one of those qubits, - // we can encode each of the values as a transformation: - - // "00" as I and |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) - // "01" as X and |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) - // "10" as Z and |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) - // "11" as Y and |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - - // Also, since Y(q) = iX(Z(q)), we can express this shorter: - - if (message[0]) { Z(qAlice); } - if (message[1]) { X(qAlice); } + operation EncodeMessageInQubit_Reference (qAlice : Qubit, message : Bool[]) : Unit { + // We are starting this step with the entangled pair in state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). + // By doing operations on one of those qubits, + // we can encode each of the values as a transformation: + + // "00" as I and |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) + // "01" as X and |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) + // "10" as Z and |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) + // "11" as Y and |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) + + // Also, since Y(q) = iX(Z(q)), we can express this shorter: + if (message[0]) { + Z(qAlice); + } + + if (message[1]) { + X(qAlice); } } - + + // Task 3. Decode the message (Bob's task) - operation DecodeMessageFromQubits_Reference (qBob : Qubit, qAlice : Qubit) : Bool[] - { - body - { - // Declare a Bool array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable decoded_bits = new Bool[2]; - - // Time to get our state back, by performing transformations as follows. - // Notice that it's important to keep the order right. The qubits that are - // subject to the Hadamard transform and the CNOT gate in the preparation - // of the pair have to match the operations below, or the order of the data - // bits will get flipped. - CNOT(qAlice, qBob); - H(qAlice); - - // What is the outcome of this transformation, assuming each of the possible - // quantum states after the encoding step? - - // |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) ---> |00⟩ - // |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) ---> |01⟩ - // |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) ---> |10⟩ - // |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) ---> |11⟩ - - // So we can retrieve the encoded bits just by measuring. - set decoded_bits[0] = M(qAlice) == One; - set decoded_bits[1] = M(qBob) == One; - - return decoded_bits; - } + operation DecodeMessageFromQubits_Reference (qBob : Qubit, qAlice : Qubit) : Bool[] { + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable decoded_bits = new Bool[2]; + + // Time to get our state back, by performing transformations as follows. + // Notice that it's important to keep the order right. The qubits that are + // subject to the Hadamard transform and the CNOT gate in the preparation + // of the pair have to match the operations below, or the order of the data + // bits will get flipped. + CNOT(qAlice, qBob); + H(qAlice); + + // What is the outcome of this transformation, assuming each of the possible + // quantum states after the encoding step? + + // |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) ---> |00⟩ + // |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) ---> |01⟩ + // |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) ---> |10⟩ + // |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) ---> |11⟩ + + // So we can retrieve the encoded bits just by measuring. + set decoded_bits[0] = M(qAlice) == One; + set decoded_bits[1] = M(qBob) == One; + + return decoded_bits; } - + + // Task 4. Superdense coding protocol end-to-end - operation SuperdenseCodingProtocol_Reference (message : Bool[]) : Bool[] - { - body - { - mutable decoded_bits = new Bool[2]; - - // Get a temporary qubit register for the protocol run. - using (qs = Qubit[2]) - { - // STEP 1: - // Start by creating an entangled pair of qubits. - CreateEntangledPair_Reference(qs); - // Alice and Bob receive one half of the pair each. - - // STEP 2: - // Alice encodes the pair of bits in the qubit she received. - EncodeMessageInQubit_Reference(qs[0], message); - // Alice sends her qubit to Bob. - - // STEP 3: - // Bob receives the qubit from Alice and can now - // manipulate and measure both qubits to get the encoded data. - set decoded_bits = DecodeMessageFromQubits_Reference(qs[1], qs[0]); - - // Make sure that we return qubits back in 0 state. - ResetAll(qs); - } - - return decoded_bits; + operation SuperdenseCodingProtocol_Reference (message : Bool[]) : Bool[] { + mutable decoded_bits = new Bool[2]; + + // Get a temporary qubit register for the protocol run. + using (qs = Qubit[2]) { + // STEP 1: + // Start by creating an entangled pair of qubits. + CreateEntangledPair_Reference(qs); + + // Alice and Bob receive one half of the pair each. + + // STEP 2: + // Alice encodes the pair of bits in the qubit she received. + EncodeMessageInQubit_Reference(qs[0], message); + + // Alice sends her qubit to Bob. + + // STEP 3: + // Bob receives the qubit from Alice and can now + // manipulate and measure both qubits to get the encoded data. + set decoded_bits = DecodeMessageFromQubits_Reference(qs[1], qs[0]); + + // Make sure that we return qubits back in 0 state. + ResetAll(qs); } + + return decoded_bits; } + } diff --git a/SuperdenseCoding/SuperdenseCoding.csproj b/SuperdenseCoding/SuperdenseCoding.csproj index 9e0e3365..df43055a 100644 --- a/SuperdenseCoding/SuperdenseCoding.csproj +++ b/SuperdenseCoding/SuperdenseCoding.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.SuperdenseCoding - - - + + + diff --git a/SuperdenseCoding/Tasks.qs b/SuperdenseCoding/Tasks.qs index f7e2c010..73e5efa0 100644 --- a/SuperdenseCoding/Tasks.qs +++ b/SuperdenseCoding/Tasks.qs @@ -1,65 +1,60 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.SuperdenseCoding -{ +namespace Quantum.Kata.SuperdenseCoding { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; - + + /////////////////////////////////////////////////////////////////////// // // // Superdense Coding Kata : Share 2 bits for the price of 1 qubit! // // // /////////////////////////////////////////////////////////////////////// - - // "Superdense Coding" quantum kata is a series of exercises designed + + // "Superdense Coding" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. - // It covers the superdense coding protocol which allows to transmit - // two bits of classical information by sending just one qubit + // It covers the superdense coding protocol which allows to transmit + // two bits of classical information by sending just one qubit // using previously shared quantum entanglement. - + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // + // Each task defines an operation that can be used in subsequent tasks to simplify implementations // and build on existing code. - + // We split the superdense coding protocol into several steps, following the description at // https://en.wikipedia.org/wiki/Superdense_coding : // * Preparation (creating the entangled pair of qubits that are sent to Alice and Bob). // * Encoding the message (Alice's task): Encoding the classical bits of the message // into the state of Alice's qubit which then is sent to Bob. - // * Decoding the message (Bob's task): Using Bob's original qubit and the qubit he + // * Decoding the message (Bob's task): Using Bob's original qubit and the qubit he // received from Alice to decode the classical message sent. // Finally, we compose those steps into the complete superdense coding protocol. - + // Task 1. Entangled pair // Input: An array of two qubits in the |00⟩ state. // Goal: Create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits. - operation CreateEntangledPair (qs : Qubit[]) : () - { - body - { - // The following lines enforce the constraints on the input that you are given. - // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. - AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); + operation CreateEntangledPair (qs : Qubit[]) : Unit { + // The following lines enforce the constraints on the input that you are given. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); - // ... - } + // ... } - + + // Task 2. Send the message (Alice's task) // Encode the message (classical bits) in the state of Alice's qubit. // Inputs: // 1) Alice's part of the entangled pair of qubits qAlice. // 2) two classical bits, stored in an array. // Goal: Transform the input qubit to encode the two classical bits. - operation EncodeMessageInQubit (qAlice : Qubit, message : Bool[]) : () - { - body - { + operation EncodeMessageInQubit (qAlice : Qubit, message : Bool[]) : Unit { // Hint: manipulate Alice's half of the entangled pair // to change the joint state of the two qubits to one of the following four states // based on the value of message: @@ -69,9 +64,9 @@ namespace Quantum.Kata.SuperdenseCoding // [1; 1]: |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) // ... - } } - + + // Task 3. Decode the message (Bob's task) // Decode the message using the qubit received from Alice. // Inputs: @@ -79,35 +74,30 @@ namespace Quantum.Kata.SuperdenseCoding // 2) qubit received from Alice qAlice. // Goal: Retrieve two bits of classic data from the qubits. // The state of the qubits in the end of the operation doesn't matter. - operation DecodeMessageFromQubits (qBob : Qubit, qAlice : Qubit) : Bool[] - { - body - { - // Declare a Bool array in which the result will be stored; - // the array has to be mutable to allow updating its elements. - mutable decoded_bits = new Bool[2]; + operation DecodeMessageFromQubits (qBob : Qubit, qAlice : Qubit) : Bool[] { + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable decoded_bits = new Bool[2]; + + // ... - // ... - - return decoded_bits; - } + return decoded_bits; } - + + // Task 4. Superdense coding protocol end-to-end // Put together the steps performed in tasks 1-3 to implement the full superdense coding protocol. // Input: Two classical bits // Goal: Prepare an EPR Pair, encode the two classical bits in the // state of the pair by applying quantum gates to one member of the pair, // and decode the two classical gates from the state of the pair - operation SuperdenseCodingProtocol (message : Bool[]) : Bool[] - { - body - { - mutable decoded_bits = new Bool[2]; + operation SuperdenseCodingProtocol (message : Bool[]) : Bool[] { + + mutable decoded_bits = new Bool[2]; + + // ... - // ... - - return decoded_bits; - } + return decoded_bits; } + } diff --git a/SuperdenseCoding/Tests.qs b/SuperdenseCoding/Tests.qs index cf5ebaad..66d144ec 100644 --- a/SuperdenseCoding/Tests.qs +++ b/SuperdenseCoding/Tests.qs @@ -1,116 +1,92 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.SuperdenseCoding -{ +namespace Quantum.Kata.SuperdenseCoding { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ - operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => ()), refImpl : (Qubit[] => () : Adjoint)) : () - { - body - { - using (qs = Qubit[N]) - { - // apply operation that needs to be tested - taskImpl(qs); - - // apply adjoint reference operation and check that the result is |0^N⟩ - (Adjoint refImpl)(qs); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + using (qs = Qubit[N]) { + // apply operation that needs to be tested + taskImpl(qs); + + // apply adjoint reference operation and check that the result is |0^N⟩ + Adjoint refImpl(qs); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } - - operation T1_CreateEntangledPair_Test () : () - { - body - { - // We only check for 2 qubits. - AssertEqualOnZeroState(2, CreateEntangledPair, CreateEntangledPair_Reference); - } + + + operation T1_CreateEntangledPair_Test () : Unit { + // We only check for 2 qubits. + AssertEqualOnZeroState(2, CreateEntangledPair, CreateEntangledPair_Reference); } - + + // ------------------------------------------------------ // Helper operation that runs superdense coding protocol using two building blocks // specified as first two parameters. - operation ComposeProtocol ( - encodeOp : ((Qubit, Bool[]) => ()), - decodeOp : ((Qubit, Qubit) => Bool[]), - message : Bool[] - ) : Bool[] - { - body - { - mutable result = new Bool[2]; - using (qs = Qubit[2]) { - CreateEntangledPair_Reference(qs); - encodeOp(qs[0], message); - set result = decodeOp(qs[1], qs[0]); - // Make sure that we return qubits back in 0 state. - ResetAll(qs); - } - return result; + operation ComposeProtocol (encodeOp : ((Qubit, Bool[]) => Unit), decodeOp : ((Qubit, Qubit) => Bool[]), message : Bool[]) : Bool[] { + mutable result = new Bool[2]; + + using (qs = Qubit[2]) { + CreateEntangledPair_Reference(qs); + encodeOp(qs[0], message); + set result = decodeOp(qs[1], qs[0]); + + // Make sure that we return qubits back in 0 state. + ResetAll(qs); } + + return result; } - + + // ------------------------------------------------------ // Helper operation that runs superdense coding protocol (specified by protocolOp) // on all possible input values and verifies that decoding result matches the inputs - operation TestProtocol ( - protocolOp : ((Bool[]) => Bool[]) - ) : () - { - body - { - mutable data = new Bool[2]; - - // Loop over the 4 possible combinations of two bits - for (n in 0..3) - { - set data[0] = 1 == (n / 2); - set data[1] = 1 == (n % 2); - for (iter in 1..100) { - let result = protocolOp(data); - - // Now test if the bits were transfered correctly. - AssertBoolArrayEqual(result, data, $"The message {data} was transfered incorrectly as {result}" ); - } + operation TestProtocol (protocolOp : (Bool[] => Bool[])) : Unit { + mutable data = new Bool[2]; + + // Loop over the 4 possible combinations of two bits + for (n in 0 .. 3) { + set data[0] = 1 == n / 2; + set data[1] = 1 == n % 2; + + for (iter in 1 .. 100) { + let result = protocolOp(data); + + // Now test if the bits were transfered correctly. + AssertBoolArrayEqual(result, data, $"The message {data} was transfered incorrectly as {result}"); } } } - - operation T2_EncodeMessageInQubit_Test () : () - { - body - { - TestProtocol(ComposeProtocol(EncodeMessageInQubit, DecodeMessageFromQubits_Reference, _)); - } + + + operation T2_EncodeMessageInQubit_Test () : Unit { + TestProtocol(ComposeProtocol(EncodeMessageInQubit, DecodeMessageFromQubits_Reference, _)); } - - operation T3_DecodeMessageFromQubits_Test () : () - { - body - { - TestProtocol(ComposeProtocol(EncodeMessageInQubit_Reference, DecodeMessageFromQubits, _)); - } + + + operation T3_DecodeMessageFromQubits_Test () : Unit { + TestProtocol(ComposeProtocol(EncodeMessageInQubit_Reference, DecodeMessageFromQubits, _)); } - - operation T4_SuperdenseCodingProtocol_Test () : () - { - body - { - TestProtocol(SuperdenseCodingProtocol); - } + + + operation T4_SuperdenseCodingProtocol_Test () : Unit { + TestProtocol(SuperdenseCodingProtocol); } -} \ No newline at end of file + +} diff --git a/Superposition/ReferenceImplementation.qs b/Superposition/ReferenceImplementation.qs index cf818a07..56750e28 100644 --- a/Superposition/ReferenceImplementation.qs +++ b/Superposition/ReferenceImplementation.qs @@ -1,114 +1,121 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // We recommend that you try to solve the tasks yourself first, // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Superposition -{ +namespace Quantum.Kata.Superposition { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; - + + // Task 1. Plus state // Input: a qubit in |0⟩ state (stored in an array of length 1). // Goal: create a |+⟩ state on this qubit (|+⟩ = (|0⟩ + |1⟩) / sqrt(2)). - operation PlusState_Reference (qs : Qubit[]) : () - { - body - { + operation PlusState_Reference (qs : Qubit[]) : Unit { + + body (...) { H(qs[0]); } - adjoint auto; + + adjoint invert; } - + + // Task 2. Minus state // Input: a qubit in |0⟩ state (stored in an array of length 1). // Goal: create a |-⟩ state on this qubit (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)). - operation MinusState_Reference (qs : Qubit[]) : () - { - body - { + operation MinusState_Reference (qs : Qubit[]) : Unit { + + body (...) { X(qs[0]); H(qs[0]); } - adjoint auto; + + adjoint invert; } - + + // Task 3. Unequal superposition // Inputs: // 1) a qubit in |0⟩ state (stored in an array of length 1). // 2) angle alpha, in radians, represented as Double // Goal: create a cos(alpha) * |0⟩ + sin(alpha) * |1⟩ state on this qubit. - operation UnequalSuperposition_Reference (qs : Qubit[], alpha : Double) : () - { - body - { + operation UnequalSuperposition_Reference (qs : Qubit[], alpha : Double) : Unit { + + body (...) { // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive Ry(2.0 * alpha, qs[0]); } - adjoint auto; + + adjoint invert; } - + + // Task 4. Superposition of all basis vectors on two qubits - operation AllBasisVectors_TwoQubits_Reference (qs : Qubit[]) : () - { - body - { + operation AllBasisVectors_TwoQubits_Reference (qs : Qubit[]) : Unit { + + body (...) { // Since a Hadamard gate will change |0⟩ into |+⟩ = (|0⟩ + |1⟩)/sqrt(2) // And the desired state is just a tensor product |+⟩|+⟩, we can apply // a Hadamard transformation to each qubit. H(qs[0]); H(qs[1]); } - adjoint auto; + + adjoint invert; } - + + // Task 5. Superposition of basis vectors with phases - operation AllBasisVectorsWithPhases_TwoQubits_Reference (qs : Qubit[]) : () - { - body - { + operation AllBasisVectorsWithPhases_TwoQubits_Reference (qs : Qubit[]) : Unit { + + body (...) { // Question: // Is this state separable? - + // Answer: // Yes. It is. We can see that: // ((|0⟩ - |1⟩) / sqrt(2)) ⊗ ((|0⟩ + i*|1⟩) / sqrt(2)) is equal to the desired // state, so we can create it by doing operations on each qubit independently. - - // We can see that the first qubit is in state |-⟩ and the second in state |i⟩, + + // We can see that the first qubit is in state |-⟩ and the second in state |i⟩, // so the transformations that we need are: - + // Qubit 0 is taken into |+⟩ and then z-rotated into |-⟩. H(qs[0]); Z(qs[0]); - + // Qubit 1 is taken into |+⟩ and then z-rotated into |i⟩. H(qs[1]); S(qs[1]); } - adjoint auto; + + adjoint invert; } - + + // Task 6. Bell state // Input: two qubits in |00⟩ state (stored in an array of length 2). // Goal: create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits. - operation BellState_Reference (qs : Qubit[]) : () - { - body - { + operation BellState_Reference (qs : Qubit[]) : Unit { + + body (...) { H(qs[0]); CNOT(qs[0], qs[1]); } - adjoint auto; + + adjoint invert; } - + + // Task 7. All Bell states // Inputs: // 1) two qubits in |00⟩ state (stored in an array of length 2) @@ -118,12 +125,12 @@ namespace Quantum.Kata.Superposition // 1: |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2: |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3: |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation AllBellStates_Reference (qs : Qubit[], index : Int) : () - { - body - { + operation AllBellStates_Reference (qs : Qubit[], index : Int) : Unit { + + body (...) { H(qs[0]); CNOT(qs[0], qs[1]); + // now we have |00⟩ + |11⟩ - modify it based on index arg if (index % 2 == 1) { // negative phase @@ -133,39 +140,44 @@ namespace Quantum.Kata.Superposition X(qs[1]); } } - adjoint auto; + + adjoint invert; } - + + // Task 8. Greenberger–Horne–Zeilinger state // Input: N qubits in |0...0⟩ state. // Goal: create a GHZ state (|0...0⟩ + |1...1⟩) / sqrt(2) on these qubits. - operation GHZ_State_Reference (qs : Qubit[]) : () - { - body - { + operation GHZ_State_Reference (qs : Qubit[]) : Unit { + + body (...) { H(qs[0]); - for (i in 1 .. Length(qs)-1) { + + for (i in 1 .. Length(qs) - 1) { CNOT(qs[0], qs[i]); } } - adjoint auto; + + adjoint invert; } - + + // Task 9. Superposition of all basis vectors // Input: N qubits in |0...0⟩ state. // Goal: create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ // (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ). - operation AllBasisVectorsSuperposition_Reference (qs : Qubit[]) : () - { - body - { - for (i in 0 .. Length(qs)-1) { + operation AllBasisVectorsSuperposition_Reference (qs : Qubit[]) : Unit { + + body (...) { + for (i in 0 .. Length(qs) - 1) { H(qs[i]); } } - adjoint auto; + + adjoint invert; } - + + // Task 10. Superposition of |0...0⟩ and given bit string // Inputs: // 1) N qubits in |0...0⟩ state @@ -174,61 +186,61 @@ namespace Quantum.Kata.Superposition // Bit values false and true correspond to |0⟩ and |1⟩ states. // You are guaranteed that the qubit array and the bit string have the same length. // You are guaranteed that the first bit of the bit string is true. - // Example: for bit string = [true; false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2). - operation ZeroAndBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[]) : () - { - body - { + // Example: for bit string = [true, false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2). + operation ZeroAndBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[]) : Unit { + + body (...) { AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length"); AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true"); - + // Hadamard first qubit H(qs[0]); - + // iterate through the bit string and CNOT to qubits corresponding to true bits - for (i in 1..Length(qs)-1) { + for (i in 1 .. Length(qs) - 1) { if (bits[i]) { CNOT(qs[0], qs[i]); } } } - adjoint auto; + + adjoint invert; } - + + // Task 11. Superposition of two bit strings // Inputs: // 1) N qubits in |0...0⟩ state // 2) two bit string represented as Bool[]s // Goal: create an equal superposition of two basis states given by the bit strings. // Bit values false and true correspond to |0⟩ and |1⟩ states. - // Example: for bit strings [false; true; false] and [false; false; true] + // Example: for bit strings [false, true, false] and [false, false, true] // the qubit state required is (|010⟩ + |001⟩) / sqrt(2). // You are guaranteed that the two bit strings will be different. - + // helper function for TwoBitstringSuperposition_Reference - function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int - { + function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int { mutable firstDiff = -1; - for (i in 0 .. Length(bits1)-1) { + for (i in 0 .. Length(bits1) - 1) { if (bits1[i] != bits2[i] && firstDiff == -1) { set firstDiff = i; } } return firstDiff; } - - operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : () - { - body - { + + + operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Unit { + + body (...) { // find the index of the first bit at which the bit strings are different let firstDiff = FindFirstDiff_Reference(bits1, bits2); - + // Hadamard corresponding qubit to create superposition H(qs[firstDiff]); - + // iterate through the bit strings again setting the final state of qubits - for (i in 0 .. Length(qs)-1) { + for (i in 0 .. Length(qs) - 1) { if (bits1[i] == bits2[i]) { // if two bits are the same apply X or nothing if (bits1[i]) { @@ -245,55 +257,61 @@ namespace Quantum.Kata.Superposition } } } - adjoint auto; + + adjoint invert; } - + + // Task 12. W state on 2^k qubits // Input: N = 2^k qubits in |0...0⟩ state. // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. // Example: for N = 4, W state is (|1000⟩ + |0100⟩ + |0010⟩ + |0001⟩) / 2. - operation WState_PowerOfTwo_Reference (qs : Qubit[]) : () - { - body - { + operation WState_PowerOfTwo_Reference (qs : Qubit[]) : Unit { + + body (...) { let N = Length(qs); + if (N == 1) { // base of recursion: |1⟩ X(qs[0]); } else { let K = N / 2; + // create W state on the first K qubits - WState_PowerOfTwo_Reference(qs[0..K-1]); - + WState_PowerOfTwo_Reference(qs[0 .. K - 1]); + // the next K qubits are in |0...0⟩ state // allocate ancilla in |+⟩ state using (anc = Qubit[1]) { H(anc[0]); - for (i in 0..K-1) { - (Controlled SWAP)(anc, (qs[i], qs[i+K])); + + for (i in 0 .. K - 1) { + Controlled SWAP(anc, (qs[i], qs[i + K])); } - for (i in K..N-1) { + for (i in K .. N - 1) { CNOT(qs[i], anc[0]); } } } } - adjoint auto; + + adjoint invert; } - + + // Task 13. W state on arbitrary number of qubits // Input: N qubits in |0...0⟩ state (N is not necessarily a power of 2). // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. // Example: for N = 3, W state is (|100⟩ + |010⟩ + |001⟩) / sqrt(3). - + // general solution based on rotations and recursive application of controlled generation routine - operation WState_Arbitrary_Reference (qs : Qubit[]) : () - { - body - { + operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit { + + body (...) { let N = Length(qs); + if (N == 1) { // base case of recursion: |1⟩ X(qs[0]); @@ -303,59 +321,62 @@ namespace Quantum.Kata.Superposition // |0⟩ -> sqrt((N-1)/N) |0⟩ + 1/sqrt(N) |1⟩ let theta = ArcSin(1.0 / Sqrt(ToDouble(N))); Ry(2.0 * theta, qs[0]); + // do a zero-controlled W-state generation for qubits 1..N-1 X(qs[0]); - (Controlled WState_Arbitrary_Reference)(qs[0..0], qs[1..N-1]); + Controlled WState_Arbitrary_Reference(qs[0 .. 0], qs[1 .. N - 1]); X(qs[0]); } } - adjoint auto; - controlled auto; - adjoint controlled auto; + + adjoint invert; + controlled distribute; + controlled adjoint distribute; } - + + // solution based on generation for 2^k and post-selection using measurements - operation WState_Arbitrary_Postselect (qs : Qubit[]) : () - { - body - { - let N = Length(qs); - if (N == 1) { - // base case of recursion: |1⟩ - X(qs[0]); - } else { - // find the smallest power of 2 which is greater than or equal to N - // as a hack, we know we're not doing it on more than 64 qubits - mutable P = 1; - for (i in 1..6) { - if (P < N) { - set P = P * 2; - } + operation WState_Arbitrary_Postselect (qs : Qubit[]) : Unit { + let N = Length(qs); + + if (N == 1) { + // base case of recursion: |1⟩ + X(qs[0]); + } else { + // find the smallest power of 2 which is greater than or equal to N + // as a hack, we know we're not doing it on more than 64 qubits + mutable P = 1; + for (i in 1 .. 6) { + if (P < N) { + set P = P * 2; } - - if (P == N) { - // prepare as a power of 2 (previous task) - WState_PowerOfTwo_Reference(qs); - } else { - // allocate extra qubits - using (ans = Qubit[P-N]) { - let all_qubits = qs + ans; - repeat { - // prepare state W_P on original + ancilla qubits - WState_PowerOfTwo_Reference(all_qubits); - - // measure ancilla qubits; if all of the results are Zero, we get the right state on main qubits - mutable allZeros = true; - for (i in 0..P-N-1) { - set allZeros = allZeros && IsResultZero(M(ans[i])); - } - } until allZeros - fixup { - ResetAll(ans); + } + + if (P == N) { + // prepare as a power of 2 (previous task) + WState_PowerOfTwo_Reference(qs); + } else { + // allocate extra qubits + using (ans = Qubit[P - N]) { + let all_qubits = qs + ans; + + repeat { + // prepare state W_P on original + ancilla qubits + WState_PowerOfTwo_Reference(all_qubits); + + // measure ancilla qubits; if all of the results are Zero, we get the right state on main qubits + mutable allZeros = true; + for (i in 0 .. (P - N) - 1) { + set allZeros = allZeros && IsResultZero(M(ans[i])); } } + until (allZeros) + fixup { + ResetAll(ans); + } } } } } + } diff --git a/Superposition/Superposition.csproj b/Superposition/Superposition.csproj index b0074c1e..15980971 100644 --- a/Superposition/Superposition.csproj +++ b/Superposition/Superposition.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.Superposition - - - + + + diff --git a/Superposition/Tasks.qs b/Superposition/Tasks.qs index e6931144..14483051 100644 --- a/Superposition/Tasks.qs +++ b/Superposition/Tasks.qs @@ -1,118 +1,101 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.Superposition -{ +namespace Quantum.Kata.Superposition { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Superposition" quantum kata is a series of exercises designed + + // "Superposition" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. // It covers the following topics: // - basic single-qubit and multi-qubit gates, // - superposition, // - flow control and recursion in Q#. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - // - // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. - + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + // Task 1. Plus state // Input: a qubit in |0⟩ state (stored in an array of length 1). // Goal: create a |+⟩ state on this qubit (|+⟩ = (|0⟩ + |1⟩) / sqrt(2)). - operation PlusState (qs : Qubit[]) : () - { - body - { - // Hadamard gate H will convert |0⟩ state to |+⟩ state. - // The first qubit of the array can be accessed as qs[0]. - // Type the following: H(qs[0]); - // Then rebuild the project and rerun the tests - T01_PlusState_Test should now pass! + operation PlusState (qs : Qubit[]) : Unit { + // Hadamard gate H will convert |0⟩ state to |+⟩ state. + // The first qubit of the array can be accessed as qs[0]. + // Type the following: H(qs[0]); + // Then rebuild the project and rerun the tests - T01_PlusState_Test should now pass! - // ... - } + // ... } - + + // Task 2. Minus state // Input: a qubit in |0⟩ state (stored in an array of length 1). // Goal: create a |-⟩ state on this qubit (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)). - operation MinusState (qs : Qubit[]) : () - { - body - { - // In this task, as well as in all subsequent ones, you have to come up with the solution yourself. + operation MinusState (qs : Qubit[]) : Unit { + // In this task, as well as in all subsequent ones, you have to come up with the solution yourself. - // ... - } + // ... } - + + // Task 3*. Unequal superposition // Inputs: // 1) a qubit in |0⟩ state (stored in an array of length 1). // 2) angle alpha, in radians, represented as Double // Goal: create a cos(alpha) * |0⟩ + sin(alpha) * |1⟩ state on this qubit. - operation UnequalSuperposition (qs : Qubit[], alpha : Double) : () - { - body - { - // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive namespace. - // Note that all rotation operators rotate the state by _half_ of its angle argument. + operation UnequalSuperposition (qs : Qubit[], alpha : Double) : Unit { + // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive namespace. + // Note that all rotation operators rotate the state by _half_ of its angle argument. - // ... - } + // ... } - + + // Task 4. Superposition of all basis vectors on two qubits // Input: two qubits in |00⟩ state (stored in an array of length 2). // Goal: create the following state on these qubits: (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. - operation AllBasisVectors_TwoQubits (qs : Qubit[]) : () - { - body - { - // The following lines enforce the constraints on the input that you are given. - // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. - AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); + operation AllBasisVectors_TwoQubits (qs : Qubit[]) : Unit { + // The following lines enforce the constraints on the input that you are given. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); - // ... - } + // ... } - + + // Task 5. Superposition of basis vectors with phases // Input: two qubits in |00⟩ state (stored in an array of length 2). // Goal: create the following state on these qubits: (|00⟩ + i*|01⟩ - |10⟩ - i*|11⟩) / 2. - operation AllBasisVectorsWithPhases_TwoQubits (qs : Qubit[]) : () - { - body - { - // The following lines enforce the constraints on the input that you are given. - // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. - AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); + operation AllBasisVectorsWithPhases_TwoQubits (qs : Qubit[]) : Unit { + // The following lines enforce the constraints on the input that you are given. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + AssertIntEqual(Length(qs), 2, "The array should have exactly 2 qubits."); - // Hint: Is this state separable? - // ... - } + // Hint: Is this state separable? + // ... } - + + // Task 6. Bell state // Input: two qubits in |00⟩ state (stored in an array of length 2). // Goal: create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits. - operation BellState (qs : Qubit[]) : () - { - body - { - // ... - } + operation BellState (qs : Qubit[]) : Unit { + // ... } - + + // Task 7. All Bell states // Inputs: // 1) two qubits in |00⟩ state (stored in an array of length 2) @@ -122,39 +105,30 @@ namespace Quantum.Kata.Superposition // 1: |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2: |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3: |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation AllBellStates (qs : Qubit[], index : Int) : () - { - body - { - // ... - } + operation AllBellStates (qs : Qubit[], index : Int) : Unit { + // ... } - + + // Task 8. Greenberger–Horne–Zeilinger state // Input: N qubits in |0...0⟩ state. // Goal: create a GHZ state (|0...0⟩ + |1...1⟩) / sqrt(2) on these qubits. - operation GHZ_State (qs : Qubit[]) : () - { - body - { - // Hint: N can be found as Length(qs). + operation GHZ_State (qs : Qubit[]) : Unit { + // Hint: N can be found as Length(qs). - // ... - } + // ... } - + + // Task 9. Superposition of all basis vectors // Input: N qubits in |0...0⟩ state. // Goal: create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ // (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ). - operation AllBasisVectorsSuperposition (qs : Qubit[]) : () - { - body - { - // ... - } + operation AllBasisVectorsSuperposition (qs : Qubit[]) : Unit { + // ... } - + + // Task 10. Superposition of |0...0⟩ and given bit string // Inputs: // 1) N qubits in |0...0⟩ state @@ -163,63 +137,51 @@ namespace Quantum.Kata.Superposition // Bit values false and true correspond to |0⟩ and |1⟩ states. // You are guaranteed that the qubit array and the bit string have the same length. // You are guaranteed that the first bit of the bit string is true. - // Example: for bit string = [true; false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2). - operation ZeroAndBitstringSuperposition (qs : Qubit[], bits : Bool[]) : () - { - body - { - // The following lines enforce the constraints on the input that you are given. - // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. - AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length"); - AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true"); + // Example: for bit string = [true, false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2). + operation ZeroAndBitstringSuperposition (qs : Qubit[], bits : Bool[]) : Unit { + // The following lines enforce the constraints on the input that you are given. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length"); + AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true"); - // ... - } + // ... } - + + // Task 11. Superposition of two bit strings // Inputs: // 1) N qubits in |0...0⟩ state // 2) two bit string represented as Bool[]s // Goal: create an equal superposition of two basis states given by the bit strings. // Bit values false and true correspond to |0⟩ and |1⟩ states. - // Example: for bit strings [false; true; false] and [false; false; true] + // Example: for bit strings [false, true, false] and [false, false, true] // the qubit state required is (|010⟩ + |001⟩) / sqrt(2). // You are guaranteed that the both bit strings have the same length as the qubit array, // and that the bit strings will differ in at least one bit. - operation TwoBitstringSuperposition (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : () - { - body - { - // ... - } + operation TwoBitstringSuperposition (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Unit { + // ... } - + + // Task 12**. W state on 2^k qubits // Input: N = 2^k qubits in |0...0⟩ state. // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. // Example: for N = 4, W state is (|1000⟩ + |0100⟩ + |0010⟩ + |0001⟩) / 2. - operation WState_PowerOfTwo (qs : Qubit[]) : () - { - body - { - // Hint: you can use Controlled functor to perform arbitrary controlled gates. + operation WState_PowerOfTwo (qs : Qubit[]) : Unit { + // Hint: you can use Controlled modifier to perform arbitrary controlled gates. // ... - } } - + + // Task 13**. W state on arbitrary number of qubits // Input: N qubits in |0...0⟩ state (N is not necessarily a power of 2). // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. // Example: for N = 3, W state is (|100⟩ + |010⟩ + |001⟩) / sqrt(3). - operation WState_Arbitrary (qs : Qubit[]) : () - { - body - { + operation WState_Arbitrary (qs : Qubit[]) : Unit { // ... - } } + } diff --git a/Superposition/Tests.qs b/Superposition/Tests.qs index b5743c36..ec823f1f 100644 --- a/Superposition/Tests.qs +++ b/Superposition/Tests.qs @@ -1,260 +1,213 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Superposition -{ +namespace Quantum.Kata.Superposition { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ - operation AssertEqualOnZeroState (N : Int, testImpl : (Qubit[] => ()), refImpl : (Qubit[] => () : Adjoint)) : () - { - body - { - using (qs = Qubit[N]) - { - // apply operation that needs to be tested - testImpl(qs); - - // apply adjoint reference operation and check that the result is |0⟩ - (Adjoint refImpl)(qs); - - // assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + operation AssertEqualOnZeroState (N : Int, testImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + using (qs = Qubit[N]) { + // apply operation that needs to be tested + testImpl(qs); + + // apply adjoint reference operation and check that the result is |0⟩ + Adjoint refImpl(qs); + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } - + + // ------------------------------------------------------ - operation T01_PlusState_Test () : () - { - body - { - AssertEqualOnZeroState(1, PlusState, PlusState_Reference); + operation T01_PlusState_Test () : Unit { + AssertEqualOnZeroState(1, PlusState, PlusState_Reference); + } + + + // ------------------------------------------------------ + operation T02_MinusState_Test () : Unit { + AssertEqualOnZeroState(1, MinusState, MinusState_Reference); + } + + + // ------------------------------------------------------ + operation T03_UnequalSuperposition_Test () : Unit { + // cross-test + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.0), ApplyToEachA(I, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(X, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(Y, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.25 * PI()), PlusState_Reference); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.75 * PI()), MinusState_Reference); + + for (i in 1 .. 36) { + let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + AssertEqualOnZeroState(1, UnequalSuperposition(_, alpha), UnequalSuperposition_Reference(_, alpha)); } } - + + // ------------------------------------------------------ - operation T02_MinusState_Test () : () - { - body - { - AssertEqualOnZeroState(1, MinusState, MinusState_Reference); + operation T04_AllBasisVectors_TwoQubits_Test () : Unit { + // We only check for 2 qubits. + AssertEqualOnZeroState(2, AllBasisVectors_TwoQubits, AllBasisVectors_TwoQubits_Reference); + } + + + // ------------------------------------------------------ + operation T05_AllBasisVectorsWithPhases_TwoQubits_Test () : Unit { + // We only check for 2 qubits. + AssertEqualOnZeroState(2, AllBasisVectorsWithPhases_TwoQubits, AllBasisVectorsWithPhases_TwoQubits_Reference); + } + + + // ------------------------------------------------------ + operation T06_BellState_Test () : Unit { + AssertEqualOnZeroState(2, BellState, BellState_Reference); + } + + + // ------------------------------------------------------ + operation T07_AllBellStates_Test () : Unit { + for (i in 0 .. 3) { + AssertEqualOnZeroState(2, AllBellStates(_, i), AllBellStates_Reference(_, i)); } } - + + // ------------------------------------------------------ - operation T03_UnequalSuperposition_Test () : () - { - body - { - // cross-test - AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.0), ApplyToEachA(I, _)); - AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(X, _)); - AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(Y, _)); - AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.25 * PI()), PlusState_Reference); - AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.75 * PI()), MinusState_Reference); - - for (i in 1..36) { - let alpha = 2.0 * PI() * ToDouble(i) / 36.0; - AssertEqualOnZeroState(1, UnequalSuperposition(_, alpha), UnequalSuperposition_Reference(_, alpha)); - } + operation T08_GHZ_State_Test () : Unit { + // for N = 1 it's just |+⟩ + AssertEqualOnZeroState(1, GHZ_State, PlusState_Reference); + + // for N = 2 it's Bell state + AssertEqualOnZeroState(2, GHZ_State, BellState_Reference); + + for (n in 3 .. 9) { + AssertEqualOnZeroState(n, GHZ_State, GHZ_State_Reference); } } - + + // ------------------------------------------------------ - operation T04_AllBasisVectors_TwoQubits_Test () : () - { - body - { - // We only check for 2 qubits. - AssertEqualOnZeroState(2, AllBasisVectors_TwoQubits, AllBasisVectors_TwoQubits_Reference); + operation T09_AllBasisVectorsSuperposition_Test () : Unit { + // for N = 1 it's just |+⟩ + AssertEqualOnZeroState(1, AllBasisVectorsSuperposition, PlusState_Reference); + + for (n in 2 .. 9) { + AssertEqualOnZeroState(n, AllBasisVectorsSuperposition, AllBasisVectorsSuperposition_Reference); } } - + + // ------------------------------------------------------ - operation T05_AllBasisVectorsWithPhases_TwoQubits_Test () : () - { - body - { - // We only check for 2 qubits. - AssertEqualOnZeroState(2, AllBasisVectorsWithPhases_TwoQubits, AllBasisVectorsWithPhases_TwoQubits_Reference); + operation T10_ZeroAndBitstringSuperposition_Test () : Unit { + // compare with results of previous operations + AssertEqualOnZeroState(1, ZeroAndBitstringSuperposition(_, [true]), PlusState_Reference); + AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, [true, true]), BellState_Reference); + AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, [true, true, true]), GHZ_State_Reference); + + if (true) { + let b = [true, false]; + AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, b), ZeroAndBitstringSuperposition_Reference(_, b)); + } + + if (true) { + let b = [true, false, true]; + AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, b), ZeroAndBitstringSuperposition_Reference(_, b)); + } + + if (true) { + let b = [true, false, true, true, false, false]; + AssertEqualOnZeroState(6, ZeroAndBitstringSuperposition(_, b), ZeroAndBitstringSuperposition_Reference(_, b)); } } - + + // ------------------------------------------------------ - operation T06_BellState_Test () : () - { - body - { - AssertEqualOnZeroState(2, BellState, BellState_Reference); + operation T11_TwoBitstringSuperposition_Test () : Unit { + // compare with results of previous operations + AssertEqualOnZeroState(1, TwoBitstringSuperposition(_, [true], [false]), PlusState_Reference); + AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, [false, false], [true, true]), BellState_Reference); + AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, [true, true, true], [false, false, false]), GHZ_State_Reference); + + // compare with reference implementation + // diff in first position + for (i in 1 .. 1) { + let b1 = [false, true]; + let b2 = [true, false]; + AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, b1, b2), TwoBitstringSuperposition_Reference(_, b1, b2)); + } + + for (i in 1 .. 1) { + let b1 = [true, true, false]; + let b2 = [false, true, true]; + AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, b1, b2), TwoBitstringSuperposition_Reference(_, b1, b2)); + } + + // diff in last position + for (i in 1 .. 1) { + let b1 = [false, true, true, false]; + let b2 = [false, true, true, true]; + AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), TwoBitstringSuperposition_Reference(_, b1, b2)); + } + + // diff in the middle + for (i in 1 .. 1) { + let b1 = [true, false, false, false]; + let b2 = [true, false, true, true]; + AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), TwoBitstringSuperposition_Reference(_, b1, b2)); } } - + + // ------------------------------------------------------ - operation T07_AllBellStates_Test () : () - { - body - { - for (i in 0..3) { - AssertEqualOnZeroState(2, AllBellStates(_, i), AllBellStates_Reference(_, i)); - } + operation T12_WState_PowerOfTwo_Test () : Unit { + // separate check for N = 1 (return must be |1⟩) + using (qs = Qubit[1]) { + WState_PowerOfTwo(qs); + Assert([PauliZ], qs, One, ""); + X(qs[0]); + } + + AssertEqualOnZeroState(2, WState_PowerOfTwo, TwoBitstringSuperposition_Reference(_, [false, true], [true, false])); + AssertEqualOnZeroState(4, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(8, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(16, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + } + + + // ------------------------------------------------------ + operation T13_WState_Arbitrary_Test () : Unit { + // separate check for N = 1 (return must be |1⟩) + using (qs = Qubit[1]) { + WState_Arbitrary_Reference(qs); + Assert([PauliZ], qs, One, ""); + X(qs[0]); + } + + // cross-tests + AssertEqualOnZeroState(2, WState_Arbitrary, TwoBitstringSuperposition_Reference(_, [false, true], [true, false])); + AssertEqualOnZeroState(2, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(4, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(8, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(16, WState_Arbitrary, WState_PowerOfTwo_Reference); + + for (i in 2 .. 16) { + AssertEqualOnZeroState(i, WState_Arbitrary, WState_Arbitrary_Reference); } } - - // ------------------------------------------------------ - operation T08_GHZ_State_Test () : () - { - body - { - // for N = 1 it's just |+⟩ - AssertEqualOnZeroState(1, GHZ_State, PlusState_Reference); - // for N = 2 it's Bell state - AssertEqualOnZeroState(2, GHZ_State, BellState_Reference); - for (n in 3..9) { - AssertEqualOnZeroState(n, GHZ_State, GHZ_State_Reference); - } - } - } - - // ------------------------------------------------------ - operation T09_AllBasisVectorsSuperposition_Test () : () - { - body - { - // for N = 1 it's just |+⟩ - AssertEqualOnZeroState(1, AllBasisVectorsSuperposition, PlusState_Reference); - - for (n in 2..9) { - AssertEqualOnZeroState(n, AllBasisVectorsSuperposition, AllBasisVectorsSuperposition_Reference); - } - } - } - - // ------------------------------------------------------ - operation T10_ZeroAndBitstringSuperposition_Test () : () - { - body - { - // compare with results of previous operations - AssertEqualOnZeroState(1, ZeroAndBitstringSuperposition(_, [true]), PlusState_Reference); - AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, [true; true]), BellState_Reference); - AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, [true; true; true]), GHZ_State_Reference); - - if (true) { - let b = [true; false]; - AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, b), - ZeroAndBitstringSuperposition_Reference(_, b)); - } - if (true) { - let b = [true; false; true]; - AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, b), - ZeroAndBitstringSuperposition_Reference(_, b)); - } - if (true) { - let b = [true; false; true; true; false; false]; - AssertEqualOnZeroState(6, ZeroAndBitstringSuperposition(_, b), - ZeroAndBitstringSuperposition_Reference(_, b)); - } - } - } - - // ------------------------------------------------------ - operation T11_TwoBitstringSuperposition_Test () : () - { - body - { - // compare with results of previous operations - AssertEqualOnZeroState(1, TwoBitstringSuperposition(_, [true], [false]), PlusState_Reference); - AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, [false; false], [true; true]), BellState_Reference); - AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, [true; true; true], [false; false; false]), GHZ_State_Reference); - - // compare with reference implementation - // diff in first position - for (i in 1..1) - { - let b1 = [false; true]; - let b2 = [true; false]; - AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, b1, b2), - TwoBitstringSuperposition_Reference(_, b1, b2)); - } - for (i in 1..1) - { - let b1 = [true; true; false]; - let b2 = [false; true; true]; - AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, b1, b2), - TwoBitstringSuperposition_Reference(_, b1, b2)); - } - // diff in last position - for (i in 1..1) - { - let b1 = [false; true; true; false]; - let b2 = [false; true; true; true]; - AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), - TwoBitstringSuperposition_Reference(_, b1, b2)); - } - // diff in the middle - for (i in 1..1) - { - let b1 = [true; false; false; false]; - let b2 = [true; false; true; true]; - AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), - TwoBitstringSuperposition_Reference(_, b1, b2)); - } - } - } - - // ------------------------------------------------------ - operation T12_WState_PowerOfTwo_Test () : () - { - body - { - // separate check for N = 1 (return must be |1⟩) - using (qs = Qubit[1]) { - WState_PowerOfTwo(qs); - Assert([PauliZ], qs, One, ""); - X(qs[0]); - } - - AssertEqualOnZeroState(2, WState_PowerOfTwo, TwoBitstringSuperposition_Reference(_, [false; true], [true; false])); - AssertEqualOnZeroState(4, WState_PowerOfTwo, WState_PowerOfTwo_Reference); - AssertEqualOnZeroState(8, WState_PowerOfTwo, WState_PowerOfTwo_Reference); - AssertEqualOnZeroState(16, WState_PowerOfTwo, WState_PowerOfTwo_Reference); - } - } - - // ------------------------------------------------------ - operation T13_WState_Arbitrary_Test () : () - { - body - { - // separate check for N = 1 (return must be |1⟩) - using (qs = Qubit[1]) { - WState_Arbitrary_Reference(qs); - Assert([PauliZ], qs, One, ""); - X(qs[0]); - } - - // cross-tests - AssertEqualOnZeroState(2, WState_Arbitrary, TwoBitstringSuperposition_Reference(_, [false; true], [true; false])); - - AssertEqualOnZeroState(2, WState_Arbitrary, WState_PowerOfTwo_Reference); - AssertEqualOnZeroState(4, WState_Arbitrary, WState_PowerOfTwo_Reference); - AssertEqualOnZeroState(8, WState_Arbitrary, WState_PowerOfTwo_Reference); - AssertEqualOnZeroState(16, WState_Arbitrary, WState_PowerOfTwo_Reference); - - for (i in 2..16) { - AssertEqualOnZeroState(i, WState_Arbitrary, WState_Arbitrary_Reference); - } - } - } -} \ No newline at end of file + +} diff --git a/Teleportation/ReferenceImplementation.qs b/Teleportation/ReferenceImplementation.qs index 83407972..d13c3c25 100644 --- a/Teleportation/ReferenceImplementation.qs +++ b/Teleportation/ReferenceImplementation.qs @@ -1,160 +1,165 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. +// This file contains reference solutions to all tasks. // The tasks themselves can be found in Tasks.qs file. // but feel free to look up the solution if you get stuck. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Teleportation -{ +namespace Quantum.Kata.Teleportation { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; + ////////////////////////////////////////////////////////////////// // Part I. Standard teleportation ////////////////////////////////////////////////////////////////// - + // Task 1.1. Entangled pair - operation Entangle_Reference (qAlice : Qubit, qBob : Qubit) : () - { - body { + operation Entangle_Reference (qAlice : Qubit, qBob : Qubit) : Unit { + + body (...) { H(qAlice); CNOT(qAlice, qBob); } - adjoint auto; + + adjoint invert; } - + + // Task 1.2. Send the message (Alice's task) - operation SendMessage_Reference (qAlice : Qubit, qMessage : Qubit) : (Bool, Bool) - { - body { - CNOT(qMessage, qAlice); - H(qMessage); - return (M(qMessage) == One, M(qAlice) == One); - } + operation SendMessage_Reference (qAlice : Qubit, qMessage : Qubit) : (Bool, Bool) { + CNOT(qMessage, qAlice); + H(qMessage); + return (M(qMessage) == One, M(qAlice) == One); } - + + // Task 1.3. Reconstruct the message (Bob's task) - operation ReconstructMessage_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - if (b1) { Z(qBob); } - if (b2) { X(qBob); } + operation ReconstructMessage_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + if (b1) { + Z(qBob); } - } - - // Task 1.4. Standard teleportation protocol - operation StandardTeleport_Reference (qAlice : Qubit, qBob : Qubit, qMessage : Qubit):() - { - body{ - Entangle_Reference(qAlice, qBob); - let classicalBits = SendMessage_Reference(qAlice, qMessage); - - // Alice sends the classical bits to Bob. - // Bob uses these bits to transform his part of the entangled pair into |ψ⟩. - ReconstructMessage_Reference(qBob, classicalBits); - } - } - - // Task 1.5. Prepare the message specified and send it (Alice's task) - operation PrepareAndSendMessage_Reference (qAlice : Qubit, basis : Pauli, state : Bool) : (Bool, Bool) - { - body { - mutable classicalBits = (false, false); - using (qs = Qubit[1]) - { - if (state) { X(qs[0]); } - PrepareQubit(basis, qs[0]); - set classicalBits = SendMessage_Reference(qAlice, qs[0]); - Reset(qs[0]); - } - return classicalBits; + if (b2) { + X(qBob); } } - // Task 1.6. Reconstruct the message and measure it (Bob's task) - operation ReconstructAndMeasureMessage_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool), basis : Pauli) : (Bool) - { - body { - ReconstructMessage_Reference(qBob, (b1, b2)); - return (Measure([basis], [qBob]) == One); - } + + // Task 1.4. Standard teleportation protocol + operation StandardTeleport_Reference (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : Unit { + Entangle_Reference(qAlice, qBob); + let classicalBits = SendMessage_Reference(qAlice, qMessage); + + // Alice sends the classical bits to Bob. + // Bob uses these bits to transform his part of the entangled pair into |ψ⟩. + ReconstructMessage_Reference(qBob, classicalBits); } - + + + // Task 1.5. Prepare the message specified and send it (Alice's task) + operation PrepareAndSendMessage_Reference (qAlice : Qubit, basis : Pauli, state : Bool) : (Bool, Bool) { + mutable classicalBits = (false, false); + using (qs = Qubit[1]) { + if (state) { + X(qs[0]); + } + + PrepareQubit(basis, qs[0]); + set classicalBits = SendMessage_Reference(qAlice, qs[0]); + Reset(qs[0]); + } + return classicalBits; + } + + + // Task 1.6. Reconstruct the message and measure it (Bob's task) + operation ReconstructAndMeasureMessage_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool), basis : Pauli) : Bool { + ReconstructMessage_Reference(qBob, (b1, b2)); + return Measure([basis], [qBob]) == One; + } + + ////////////////////////////////////////////////////////////////// // Part II. Teleportation using different entangled pair ////////////////////////////////////////////////////////////////// - + // Task 2.1. Reconstruct the message if the entangled qubits were in the state |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2). - operation ReconstructMessage_PhiMinus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // Bob can apply a Z gate to his qubit to convert the pair to |Φ⁺⟩ - // and use the standard teleportation reconstruction process. - if (!b1) { Z(qBob); } - if (b2) { X(qBob); } + operation ReconstructMessage_PhiMinus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // Bob can apply a Z gate to his qubit to convert the pair to |Φ⁺⟩ + // and use the standard teleportation reconstruction process. + if (not b1) { + Z(qBob); + } + + if (b2) { + X(qBob); } } - + + // Task 2.2. Reconstruct the message if the entangled qubits were in the state |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2). - operation ReconstructMessage_PsiPlus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // Bob can apply an X gate to his qubit to convert the pair to |Φ⁺⟩ - // and use the standard teleportation reconstruction process. - if (b1) { Z(qBob); } - if (!b2) { X(qBob); } + operation ReconstructMessage_PsiPlus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // Bob can apply an X gate to his qubit to convert the pair to |Φ⁺⟩ + // and use the standard teleportation reconstruction process. + if (b1) { + Z(qBob); + } + + if (not b2) { + X(qBob); } } - + + // Task 2.3. Reconstruct the message if the entangled qubits were in the state |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2). - operation ReconstructMessage_PsiMinus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // Bob can apply a Z gate and an X gate to his qubit to convert the pair to |Φ⁺⟩ - // and use the standard teleportation reconstruction process. - if (!b1) { Z(qBob); } - if (!b2) { X(qBob); } + operation ReconstructMessage_PsiMinus_Reference (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // Bob can apply a Z gate and an X gate to his qubit to convert the pair to |Φ⁺⟩ + // and use the standard teleportation reconstruction process. + if (not b1) { + Z(qBob); + } + + if (not b2) { + X(qBob); } } - + + ////////////////////////////////////////////////////////////////// // Part III. Principle of deferred measurement ////////////////////////////////////////////////////////////////// - + // Task 3.1. Measurement-free teleportation. - operation MeasurementFreeTeleport_Reference (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : () - { - body { - // The first part of the circuit is similar to Alice's part, but without measurements. - CNOT(qMessage, qAlice); - H(qMessage); - - // Classically controlled gates applied by Bob are replaced by controlled gates - (Controlled Z)([qMessage], qBob); - (Controlled X)([qAlice], qBob); - } + operation MeasurementFreeTeleport_Reference (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : Unit { + // The first part of the circuit is similar to Alice's part, but without measurements. + CNOT(qMessage, qAlice); + H(qMessage); + + // Classically controlled gates applied by Bob are replaced by controlled gates + Controlled Z([qMessage], qBob); + Controlled X([qAlice], qBob); } - + + ////////////////////////////////////////////////////////////////// // Part IV. Teleportation with three entangled qubits ////////////////////////////////////////////////////////////////// - + // Task 4.1. Entangled trio - operation EntangleThreeQubits_Reference (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : () - { - body - { + operation EntangleThreeQubits_Reference (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : Unit { + + body (...) { H(qAlice); H(qBob); X(qCharlie); CCNOT(qAlice, qBob, qCharlie); - + X(qAlice); X(qBob); @@ -163,16 +168,25 @@ namespace Quantum.Kata.Teleportation X(qAlice); X(qBob); } - adjoint auto; + + adjoint invert; } - + + // Task 4.2. Reconstruct the message (Charlie's task) - operation ReconstructMessageWhenThreeEntangledQubits_Reference (qCharlie : Qubit, (b1 : Bool, b2 : Bool), b3 : Bool) : () - { - body { - if (b1) { Z(qCharlie); } - if (b2) { Z(qCharlie); Y(qCharlie); } - if (b3) { X(qCharlie); } + operation ReconstructMessageWhenThreeEntangledQubits_Reference (qCharlie : Qubit, (b1 : Bool, b2 : Bool), b3 : Bool) : Unit { + if (b1) { + Z(qCharlie); + } + + if (b2) { + Z(qCharlie); + Y(qCharlie); + } + + if (b3) { + X(qCharlie); } } + } diff --git a/Teleportation/Tasks.qs b/Teleportation/Tasks.qs index a662d99a..6e4877c3 100644 --- a/Teleportation/Tasks.qs +++ b/Teleportation/Tasks.qs @@ -1,58 +1,57 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -namespace Quantum.Kata.Teleportation -{ +namespace Quantum.Kata.Teleportation { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Convert; open Microsoft.Quantum.Extensions.Math; - + + ////////////////////////////////////////////////////////////////// // Welcome! ////////////////////////////////////////////////////////////////// - - // "Teleportation" quantum kata is a series of exercises designed + + // "Teleportation" quantum kata is a series of exercises designed // to get you familiar with programming in Q#. // It covers the quantum teleportation protocol which allows you - // to communicate a quantum state using only classical communication + // to communicate a quantum state using only classical communication // and previously shared quantum entanglement. - // + // Each task is wrapped in one operation preceded by the description of the task. // Each task (except tasks in which you have to write a test) has a unit test associated with it, // which initially fails. Your goal is to fill in the blank (marked with // ... comment) // with some Q# code to make the failing test pass. - + ////////////////////////////////////////////////////////////////// // Part I. Standard Teleportation ////////////////////////////////////////////////////////////////// - + // We split the teleportation protocol into several steps, following the description at // https://docs.microsoft.com/en-us/quantum/quantum-techniques-6-puttingitalltogether : // * Preparation (creating the entangled pair of qubits that are sent to Alice and Bob). // * Sending the message (Alice's task): Entangling the message qubit with Alice's qubit // and extracting two classical bits to be sent to Bob. - // * Reconstructing the message (Bob's task): Using the two classical bits Bob received from Alice + // * Reconstructing the message (Bob's task): Using the two classical bits Bob received from Alice // to get Bob's qubit into the state in which the message qubit had been originally. // Finally, we compose these steps into the complete teleportation protocol. - + // Task 1.1. Entangled pair // Input: two qubits qAlice and qBob, each in |0⟩ state. // Goal: create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits. // // In the context of the quantum teleportation protocol, this is the preparation step: - // qubits qAlice and qBob will be sent to Alice and Bob, respectively. - operation Entangle (qAlice : Qubit, qBob : Qubit) : () - { - body { - // ... - } + // qubits qAlice and qBob will be sent to Alice and Bob, respectively. + operation Entangle (qAlice : Qubit, qBob : Qubit) : Unit { + // ... } - + + // Task 1.2. Send the message (Alice's task) // Entangle the message qubit with Alice's qubit // and extract two classical bits to be sent to Bob. - // Inputs: + // Inputs: // 1) Alice's part of the entangled pair of qubits qAlice. // 2) The message qubit qMessage. // Output: @@ -61,53 +60,47 @@ namespace Quantum.Kata.Teleportation // the second bit - the result of measurement of Alice's qubit. // Represent measurement result 'One' as 'True' and 'Zero' as 'False'. // The state of the qubits in the end of the operation doesn't matter. - operation SendMessage (qAlice : Qubit, qMessage : Qubit) : (Bool, Bool) - { - body { - // ... - return (false, false); - } + operation SendMessage (qAlice : Qubit, qMessage : Qubit) : (Bool, Bool) { + // ... + return (false, false); } - + + // Task 1.3. Reconstruct the message (Bob's task) // Transform Bob's qubit into the required state using the two classical bits // received from Alice. // Inputs: // 1) Bob's part of the entangled pair of qubits qBob. - // 2) The tuple of classical bits received from Alice, + // 2) The tuple of classical bits received from Alice, // in the format used in task 1.2. // Goal: transform Bob's qubit qBob into the state in which the message qubit had been originally. - operation ReconstructMessage (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // ... - } + operation ReconstructMessage (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // ... } - + + // Task 1.4. Standard teleportation protocol - // Put together the steps implemented in tasks 1.1 - 1.3 to implement + // Put together the steps implemented in tasks 1.1 - 1.3 to implement // the full teleportation protocol. - // Inputs: - // 1) The two qubits qAlice and qBob in |0⟩ state. + // Inputs: + // 1) The two qubits qAlice and qBob in |0⟩ state. // 2) The message qubit qMessage in the state |ψ⟩ to be teleported. // Goal: transform Bob's qubit qBob into the state |ψ⟩. // The state of the qubits qAlice and qMessage in the end of the operation doesn't matter. - operation StandardTeleport (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : () - { - body { - // ... - } + operation StandardTeleport (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : Unit { + // ... } - + + // Task 1.5. Prepare a state and send it as a message (Alice's task) - // Given a Pauli basis along with a state 'True' as 'One' or 'False' - // as 'Zero' prepare a message qubit, entangle it with Alice's qubit, + // Given a Pauli basis along with a state 'True' as 'One' or 'False' + // as 'Zero' prepare a message qubit, entangle it with Alice's qubit, // and extract two classical bits to be sent to Bob. - // Inputs: + // Inputs: // 1) Alice's part of the entangled pair of qubits qAlice. - // 2) A PauliX, PauliY, or PauliZ basis in which the message + // 2) A PauliX, PauliY, or PauliZ basis in which the message // qubit should be prepared - // 3) A Bool indicating the eigenstate in which the message + // 3) A Bool indicating the eigenstate in which the message // qubit should be prepared // Output: // Two classical bits Alice will send to Bob via classical channel as a tuple of Bool values. @@ -115,158 +108,141 @@ namespace Quantum.Kata.Teleportation // the second bit - the result of measurement of Alice's qubit. // Represent measurement result 'One' as 'True' and 'Zero' as 'False'. // The state of the qubit qAlice in the end of the operation doesn't matter. - operation PrepareAndSendMessage (qAlice : Qubit, basis : Pauli, state : Bool) : (Bool, Bool) - { - body { - // ... - return (false, false); - } + operation PrepareAndSendMessage (qAlice : Qubit, basis : Pauli, state : Bool) : (Bool, Bool) { + // ... + return (false, false); } - + + // Task 1.6. Reconstruct and measure the message state (Bob's task) // Transform Bob's qubit into the required state using the two classical bits // received from Alice and measure it in the same basis in which she prepared the message. // Inputs: // 1) Bob's part of the entangled pair of qubits qBob. - // 2) The tuple of classical bits received from Alice, + // 2) The tuple of classical bits received from Alice, // in the format used in task 1.5. - // 3) The PauliX, PauliY, or PauliZ basis in which the + // 3) The PauliX, PauliY, or PauliZ basis in which the // message qubit was originally prepared // Output: - // A Bool indicating the eigenstate in which the message qubit was prepared, 'One' as + // A Bool indicating the eigenstate in which the message qubit was prepared, 'One' as // 'True' and 'Zero' as 'False'. - // Goal: transform Bob's qubit qBob into the state in which the message qubit was originally + // Goal: transform Bob's qubit qBob into the state in which the message qubit was originally // prepared, then measure it. The state of the qubit qBob in the end of the operation doesn't matter. - operation ReconstructAndMeasureMessage (qBob : Qubit, (b1 : Bool, b2 : Bool), basis : Pauli) : (Bool) - { - body { - // ... - return false; - } + operation ReconstructAndMeasureMessage (qBob : Qubit, (b1 : Bool, b2 : Bool), basis : Pauli) : Bool { + + // ... + return false; } - + + // Task 1.7. Testing standard quantum teleportation - // Goal: Test that the StandardTeleport operation from task 1.4 is able + // Goal: Test that the StandardTeleport operation from task 1.4 is able // to successfully teleport the states |0⟩ and |1⟩, as well as superpositions such as // (|0⟩ + |1⟩) / sqrt(2), // (|0⟩ - |1⟩) / sqrt(2), - // (|0⟩ + i|1⟩) / sqrt(2), and + // (|0⟩ + i|1⟩) / sqrt(2), and // (|0⟩ - i|1⟩) / sqrt(2) - operation StandardTeleport_Test () : () - { - body { - // Hint: You may find your answers for 1.5 and 1.6 useful + operation StandardTeleport_Test () : Unit { + // Hint: You may find your answers for 1.5 and 1.6 useful - // StandardTeleport_Test appears in the list of unit tests for the solution; run it to verify your code. + // StandardTeleport_Test appears in the list of unit tests for the solution; run it to verify your code. - // ... - } + // ... } - + + ////////////////////////////////////////////////////////////////// // Part II. Teleportation using different entangled pair ////////////////////////////////////////////////////////////////// - + // In this section we will take a look at the changes in the reconstruction process (Bob's task) // if the qubits shared between Alice and Bob are entangled in a different state. // Alice's part of the protocol remains the same in all tasks. - // As a reminder, the standard teleportation protocol requires shared qubits in state + // As a reminder, the standard teleportation protocol requires shared qubits in state // |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2). - - // In each task, the inputs are + + // In each task, the inputs are // 1) Bob's part of the entangled pair of qubits qBob. - // 2) the tuple of classical bits received from Alice, + // 2) the tuple of classical bits received from Alice, // in the format used in task 1.2. // The goal is to transform Bob's qubit qBob into the state in which the message qubit had been originally. - + // Task 2.1. Reconstruct the message if the entangled qubits were in the state |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2). - operation ReconstructMessage_PhiMinus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // ... - } + operation ReconstructMessage_PhiMinus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // ... } - + + // Task 2.2. Reconstruct the message if the entangled qubits were in the state |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2). - operation ReconstructMessage_PsiPlus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // ... - } + operation ReconstructMessage_PsiPlus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // ... } - + + // Task 2.3. Reconstruct the message if the entangled qubits were in the state |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2). - operation ReconstructMessage_PsiMinus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : () - { - body { - // ... - } + operation ReconstructMessage_PsiMinus (qBob : Qubit, (b1 : Bool, b2 : Bool)) : Unit { + // ... } - + + ////////////////////////////////////////////////////////////////// // Part III. Principle of deferred measurement ////////////////////////////////////////////////////////////////// - + // The principle of deferred measurement claims that measurements can be moved // from an intermediate stage of a quantum circuit to the end of the circuit. // If the measurement results are used to perform classically controlled operations, // they can be replaced by controlled quantum operations. - + // In this task we will apply this principle to the teleportation circuit. // Task 3.1. Measurement-free teleportation. - // Inputs: - // 1) The two qubits qAlice and qBob in |Φ⁺⟩ state. + // Inputs: + // 1) The two qubits qAlice and qBob in |Φ⁺⟩ state. // 2) The message qubit qMessage in the state |ψ⟩ to be teleported. // Goal: transform Bob's qubit qBob into the state |ψ⟩ using no measurements. // At the end of the operation qubits qAlice and qMessage should not be entangled with qBob. - operation MeasurementFreeTeleport (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : () - { - body { - // ... - } + operation MeasurementFreeTeleport (qAlice : Qubit, qBob : Qubit, qMessage : Qubit) : Unit { + // ... } - + + ////////////////////////////////////////////////////////////////// // Part IV. Teleportation with three entangled qubits ////////////////////////////////////////////////////////////////// - + // Quantum teleportation using entangled states other than Bell pairs is also feasible. // Here we look at just one of many possible schemes - in it a state is transferred from - // Alice to a third participant Charlie, but this may only be accomplished if Charlie + // Alice to a third participant Charlie, but this may only be accomplished if Charlie // has the trust of the second participant Bob. - + // Task 4.1*. Entangled trio // Input: three qubits qAlice, qBob, and qCharlie, each in |0⟩ state. // Goal: create an entangled state |Ψ³⟩ = (|000⟩ + |011⟩ + |101⟩ + |110⟩) / 2 on these qubits. // // In the context of the quantum teleportation protocol, this is the preparation step: - // qubits qAlice, qBob, and qCharlie will be sent to Alice, Bob, and Charlie respectively. - operation EntangleThreeQubits (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : () - { - body { - // ... - } + // qubits qAlice, qBob, and qCharlie will be sent to Alice, Bob, and Charlie respectively. + operation EntangleThreeQubits (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : Unit { + // ... } - + + // Task 4.2*. Reconstruct the message (Charlie's task) - // Alice has a message qubit in the state |ψ⟩ to be teleported, she has entangled it with + // Alice has a message qubit in the state |ψ⟩ to be teleported, she has entangled it with // her own qubit from |Ψ³⟩ in the same manner as task 1.2 and extracted two classical bits - // in order to send them to Charlie. Bob has also measured his own qubit from |Ψ³⟩ and sent + // in order to send them to Charlie. Bob has also measured his own qubit from |Ψ³⟩ and sent // Charlie the result. // // Transform Charlie's qubit into the required state using the two classical bits // received from Alice, and the one classical bit received from Bob. // Inputs: // 1) Charlie's part of the entangled trio of qubits qCharlie. - // 2) The tuple of classical bits received from Alice, + // 2) The tuple of classical bits received from Alice, // in the format used in task 1.2. // 3) A classical bit resulting from the measurement of Bob's qubit. // Goal: transform Charlie's qubit qCharlie into the state in which the message qubit had been originally. - operation ReconstructMessageWhenThreeEntangledQubits (qCharlie : Qubit, (b1 : Bool, b2 : Bool), b3 : Bool) : () - { - body { - // ... - } + operation ReconstructMessageWhenThreeEntangledQubits (qCharlie : Qubit, (b1 : Bool, b2 : Bool), b3 : Bool) : Unit { + // ... } + } diff --git a/Teleportation/Teleportation.csproj b/Teleportation/Teleportation.csproj index 49c68d3f..b69d0845 100644 --- a/Teleportation/Teleportation.csproj +++ b/Teleportation/Teleportation.csproj @@ -1,15 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1 x64 false Quantum.Kata.Teleportation - - - + + + diff --git a/Teleportation/Tests.qs b/Teleportation/Tests.qs index a8ccdb26..ec6d57c2 100644 --- a/Teleportation/Tests.qs +++ b/Teleportation/Tests.qs @@ -1,352 +1,290 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. ////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. +// This file contains testing harness for all tasks. // You should not modify anything in this file. // The tasks themselves can be found in Tasks.qs file. ////////////////////////////////////////////////////////////////////// -namespace Quantum.Kata.Teleportation -{ +namespace Quantum.Kata.Teleportation { + open Microsoft.Quantum.Primitive; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Testing; - + + // ------------------------------------------------------ - operation T11_Entangle_Test () : () - { - body - { - using (qs = Qubit[2]) - { - let q0 = qs[0]; - let q1 = qs[1]; - // Apply operation that needs to be tested - Entangle(q0, q1); - - // Apply adjoint reference operation and check that the result is |00⟩ - (Adjoint Entangle_Reference)(q0, q1); - - // Assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } + operation T11_Entangle_Test () : Unit { + using (qs = Qubit[2]) { + let q0 = qs[0]; + let q1 = qs[1]; + + // Apply operation that needs to be tested + Entangle(q0, q1); + + // Apply adjoint reference operation and check that the result is |00⟩ + Adjoint Entangle_Reference(q0, q1); + + // Assert that all qubits end up in |0⟩ state + AssertAllZero(qs); } } - + + // ------------------------------------------------------ // Helper which prepares proper Bell state on two qubits // 0 - |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) // 1 - |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2 - |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3 - |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation StatePrep_BellState (q1 : Qubit, q2 : Qubit, state : Int) : () { - body { - H(q1); - CNOT(q1, q2); - // now we have |00⟩ + |11⟩ - modify it based on state arg - if (state % 2 == 1) { - // negative phase - Z(q2); - } - if (state / 2 == 1) { - X(q2); - } + operation StatePrep_BellState (q1 : Qubit, q2 : Qubit, state : Int) : Unit { + H(q1); + CNOT(q1, q2); + + // now we have |00⟩ + |11⟩ - modify it based on state arg + if (state % 2 == 1) { + // negative phase + Z(q2); + } + + if (state / 2 == 1) { + X(q2); } } - + + // ------------------------------------------------------ // Helper operation that runs teleportation using two building blocks // specified as first two parameters. - operation ComposeTeleportation( - bellPrepOp : ((Qubit, Qubit) => ()), - getDescriptionOp : ((Qubit, Qubit) => (Bool, Bool)), - reconstructOp : ((Qubit, (Bool, Bool)) => ()), - qAlice : Qubit, - qBob : Qubit, - qMessage : Qubit - ) : () - { - body - { - bellPrepOp(qAlice, qBob); - let classicalBits = getDescriptionOp(qAlice, qMessage); - - // Alice sends the classical bits to Bob. - // Bob uses these bits to transform his part of the entangled pair into the message. - reconstructOp(qBob, classicalBits); - } + operation ComposeTeleportation ( + bellPrepOp : ((Qubit, Qubit) => Unit), + getDescriptionOp : ((Qubit, Qubit) => (Bool, Bool)), + reconstructOp : ((Qubit, (Bool, Bool)) => Unit), + qAlice : Qubit, + qBob : Qubit, + qMessage : Qubit) : Unit { + + bellPrepOp(qAlice, qBob); + let classicalBits = getDescriptionOp(qAlice, qMessage); + + // Alice sends the classical bits to Bob. + // Bob uses these bits to transform his part of the entangled pair into the message. + reconstructOp(qBob, classicalBits); } - + + // ------------------------------------------------------ - // Helper operation that runs a teleportation operation (specified by teleportOp). + // Helper operation that runs a teleportation operation (specified by teleportOp). // The state to teleport is set up using an operation (specified by setupPsiOp). // // Specifying the state to teleport through an operation allows to get the inverse // which makes testing easier. - operation TeleportTestHelper( - teleportOp : ((Qubit, Qubit, Qubit) => ()), - setupPsiOp : (Qubit => () : Adjoint) - ) : () - { - body{ - using(qs = Qubit[3]) - { - let qMessage = qs[0]; - let qAlice = qs[1]; - let qBob = qs[2]; - - setupPsiOp(qMessage); - - // This should modify qBob to be identical to the state - // of qMessage before the function call. - teleportOp(qAlice, qBob, qMessage); - - // Applying the inverse of the setup operation to qBob - // should make it Zero. - (Adjoint setupPsiOp)(qBob); - - AssertQubit(Zero, qBob); - - ResetAll(qs); - } + operation TeleportTestHelper ( + teleportOp : ((Qubit, Qubit, Qubit) => Unit), + setupPsiOp : (Qubit => Unit : Adjoint)) : Unit { + + using (qs = Qubit[3]) { + let qMessage = qs[0]; + let qAlice = qs[1]; + let qBob = qs[2]; + setupPsiOp(qMessage); + + // This should modify qBob to be identical to the state + // of qMessage before the function call. + teleportOp(qAlice, qBob, qMessage); + + // Applying the inverse of the setup operation to qBob + // should make it Zero. + Adjoint setupPsiOp(qBob); + AssertQubit(Zero, qBob); + ResetAll(qs); } } - + + // ------------------------------------------------------ // Run teleportation for a number of different states. // After each teleportation success is asserted. - // Also repeats for each state several times as + // Also repeats for each state several times as // code is expected to take different paths each time because // measurements done by Alice are not deterministic. - operation TeleportTestLoop(teleportOp : ((Qubit, Qubit, Qubit) => ())) : () - { - body - { - // Define setup operations for the message qubit - // on which to test teleportation: |0⟩, |1⟩, |0⟩ + |1⟩, unequal superposition. - let setupPsiOps = [I; X; H; Ry(42.0, _)]; - - // As part of teleportation Alice runs some measurements - // with nondeterministic outcome. - // Depending on the outcomes different paths are taken on Bob's side. - // We repeat each test run several times to ensure that all paths are checked. - let numRepetitions = 100; - for (i in 0..Length(setupPsiOps)-1) - { - for (j in 1..numRepetitions) - { - TeleportTestHelper(teleportOp, setupPsiOps[i]); - } + operation TeleportTestLoop (teleportOp : ((Qubit, Qubit, Qubit) => Unit)) : Unit { + // Define setup operations for the message qubit + // on which to test teleportation: |0⟩, |1⟩, |0⟩ + |1⟩, unequal superposition. + let setupPsiOps = [I, X, H, Ry(42.0, _)]; + + // As part of teleportation Alice runs some measurements + // with nondeterministic outcome. + // Depending on the outcomes different paths are taken on Bob's side. + // We repeat each test run several times to ensure that all paths are checked. + let numRepetitions = 100; + for (i in 0 .. Length(setupPsiOps) - 1) { + for (j in 1 .. numRepetitions) { + TeleportTestHelper(teleportOp, setupPsiOps[i]); } } } - - // Test the 'SendMessage' operation by using it as one part of full teleportation, + + + // Test the 'SendMessage' operation by using it as one part of full teleportation, // taking reference implementation for the other parts. - operation T12_SendMessage_Test() : () - { - body - { - let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 0), SendMessage, ReconstructMessage_Reference, _, _ , _); - TeleportTestLoop(teleport); - } + operation T12_SendMessage_Test () : Unit { + let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 0), SendMessage, ReconstructMessage_Reference, _, _, _); + TeleportTestLoop(teleport); } - + + // Test the 'ReconstructMessage' operation by using it as one part of full teleportation, // taking reference implementation for the other parts. - operation T13_ReconstructMessage_Test() : () - { - body - { - let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 0), SendMessage_Reference, ReconstructMessage, _, _ , _); - TeleportTestLoop(teleport); - } + operation T13_ReconstructMessage_Test () : Unit { + let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 0), SendMessage_Reference, ReconstructMessage, _, _, _); + TeleportTestLoop(teleport); } - + + // Test the full Teleport operation - operation T14_Teleport_Test() : () - { - body - { - TeleportTestLoop(StandardTeleport); - } + operation T14_Teleport_Test () : Unit { + TeleportTestLoop(StandardTeleport); } - + + // ------------------------------------------------------ // Runs teleportation for each state that is to be prepared and // sent by Alice. Success is asserted after each teleportation. - // Also repeats for each state several times; this is because + // Also repeats for each state several times; this is because // code is expected to take different paths each time since // measurements done by Alice are not deterministic. - operation TeleportPreparedStateTestLoop( - prepareAndSendMessageOp : ((Qubit, Pauli, Bool) => (Bool, Bool)), - reconstructAndMeasureMessageOp : ((Qubit, (Bool, Bool), Pauli) => (Bool)) - ) : () - { - body - { - let messages = [(PauliX, false); (PauliX, true); (PauliY, false); (PauliY, true); (PauliZ, false); (PauliZ, true)]; - let numRepetitions = 100; - - using(qs = Qubit[2]) - { - let qAlice = qs[0]; - let qBob = qs[1]; - - for (i in 0..Length(messages)-1) - { - for (j in 1..numRepetitions) - { - let (basis, sentState) = messages[i]; - StatePrep_BellState(qAlice, qBob, 0); - let classicalBits = prepareAndSendMessageOp(qAlice, basis, sentState); - let receivedState = reconstructAndMeasureMessageOp(qBob, classicalBits, basis); - AssertBoolEqual(receivedState, sentState, $"Sent and received states were not equal for {sentState} eigenstate in {basis} basis."); - ResetAll(qs); - } + operation TeleportPreparedStateTestLoop (prepareAndSendMessageOp : ((Qubit, Pauli, Bool) => (Bool, Bool)), reconstructAndMeasureMessageOp : ((Qubit, (Bool, Bool), Pauli) => Bool)) : Unit { + + let messages = [(PauliX, false), + (PauliX, true), + (PauliY, false), + (PauliY, true), + (PauliZ, false), + (PauliZ, true)]; + let numRepetitions = 100; + + using (qs = Qubit[2]) { + let qAlice = qs[0]; + let qBob = qs[1]; + + for (i in 0 .. Length(messages) - 1) { + for (j in 1 .. numRepetitions) { + let (basis, sentState) = messages[i]; + StatePrep_BellState(qAlice, qBob, 0); + let classicalBits = prepareAndSendMessageOp(qAlice, basis, sentState); + let receivedState = reconstructAndMeasureMessageOp(qBob, classicalBits, basis); + AssertBoolEqual(receivedState, sentState, $"Sent and received states were not equal for {sentState} eigenstate in {basis} basis."); + ResetAll(qs); } } } } - - // Test the 'PrepareAndSendMessage' operation by using it as one part of full teleportation, + + + // Test the 'PrepareAndSendMessage' operation by using it as one part of full teleportation, // taking reference implementation for the other parts. - operation T15_PrepareAndSendMessage_Test() : () - { - body - { - TeleportPreparedStateTestLoop(PrepareAndSendMessage, ReconstructAndMeasureMessage_Reference); - } + operation T15_PrepareAndSendMessage_Test () : Unit { + TeleportPreparedStateTestLoop(PrepareAndSendMessage, ReconstructAndMeasureMessage_Reference); } - + + // Test the 'ReconstructAndMeasureMessage' operation by using it as one part of full teleportation, // taking reference implementation for the other parts. - operation T16_ReconstructAndMeasureMessage_Test() : () - { - body - { - TeleportPreparedStateTestLoop(PrepareAndSendMessage_Reference, ReconstructAndMeasureMessage); - } + operation T16_ReconstructAndMeasureMessage_Test () : Unit { + TeleportPreparedStateTestLoop(PrepareAndSendMessage_Reference, ReconstructAndMeasureMessage); } - + + // ------------------------------------------------------ // Test variations of the teleport protocol using different state prep procedures - operation T21_ReconstructMessage_PhiMinus_Test() : () - { - body - { - let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 1), SendMessage_Reference, ReconstructMessage_PhiMinus, _, _ , _); - TeleportTestLoop(teleport); - } + operation T21_ReconstructMessage_PhiMinus_Test () : Unit { + let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 1), SendMessage_Reference, ReconstructMessage_PhiMinus, _, _, _); + TeleportTestLoop(teleport); } - - operation T22_ReconstructMessage_PsiPlus_Test() : () - { - body - { - let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 2), SendMessage_Reference, ReconstructMessage_PsiPlus, _, _ , _); - TeleportTestLoop(teleport); - } + + + operation T22_ReconstructMessage_PsiPlus_Test () : Unit { + let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 2), SendMessage_Reference, ReconstructMessage_PsiPlus, _, _, _); + TeleportTestLoop(teleport); } - - operation T23_ReconstructMessage_PsiMinus_Test() : () - { - body - { - let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 3), SendMessage_Reference, ReconstructMessage_PsiMinus, _, _ , _); - TeleportTestLoop(teleport); - } + + + operation T23_ReconstructMessage_PsiMinus_Test () : Unit { + let teleport = ComposeTeleportation(StatePrep_BellState(_, _, 3), SendMessage_Reference, ReconstructMessage_PsiMinus, _, _, _); + TeleportTestLoop(teleport); } - + + // ------------------------------------------------------ - operation T31_MeasurementFreeTeleport_Test() : () - { - body - { - let setupPsiOps = [I; X; H; Ry(42.0, _)]; - let numRepetitions = 100; - using(qs = Qubit[3]) - { - let qMessage = qs[0]; - let qAlice = qs[1]; - let qBob = qs[2]; - - for (i in 0..Length(setupPsiOps)-1) - { - for (j in 1..numRepetitions) - { - (setupPsiOps[i])(qMessage); - StatePrep_BellState(qAlice, qBob, 0); - - MeasurementFreeTeleport(qAlice, qBob, qMessage); - - (Adjoint setupPsiOps[i])(qBob); - - AssertQubit(Zero, qBob); - - ResetAll(qs); - } - } - } - } - } - - // ------------------------------------------------------ - operation T41_EntangleThreeQubits_Test() : () - { - body - { - using (qs = Qubit[3]) - { - let qAlice = qs[0]; - let qBob = qs[1]; - let qCharlie = qs[2]; - - // Apply operation that needs to be tested - EntangleThreeQubits(qAlice, qBob, qCharlie); - - // Apply adjoint reference operation and check that the result is |000⟩ - (Adjoint EntangleThreeQubits_Reference)(qAlice, qBob, qCharlie); - - // Assert that all qubits end up in |0⟩ state - AssertAllZero(qs); - } - } - } - - operation T42_ReconstructMessageWhenThreeEntangledQubits_Test() : () - { - body - { - let setupPsiOps = [I; X; H; Ry(42.0, _)]; - let numRepetitions = 100; - using (qs = Qubit[4]) - { - let qMessage = qs[0]; - let qAlice = qs[1]; - let qBob = qs[2]; - let qCharlie = qs[3]; + operation T31_MeasurementFreeTeleport_Test () : Unit { + let setupPsiOps = [I, X, H, Ry(42.0, _)]; + let numRepetitions = 100; + + using (qs = Qubit[3]) { + let qMessage = qs[0]; + let qAlice = qs[1]; + let qBob = qs[2]; - for (i in 0..Length(setupPsiOps)-1) - { - for (j in 1..numRepetitions) - { - (setupPsiOps[i])(qMessage); - EntangleThreeQubits_Reference(qAlice, qBob, qCharlie); - - let (b1, b2) = SendMessage_Reference(qAlice, qMessage); - let b3 = BoolFromResult(M(qBob)); - ReconstructMessageWhenThreeEntangledQubits(qCharlie, (b1, b2), b3); - - (Adjoint setupPsiOps[i])(qCharlie); - - AssertQubit(Zero, qCharlie); - - ResetAll(qs); - } + for (i in 0 .. Length(setupPsiOps) - 1) { + for (j in 1 .. numRepetitions) { + setupPsiOps[i](qMessage); + StatePrep_BellState(qAlice, qBob, 0); + MeasurementFreeTeleport(qAlice, qBob, qMessage); + Adjoint setupPsiOps[i](qBob); + AssertQubit(Zero, qBob); + ResetAll(qs); } } } } + + + // ------------------------------------------------------ + operation T41_EntangleThreeQubits_Test () : Unit { + + using (qs = Qubit[3]) { + let qAlice = qs[0]; + let qBob = qs[1]; + let qCharlie = qs[2]; + + // Apply operation that needs to be tested + EntangleThreeQubits(qAlice, qBob, qCharlie); + + // Apply adjoint reference operation and check that the result is |000⟩ + Adjoint EntangleThreeQubits_Reference(qAlice, qBob, qCharlie); + + // Assert that all qubits end up in |0⟩ state + AssertAllZero(qs); + } + } + + + operation T42_ReconstructMessageWhenThreeEntangledQubits_Test () : Unit { + + let setupPsiOps = [I, X, H, Ry(42.0, _)]; + let numRepetitions = 100; + + using (qs = Qubit[4]) { + let qMessage = qs[0]; + let qAlice = qs[1]; + let qBob = qs[2]; + let qCharlie = qs[3]; + + for (i in 0 .. Length(setupPsiOps) - 1) { + for (j in 1 .. numRepetitions) { + setupPsiOps[i](qMessage); + EntangleThreeQubits_Reference(qAlice, qBob, qCharlie); + let (b1, b2) = SendMessage_Reference(qAlice, qMessage); + let b3 = BoolFromResult(M(qBob)); + ReconstructMessageWhenThreeEntangledQubits(qCharlie, (b1, b2), b3); + Adjoint setupPsiOps[i](qCharlie); + AssertQubit(Zero, qCharlie); + ResetAll(qs); + } + } + } + } + }