Migrate the katas to QDK 0.3 and .NET Core 2.1 (#43)
This commit is contained in:
Родитель
5486ea21ba
Коммит
63e4e52a48
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.BasicGates</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.DeutschJozsaAlgorithm</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.GroversAlgorithm</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.JointMeasurements</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.Measurements</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
operation T202_IsQubitZeroOrPlusSimpleUSD_Test () : Unit {
|
||||
USD_DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, 0.1, StatePrep_IsQubitZeroOrPlus, IsQubitPlusZeroOrInconclusiveSimpleUSD);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.QEC_BitFlipCode</RootNamespace>
|
||||
|
@ -10,9 +10,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Qubit[]>, CorrectErrorOnLeftQubit);
|
||||
let errors = PauliErrors();
|
||||
|
||||
for (idxError in 0 .. 1) {
|
||||
AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp<Qubit[]>, 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<Qubit[]>, CorrectErrorOnAnyQubit);
|
||||
let errors = PauliErrors();
|
||||
|
||||
for (idxError in 0 .. Length(errors) - 1) {
|
||||
Message($"Task 06: Testing on {errors[idxError]}...");
|
||||
AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp<Qubit[]>, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.SimonsAlgorithm</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.SuperdenseCoding</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.Superposition</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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 {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Quantum.Kata.Teleportation</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.2.1809.701-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.3.1810.2508-preview" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче