From 7519eb3f74a82066e4fc1cecc81531cd3bd4d476 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Fri, 3 May 2019 16:46:04 -0700 Subject: [PATCH] Updating Katas to QDK 0.6 (#111) * Update the katas to use QDK 0.6.1905.301. * Add mechanism to verify that the reference solutions actually solve all tasks of a Notebook correctly. Co-Authored-By: Bettina Heim <34236215+bettinaheim@users.noreply.github.com> Co-Authored-By: Andres Paz --- .gitignore | 1 + BasicGates/BasicGates.csproj | 6 +- BasicGates/BasicGates.ipynb | 94 ++--- BasicGates/ReferenceImplementation.qs | 68 +-- BasicGates/Tasks.qs | 62 +-- BasicGates/Tests.qs | 71 ++-- CHSHGame/CHSHGame.csproj | 8 +- CHSHGame/ReferenceImplementation.qs | 37 +- CHSHGame/Tasks.qs | 3 +- CHSHGame/Tests.qs | 50 ++- .../DeutschJozsaAlgorithm.csproj | 6 +- .../DeutschJozsaAlgorithm.ipynb | 42 +- .../ReferenceImplementation.qs | 217 ++++------ DeutschJozsaAlgorithm/Tasks.qs | 29 +- DeutschJozsaAlgorithm/Tests.qs | 48 +-- Dockerfile | 2 +- GHZGame/GHZGame.csproj | 8 +- GHZGame/ReferenceImplementation.qs | 41 +- GHZGame/Tests.qs | 29 +- GroversAlgorithm/GroversAlgorithm.csproj | 8 +- GroversAlgorithm/ReferenceImplementation.qs | 139 +++---- GroversAlgorithm/Tasks.qs | 80 ++-- GroversAlgorithm/Tests.qs | 31 +- JointMeasurements/JointMeasurements.csproj | 8 +- JointMeasurements/ReferenceImplementation.qs | 7 +- JointMeasurements/Tasks.qs | 6 +- JointMeasurements/Tests.qs | 132 +++--- Measurements/Measurements.csproj | 8 +- Measurements/Measurements.ipynb | 6 +- Measurements/ReferenceImplementation.qs | 46 +-- Measurements/Tasks.qs | 8 +- Measurements/Tests.qs | 129 +++--- PhaseEstimation/CounterSimulator.cs | 4 +- PhaseEstimation/PhaseEstimation.csproj | 8 +- PhaseEstimation/ReferenceImplementation.qs | 111 +++-- PhaseEstimation/Tasks.qs | 30 +- PhaseEstimation/Tests.qs | 52 ++- QEC_BitFlipCode/QEC_BitFlipCode.csproj | 6 +- QEC_BitFlipCode/ReferenceImplementation.qs | 13 +- QEC_BitFlipCode/Tasks.qs | 2 +- QEC_BitFlipCode/Tests.qs | 118 +++--- SimonsAlgorithm/ReferenceImplementation.qs | 82 ++-- SimonsAlgorithm/SimonsAlgorithm.csproj | 8 +- SimonsAlgorithm/Tasks.qs | 67 ++- SimonsAlgorithm/Tests.cs | 16 +- SimonsAlgorithm/Tests.qs | 52 +-- SolveSATWithGrover/CounterSimulator.cs | 4 +- SolveSATWithGrover/ReferenceImplementation.qs | 257 +++++------- SolveSATWithGrover/SolveSATWithGrover.csproj | 8 +- SolveSATWithGrover/Tasks.qs | 67 +-- SolveSATWithGrover/Tests.qs | 67 ++- SuperdenseCoding/ReferenceImplementation.qs | 49 +-- SuperdenseCoding/SuperdenseCoding.csproj | 8 +- SuperdenseCoding/Tasks.qs | 7 +- SuperdenseCoding/Tests.qs | 18 +- Superposition/ReferenceImplementation.qs | 386 ++++++++---------- Superposition/Superposition.csproj | 8 +- Superposition/Superposition.ipynb | 12 +- Superposition/Tasks.qs | 17 +- Superposition/Tests.qs | 16 +- Teleportation/ReferenceImplementation.qs | 49 +-- Teleportation/Tasks.qs | 6 +- Teleportation/Teleportation.csproj | 8 +- Teleportation/Tests.qs | 8 +- UnitaryPatterns/CounterSimulator.cs | 4 +- UnitaryPatterns/ReferenceImplementation.qs | 123 +++--- UnitaryPatterns/Tasks.qs | 9 +- UnitaryPatterns/Tests.qs | 27 +- UnitaryPatterns/UnitaryPatterns.csproj | 8 +- scripts/validate-notebooks.ps1 | 51 +++ utilities/Common/Common.csproj | 2 +- utilities/Common/CounterSimulator.cs | 4 +- utilities/DumpUnitary/DumpUnitary.csproj | 6 +- utilities/DumpUnitary/Operations.qs | 5 +- .../Microsoft.Quantum.Katas/CheckKataMagic.cs | 203 +++++++++ .../Microsoft.Quantum.Katas/KataMagic.cs | 38 +- .../Microsoft.Quantum.Katas.csproj | 2 +- 77 files changed, 1630 insertions(+), 1846 deletions(-) create mode 100644 scripts/validate-notebooks.ps1 create mode 100644 utilities/Microsoft.Quantum.Katas/CheckKataMagic.cs diff --git a/.gitignore b/.gitignore index fb07114feeb..57ff868dddf 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ Tests/build # test outputs TestResults/ *.qxe +Check.ipynb # Random VS files *.suo diff --git a/BasicGates/BasicGates.csproj b/BasicGates/BasicGates.csproj index 8ea7755b8af..f6694aa933d 100644 --- a/BasicGates/BasicGates.csproj +++ b/BasicGates/BasicGates.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/BasicGates/BasicGates.ipynb b/BasicGates/BasicGates.ipynb index 5b52077e7fc..b93c5944ba5 100644 --- a/BasicGates/BasicGates.ipynb +++ b/BasicGates/BasicGates.ipynb @@ -36,7 +36,7 @@ "metadata": {}, "outputs": [], "source": [ - "%package Microsoft.Quantum.Katas::0.5.1904.1302" + "%package Microsoft.Quantum.Katas::0.6.1905.301" ] }, { @@ -46,13 +46,13 @@ "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", ">
\n", "> How to install the right IQ# version\n", - "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.5.1904.1302, the installation steps are as follows:\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.6.1905.301, the installation steps are as follows:\n", ">\n", "> 1. Stop the kernel.\n", "> 2. Uninstall the existing version of IQ#:\n", "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", "> 3. Install the matching version:\n", - "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.5.1904.1302\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.6.1905.301\n", "> 4. Reinstall the kernel:\n", "> dotnet iqsharp install\n", "> 5. Restart the Notebook.\n", @@ -94,17 +94,12 @@ "source": [ "%kata T11_StateFlip_Test \n", "\n", - "operation StateFlip (q : Qubit) : Unit {\n", + "operation StateFlip (q : Qubit) : Unit is Adj {\n", + " // The Pauli X gate will change the |0⟩ state to the |1⟩ state and vice versa.\n", + " // Type X(q);\n", + " // Then run the cell using Ctrl/⌘+Enter.\n", "\n", - " body (...) {\n", - " // The Pauli X gate will change the |0⟩ state to the |1⟩ state and vice versa.\n", - " // Type X(q);\n", - " // Then run the cell using Ctrl/⌘+Enter.\n", - "\n", - " // ...\n", - " }\n", - "\n", - " adjoint self;\n", + " // ...\n", "}" ] }, @@ -134,13 +129,8 @@ "source": [ "%kata T12_BasisChange_Test \n", "\n", - "operation BasisChange (q : Qubit) : Unit {\n", - " \n", - " body (...) {\n", - " // ...\n", - " }\n", - "\n", - " adjoint self;\n", + "operation BasisChange (q : Qubit) : Unit is Adj {\n", + " // ...\n", "}" ] }, @@ -163,13 +153,8 @@ "source": [ "%kata T13_SignFlip_Test \n", "\n", - "operation SignFlip (q : Qubit) : Unit {\n", - "\n", - " body (...) {\n", - " // ...\n", - " }\n", - "\n", - " adjoint self;\n", + "operation SignFlip (q : Qubit) : Unit is Adj {\n", + " // ...\n", "}" ] }, @@ -190,7 +175,7 @@ "- If the qubit is in superposition, change its state according to the effect on basis vectors.\n", "\n", "> This is the first operation in this kata that is not self-adjoint, i.e., applying it for a second time\n", - "> does not return the qubit to the original state. `adjoint invert` means that Q# can compute the operation \n", + "> does not return the qubit to the original state. `is Adj` means that Q# can compute the operation \n", "> that returns the qubit to the original state automatically." ] }, @@ -202,13 +187,8 @@ "source": [ "%kata T14_AmplitudeChange_Test\n", "\n", - "operation AmplitudeChange (q : Qubit, α : Double) : Unit {\n", - "\n", - " body (...) {\n", - " // ...\n", - " }\n", - "\n", - " adjoint invert;\n", + "operation AmplitudeChange (q : Qubit, α : Double) : Unit is Adj {\n", + " // ...\n", "}\n" ] }, @@ -231,13 +211,8 @@ "source": [ "%kata T15_PhaseFlip_Test\n", "\n", - "operation PhaseFlip (q : Qubit) : Unit {\n", - " \n", - " body (...) {\n", - " // ...\n", - " }\n", - " \n", - " adjoint invert;\n", + "operation PhaseFlip (q : Qubit) : Unit is Adj { \n", + " // ...\n", "}\n", "\n" ] @@ -267,13 +242,8 @@ "source": [ "%kata T16_PhaseChange_Test\n", "\n", - "operation PhaseChange (q : Qubit, α : Double) : Unit {\n", - " \n", - " body (...) {\n", - " // ...\n", - " }\n", - " \n", - " adjoint invert;\n", + "operation PhaseChange (q : Qubit, α : Double) : Unit is Adj {\n", + " // ...\n", "}\n" ] }, @@ -296,13 +266,9 @@ "source": [ "%kata T17_BellStateChange1_Test\n", "\n", - "operation BellStateChange1 (qs : Qubit[]) : Unit {\n", - " \n", - " body (...) {\n", - " // ...\n", - " }\n", + "operation BellStateChange1 (qs : Qubit[]) : Unit is Adj {\n", " \n", - " adjoint invert;\n", + " // ...\n", "}\n" ] }, @@ -325,13 +291,9 @@ "source": [ "%kata T18_BellStateChange2_Test\n", "\n", - "operation BellStateChange2 (qs : Qubit[]) : Unit {\n", + "operation BellStateChange2 (qs : Qubit[]) : Unit is Adj {\n", " \n", - " body (...) {\n", - " // ...\n", - " }\n", - " \n", - " adjoint invert;\n", + " // ...\n", "}\n" ] }, @@ -354,13 +316,9 @@ "source": [ "%kata T19_BellStateChange3_Test\n", "\n", - "operation BellStateChange3 (qs : Qubit[]) : Unit {\n", - " \n", - " body (...) {\n", - " // ...\n", - " }\n", + "operation BellStateChange3 (qs : Qubit[]) : Unit is Adj {\n", " \n", - " adjoint invert;\n", + " // ...\n", "}\n" ] }, @@ -451,7 +409,7 @@ "\n", "**Goal:** Change the two-qubit state to $\\alpha |00\\rangle + \\color{red}\\gamma |01\\rangle + \\color{red}\\beta |10\\rangle + \\delta |11\\rangle$.\n", "\n", - "> This task can be solved using one primitive gate; as an exercise, try to express the solution using several (possibly controlled) Pauli gates." + "> This task can be solved using one intrinsic gate; as an exercise, try to express the solution using several (possibly controlled) Pauli gates." ] }, { diff --git a/BasicGates/ReferenceImplementation.qs b/BasicGates/ReferenceImplementation.qs index 5c9dbaee0b7..f42a68e7b21 100644 --- a/BasicGates/ReferenceImplementation.qs +++ b/BasicGates/ReferenceImplementation.qs @@ -10,7 +10,7 @@ namespace Quantum.Kata.BasicGates { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -72,27 +72,19 @@ namespace Quantum.Kata.BasicGates { // 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) : Unit { - - body (...) { - Ry(2.0 * alpha, q); - } - - adjoint invert; + operation AmplitudeChange_Reference (q : Qubit, alpha : Double) : Unit + is Adj { + Ry(2.0 * alpha, q); } // 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) : Unit { - - body (...) { - S(q); - // alternatively Rz(0.5 * PI(), q); - } - - adjoint invert; + operation PhaseFlip_Reference (q : Qubit) : Unit + is Adj { + S(q); + // alternatively Rz(0.5 * PI(), q); } @@ -104,54 +96,38 @@ namespace Quantum.Kata.BasicGates { // 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) : Unit { - - body (...) { - Rz(alpha, q); - } - - adjoint invert; + operation PhaseChange_Reference (q : Qubit, alpha : Double) : Unit + is Adj { + Rz(alpha, q); } // 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[]) : Unit { - - body (...) { - Z(qs[0]); - // alternatively Z(qs[1]); - } - - adjoint invert; + operation BellStateChange1_Reference (qs : Qubit[]) : Unit + is Adj { + Z(qs[0]); + // alternatively Z(qs[1]); } // 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[]) : Unit { - - body (...) { - X(qs[0]); - // alternatively X(qs[1]); - } - - adjoint invert; + operation BellStateChange2_Reference (qs : Qubit[]) : Unit + is Adj { + X(qs[0]); + // alternatively X(qs[1]); } // 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[]) : Unit { - - body (...) { - Y(qs[0]); - } - - adjoint invert; + operation BellStateChange3_Reference (qs : Qubit[]) : Unit + is Adj { + Y(qs[0]); } diff --git a/BasicGates/Tasks.qs b/BasicGates/Tasks.qs index 2704aa0acef..2d49e98ebb1 100644 --- a/BasicGates/Tasks.qs +++ b/BasicGates/Tasks.qs @@ -3,7 +3,7 @@ namespace Quantum.Kata.BasicGates { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -94,26 +94,18 @@ namespace Quantum.Kata.BasicGates { // 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) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation AmplitudeChange (q : Qubit, alpha : Double) : Unit + is Adj { + // ... } // 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) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation PhaseFlip (q : Qubit) : Unit + is Adj { + // ... } @@ -125,52 +117,36 @@ namespace Quantum.Kata.BasicGates { // 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) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation PhaseChange (q : Qubit, alpha : Double) : Unit + is Adj { + // ... } // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation BellStateChange1 (qs : Qubit[]) : Unit + is Adj { + // ... } // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation BellStateChange2 (qs : Qubit[]) : Unit + is Adj { + // ... } // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation BellStateChange3 (qs : Qubit[]) : Unit + is Adj { + // ... } diff --git a/BasicGates/Tests.qs b/BasicGates/Tests.qs index 1f9f8a8cbbe..42f1ce2670d 100644 --- a/BasicGates/Tests.qs +++ b/BasicGates/Tests.qs @@ -9,65 +9,59 @@ namespace Quantum.Kata.BasicGates { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Diagnostics; // ------------------------------------------------------ // helper wrapper to represent operation on one qubit as an operation on an array of qubits - operation ArrayWrapperOperation (op : (Qubit => Unit : Adjoint), qs : Qubit[]) : Unit { - - body (...) { - op(qs[0]); - } - - adjoint (...) { - Adjoint op(qs[0]); - } + operation ArrayWrapperOperation (op : (Qubit => Unit is Adj), qs : Qubit[]) : Unit + is Adj { + op(qs[0]); } // ------------------------------------------------------ operation T11_StateFlip_Test () : Unit { - AssertOperationsEqualReferenced(ArrayWrapperOperation(StateFlip, _), ArrayWrapperOperation(StateFlip_Reference, _), 1); + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(StateFlip, _), ArrayWrapperOperation(StateFlip_Reference, _)); } // ------------------------------------------------------ operation T12_BasisChange_Test () : Unit { - AssertOperationsEqualReferenced(ArrayWrapperOperation(BasisChange, _), ArrayWrapperOperation(BasisChange_Reference, _), 1); + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(BasisChange, _), ArrayWrapperOperation(BasisChange_Reference, _)); } // ------------------------------------------------------ operation T13_SignFlip_Test () : Unit { - AssertOperationsEqualReferenced(ArrayWrapperOperation(SignFlip, _), ArrayWrapperOperation(SignFlip_Reference, _), 1); + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(SignFlip, _), ArrayWrapperOperation(SignFlip_Reference, _)); } // ------------------------------------------------------ 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); + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(AmplitudeChange(_, alpha), _), ArrayWrapperOperation(AmplitudeChange_Reference(_, alpha), _)); } } // ------------------------------------------------------ operation T15_PhaseFlip_Test () : Unit { - AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseFlip, _), ArrayWrapperOperation(PhaseFlip_Reference, _), 1); + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(PhaseFlip, _), ArrayWrapperOperation(PhaseFlip_Reference, _)); } // ------------------------------------------------------ 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); + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(PhaseChange(_, alpha), _), ArrayWrapperOperation(PhaseChange_Reference(_, alpha), _)); } } @@ -77,24 +71,21 @@ namespace Quantum.Kata.BasicGates { // 1 - |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2) // 2 - |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2) // 3 - |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2) - operation StatePrep_BellState (qs : Qubit[], state : Int) : Unit { + operation StatePrep_BellState (qs : Qubit[], state : Int) : Unit + is Adj { - body (...) { - H(qs[0]); - CNOT(qs[0], qs[1]); + 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]); - } + // 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]); - } + if (state / 2 == 1) { + X(qs[1]); } - - adjoint invert; } @@ -153,7 +144,7 @@ namespace Quantum.Kata.BasicGates { // 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; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; // prepare A state StatePrep_A(alpha, qs[0]); @@ -215,20 +206,20 @@ namespace Quantum.Kata.BasicGates { operation T23_TwoQubitGate3_Test () : Unit { - AssertOperationsEqualReferenced(SwapWrapper, TwoQubitGate3_Reference, 2); - AssertOperationsEqualReferenced(TwoQubitGate3, TwoQubitGate3_Reference, 2); + AssertOperationsEqualReferenced(2, SwapWrapper, TwoQubitGate3_Reference); + AssertOperationsEqualReferenced(2, TwoQubitGate3, TwoQubitGate3_Reference); } // ------------------------------------------------------ operation T24_ToffoliGate_Test () : Unit { - AssertOperationsEqualReferenced(ToffoliGate, ToffoliGate_Reference, 3); + AssertOperationsEqualReferenced(3, ToffoliGate, ToffoliGate_Reference); } // ------------------------------------------------------ operation T25_FredkinGate_Test () : Unit { - AssertOperationsEqualReferenced(FredkinGate, FredkinGate_Reference, 3); + AssertOperationsEqualReferenced(3, FredkinGate, FredkinGate_Reference); } } diff --git a/CHSHGame/CHSHGame.csproj b/CHSHGame/CHSHGame.csproj index 21dee52e802..44dc7ea0cf3 100644 --- a/CHSHGame/CHSHGame.csproj +++ b/CHSHGame/CHSHGame.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/CHSHGame/ReferenceImplementation.qs b/CHSHGame/ReferenceImplementation.qs index b516e8043df..876553b1309 100644 --- a/CHSHGame/ReferenceImplementation.qs +++ b/CHSHGame/ReferenceImplementation.qs @@ -43,35 +43,26 @@ namespace Quantum.Kata.CHSHGame { ////////////////////////////////////////////////////////////////// // Task 2.1. Entangled pair - operation CreateEntangledPair_Reference (qs : Qubit[]) : Unit { - body (...) { - H(qs[0]); - CNOT(qs[0], qs[1]); - } - - adjoint invert; + operation CreateEntangledPair_Reference (qs : Qubit[]) : Unit + is Adj { + H(qs[0]); + CNOT(qs[0], qs[1]); } // Task 2.2. Alice's quantum strategy operation AliceQuantum_Reference (bit : Bool, qubit : Qubit) : Bool { - if (bit) { - // Measure in sign basis if bit is 1 - return BoolFromResult(Measure([PauliX], [qubit])); - } else { - // Measure in computational basis if bit is 0 - return BoolFromResult(Measure([PauliZ], [qubit])); - } + // Measure in sign basis if bit is 1, and + // measure in computational basis if bit is 0 + let basis = bit ? PauliX | PauliZ; + return BoolFromResult(Measure([basis], [qubit])); } // Task 2.3. Rotate Bob's qubit operation RotateBobQubit_Reference (clockwise : Bool, qubit : Qubit) : Unit { - if (clockwise) { - Ry(-2.0 * PI() / 8.0, qubit); - } else { - Ry(2.0 * PI() / 8.0, qubit); - } + let angle = 2.0 * PI() / 8.0; + Ry(clockwise ? -angle | angle, qubit); } @@ -85,20 +76,18 @@ namespace Quantum.Kata.CHSHGame { // Task 2.5. Play the CHSH game operation PlayQuantumCHSH_Reference (askAlice : (Qubit => Bool), askBob : (Qubit => Bool)) : (Bool, Bool) { - mutable aliceResult = false; - mutable bobResult = false; using ((aliceQubit, bobQubit) = (Qubit(), Qubit())) { CreateEntangledPair_Reference([aliceQubit, bobQubit]); - set aliceResult = askAlice(aliceQubit); - set bobResult = askBob(bobQubit); + let aliceResult = askAlice(aliceQubit); + let bobResult = askBob(bobQubit); Reset(aliceQubit); Reset(bobQubit); + return (aliceResult, bobResult); } - return (aliceResult, bobResult); } } diff --git a/CHSHGame/Tasks.qs b/CHSHGame/Tasks.qs index 5213c79a41e..348ce0ebf16 100644 --- a/CHSHGame/Tasks.qs +++ b/CHSHGame/Tasks.qs @@ -3,6 +3,7 @@ namespace Quantum.Kata.CHSHGame { + open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Extensions.Math; open Microsoft.Quantum.Primitive; @@ -75,7 +76,7 @@ namespace Quantum.Kata.CHSHGame { 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."); + EqualityFactI(Length(qs), 2, "The array should have exactly 2 qubits."); // ... fail "Task 2.1 not implemented yet"; diff --git a/CHSHGame/Tests.qs b/CHSHGame/Tests.qs index 5e90614debe..12cabd866a2 100644 --- a/CHSHGame/Tests.qs +++ b/CHSHGame/Tests.qs @@ -9,16 +9,16 @@ namespace Quantum.Kata.CHSHGame { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; operation T11_WinCondition_Test () : Unit { for (i in 0..1 <<< 4 - 1) { let bits = BoolArrFromPositiveInt(i, 4); - AssertBoolEqual( + EqualityFactB( WinCondition(bits[0], bits[1], bits[2], bits[3]), (bits[0] and bits[1]) == (bits[2] != bits[3]), $"Win condition is wrong for X = {bits[0]}, Y = {bits[1]}, A = {bits[2]}, " + @@ -38,16 +38,16 @@ namespace Quantum.Kata.CHSHGame { set wins = wins + 1; } } - Message($"Win rate {ToDouble(wins) / 1000.}"); + Message($"Win rate {IntAsDouble(wins) / 1000.}"); - AssertBoolEqual(wins >= 700, true, + EqualityFactB(wins >= 700, true, "Alice and Bob's classical strategy is not optimal"); } // ------------------------------------------------------ operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), - refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + refImpl : (Qubit[] => Unit is Adj)) : Unit { using (qs = Qubit[N]) { // apply operation that needs to be tested taskImpl(qs); @@ -70,20 +70,20 @@ namespace Quantum.Kata.CHSHGame { // ------------------------------------------------------ operation T22_AliceQuantum_Test () : Unit { using (q = Qubit()) { - AssertBoolEqual(AliceQuantum(false, q), false, "|0⟩ not measured as false"); + EqualityFactB(AliceQuantum(false, q), false, "|0⟩ not measured as false"); Reset(q); X(q); - AssertBoolEqual(AliceQuantum(false, q), true, "|1⟩ not measured as true"); + EqualityFactB(AliceQuantum(false, q), true, "|1⟩ not measured as true"); Reset(q); H(q); - AssertBoolEqual(AliceQuantum(true, q), false, "|+⟩ is not measured as false"); + EqualityFactB(AliceQuantum(true, q), false, "|+⟩ is not measured as false"); Reset(q); X(q); H(q); - AssertBoolEqual(AliceQuantum(true, q), true, "|-⟩ is not measured as true"); + EqualityFactB(AliceQuantum(true, q), true, "|-⟩ is not measured as true"); Reset(q); } } @@ -94,18 +94,16 @@ namespace Quantum.Kata.CHSHGame { op(qs[0]); } - operation QubitToRegisterOperationA (op : (Qubit => Unit : Adjoint), qs : Qubit[]) : Unit { - body (...) { - op(qs[0]); - } - adjoint auto; + operation QubitToRegisterOperationA (op : (Qubit => Unit is Adj), qs : Qubit[]) : Unit + is Adj { + op(qs[0]); } operation T23_RotateBobQubit_Test () : Unit { - AssertOperationsEqualReferenced(QubitToRegisterOperation(RotateBobQubit(true, _), _), - QubitToRegisterOperationA(Ry(-2.0 * PI() / 8.0, _), _), 1); - AssertOperationsEqualReferenced(QubitToRegisterOperation(RotateBobQubit(false, _), _), - QubitToRegisterOperationA(Ry(2.0 * PI() / 8.0, _), _), 1); + AssertOperationsEqualReferenced(1, QubitToRegisterOperation(RotateBobQubit(true, _), _), + QubitToRegisterOperationA(Ry(-2.0 * PI() / 8.0, _), _)); + AssertOperationsEqualReferenced(1, QubitToRegisterOperation(RotateBobQubit(false, _), _), + QubitToRegisterOperationA(Ry(2.0 * PI() / 8.0, _), _)); } @@ -113,21 +111,21 @@ namespace Quantum.Kata.CHSHGame { operation T24_BobQuantum_Test () : Unit { using (q = Qubit()) { RotateBobQubit_Reference(false, q); - AssertBoolEqual(BobQuantum(false, q), false, "π/8 from |0⟩ not measured as false"); + EqualityFactB(BobQuantum(false, q), false, "π/8 from |0⟩ not measured as false"); Reset(q); X(q); RotateBobQubit_Reference(false, q); - AssertBoolEqual(BobQuantum(false, q), true, "π/8 from |1⟩ not measured as true"); + EqualityFactB(BobQuantum(false, q), true, "π/8 from |1⟩ not measured as true"); Reset(q); RotateBobQubit_Reference(true, q); - AssertBoolEqual(BobQuantum(true, q), false, "-π/8 from |0⟩ not measured as false"); + EqualityFactB(BobQuantum(true, q), false, "-π/8 from |0⟩ not measured as false"); Reset(q); X(q); RotateBobQubit_Reference(true, q); - AssertBoolEqual(BobQuantum(true, q), true, "-π/8 from |1⟩ not measured as true"); + EqualityFactB(BobQuantum(true, q), true, "-π/8 from |1⟩ not measured as true"); Reset(q); } } @@ -144,7 +142,7 @@ namespace Quantum.Kata.CHSHGame { set wins = wins + 1; } } - AssertAlmostEqualTol(ToDouble(wins) / 10000., 0.85, 0.01); + EqualityWithinToleranceFact(IntAsDouble(wins) / 10000., 0.85, 0.01); } } diff --git a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj index 4c9fe93d859..73ee0fddb4a 100644 --- a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj +++ b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.ipynb b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.ipynb index e4c1e91f882..6cca83b2589 100644 --- a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.ipynb +++ b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "%package Microsoft.Quantum.Katas::0.5.1904.1302" + "%package Microsoft.Quantum.Katas::0.6.1905.301" ] }, { @@ -44,13 +44,13 @@ "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", ">
\n", "> How to install the right IQ# version\n", - "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.5.1904.1302, the installation steps are as follows:\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.6.1905.301, the installation steps are as follows:\n", ">\n", "> 1. Stop the kernel.\n", "> 2. Uninstall the existing version of IQ#:\n", "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", "> 3. Install the matching version:\n", - "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.5.1904.1302\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.6.1905.301\n", "> 4. Reinstall the kernel:\n", "> dotnet iqsharp install\n", "> 5. Restart the Notebook.\n", @@ -169,10 +169,12 @@ "source": [ "%kata T13_Oracle_Kth_Qubit_Test \n", "\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Oracle_Kth_Qubit (x : Qubit[], y : Qubit, k : Int) : Unit {\n", " // The following line enforces the constraints on the value of k that you are given.\n", " // You don't need to modify it. Feel free to remove it, this won't cause your code to fail.\n", - " AssertBoolEqual(0 <= k and k < Length(x), true, \"k should be between 0 and N-1, inclusive\");\n", + " EqualityFactB(0 <= k and k < Length(x), true, \"k should be between 0 and N-1, inclusive\");\n", "\n", " // ...\n", "}" @@ -234,10 +236,12 @@ "source": [ "%kata T15_Oracle_ProductFunction_Test\n", "\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Oracle_ProductFunction (x : Qubit[], y : Qubit, r : Int[]) : Unit {\n", " // The following line enforces the constraint on the input arrays.\n", " // You don't need to modify it. Feel free to remove it, this won't cause your code to fail.\n", - " AssertIntEqual(Length(x), Length(r), \"Arrays should have the same length\");\n", + " EqualityFactI(Length(x), Length(r), \"Arrays should have the same length\");\n", "\n", " // ...\n", "}" @@ -272,10 +276,12 @@ "source": [ "%kata T16_Oracle_ProductWithNegationFunction_Test\n", "\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Oracle_ProductWithNegationFunction (x : Qubit[], y : Qubit, r : Int[]) : Unit {\n", " // The following line enforces the constraint on the input arrays.\n", " // You don't need to modify it. Feel free to remove it, this won't cause your code to fail.\n", - " AssertIntEqual(Length(x), Length(r), \"Arrays should have the same length\");\n", + " EqualityFactI(Length(x), Length(r), \"Arrays should have the same length\");\n", "\n", " // ...\n", "}" @@ -311,11 +317,13 @@ "source": [ "%kata T17_Oracle_HammingWithPrefix_Test\n", "\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Oracle_HammingWithPrefix (x : Qubit[], y : Qubit, prefix : Int[]) : Unit {\n", " // The following line enforces the constraint on the input arrays.\n", " // You don't need to modify it. Feel free to remove it, this won't cause your code to fail.\n", " let K = Length(prefix);\n", - " AssertBoolEqual(1 <= K and K <= Length(x), true, \"K should be between 1 and N, inclusive\");\n", + " EqualityFactB(1 <= K and K <= Length(x), true, \"K should be between 1 and N, inclusive\");\n", "\n", " // ...\n", "}" @@ -349,10 +357,12 @@ "source": [ "%kata T18_Oracle_MajorityFunction_Test\n", "\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Oracle_MajorityFunction (x : Qubit[], y : Qubit) : Unit {\n", " // The following line enforces the constraint on the input array.\n", " // You don't need to modify it. Feel free to remove it, this won't cause your code to fail.\n", - " AssertBoolEqual(3 == Length(x), true, \"x should have exactly 3 qubits\");\n", + " EqualityFactI(3, Length(x), \"x should have exactly 3 qubits\");\n", "\n", " // ...\n", "}" @@ -446,9 +456,11 @@ "metadata": {}, "outputs": [], "source": [ + "open Microsoft.Quantum.Diagnostics;\n", + "\n", "operation Run_DeutschJozsa_Algorithm () : String {\n", - " // You can use AssertBoolEqual function to assert that the return value of DJ_Algorithm operation matches the expected value\n", - " AssertBoolEqual(DJ_Algorithm(4, Oracle_Zero), true, \"f(x) = 0 not identified as constant\");\n", + " // You can use EqualityFactB function to represent the invariant that the return value of DJ_Algorithm operation matches the expected value\n", + " EqualityFactB(DJ_Algorithm(4, Oracle_Zero), true, \"f(x) = 0 not identified as constant\");\n", " \n", " // Run the algorithm for the rest of the oracles\n", " // ...\n", @@ -532,20 +544,20 @@ "metadata": {}, "outputs": [], "source": [ - "// Start by implementing a function AreIntArraysEqual \n", + "// Start by implementing a function AllEqualityFactI \n", "// to check the results of applying the algorithm to each oracle in a uniform manner.\n", - "function AreIntArraysEqual(actual : Int[], expected : Int[]) : Bool {\n", + "function AllEqualityFactI(actual : Int[], expected : Int[]) : Bool {\n", " // Check that array lengths are equal\n", " // ...\n", " \n", " // Check that the corresponding elements of the arrays are equal\n", " // ...\n", - " fail \"AreIntArraysEqual is not implemented\";\n", + " fail \"AllEqualityFactI is not implemented\";\n", "}\n", "\n", "operation Run_BernsteinVazirani_Algorithm () : String {\n", - " // Now use AreIntArraysEqual to verify the results of the algorithm\n", - " if (not AreIntArraysEqual(BV_Algorithm(3, Oracle_Zero), [0, 0, 0])) {\n", + " // Now use AllEqualityFactI to verify the results of the algorithm\n", + " if (not AllEqualityFactI(BV_Algorithm(3, Oracle_Zero), [0, 0, 0])) {\n", " return \"Incorrect result for f(x) = 0\";\n", " }\n", " \n", diff --git a/DeutschJozsaAlgorithm/ReferenceImplementation.qs b/DeutschJozsaAlgorithm/ReferenceImplementation.qs index 9c70c391696..b18a701448c 100644 --- a/DeutschJozsaAlgorithm/ReferenceImplementation.qs +++ b/DeutschJozsaAlgorithm/ReferenceImplementation.qs @@ -10,7 +10,9 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -23,15 +25,11 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) : 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 invert; + operation Oracle_Zero_Reference (x : Qubit[], y : Qubit) : Unit + is Adj { + // 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. } @@ -40,15 +38,11 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) : 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 invert; + operation Oracle_One_Reference (x : Qubit[], y : Qubit) : Unit + is Adj { + // 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); } @@ -58,14 +52,10 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) : Unit { - - body (...) { - AssertBoolEqual(0 <= k and k < Length(x), true, "k should be between 0 and N-1, inclusive"); - CNOT(x[k], y); - } - - adjoint invert; + operation Oracle_Kth_Qubit_Reference (x : Qubit[], y : Qubit, k : Int) : Unit + is Adj { + EqualityFactB(0 <= k and k < Length(x), true, "k should be between 0 and N-1, inclusive"); + CNOT(x[k], y); } @@ -74,17 +64,13 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) : Unit { - - body (...) { - // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) - for (i in 0 .. Length(x) - 1) { - CNOT(x[i], y); - } - // alternative solution: ApplyToEachA(CNOT(_, y), x); + operation Oracle_OddNumberOfOnes_Reference (x : Qubit[], y : Qubit) : Unit + is Adj { + // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) + for (q in x) { + CNOT(q, y); } - - adjoint invert; + // alternative solution: ApplyToEachA(CNOT(_, y), x); } @@ -97,21 +83,17 @@ 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_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"); + operation Oracle_ProductFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : Unit + is Adj { + // 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. + EqualityFactI(Length(x), Length(r), "Arrays should have the same length"); - for (i in 0 .. Length(x) - 1) { - if (r[i] == 1) { - CNOT(x[i], y); - } + for (i in IndexRange(x)) { + if (r[i] == 1) { + CNOT(x[i], y); } } - - adjoint invert; } @@ -122,26 +104,22 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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[]) : 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"); + operation Oracle_ProductWithNegationFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : Unit + is Adj { + // 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. + EqualityFactI(Length(x), Length(r), "Arrays should have the same length"); - for (i in 0 .. Length(x) - 1) { - if (r[i] == 1) { - CNOT(x[i], y); - } else { - // do a 0-controlled NOT - X(x[i]); - CNOT(x[i], y); - X(x[i]); - } + for (i in IndexRange(x)) { + if (r[i] == 1) { + CNOT(x[i], y); + } else { + // do a 0-controlled NOT + X(x[i]); + CNOT(x[i], y); + X(x[i]); } } - - adjoint invert; } @@ -154,40 +132,36 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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[]) : 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 and P <= Length(x), true, "P should be between 1 and N, inclusive"); + operation Oracle_HammingWithPrefix_Reference (x : Qubit[], y : Qubit, prefix : Int[]) : Unit + is Adj { + // 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); + EqualityFactB(1 <= P and 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) { - CNOT(x[i], y); - } + // Hint: the first part of the function is the same as in task 1.4 + for (q in x) { + CNOT(q, 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) { + // 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) { - if (prefix[i] == 0) { - X(x[i]); - } + 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) { + // uncompute changes done to input register + for (i in 0 .. P - 1) { - if (prefix[i] == 0) { - X(x[i]); - } + if (prefix[i] == 0) { + X(x[i]); } } - - adjoint invert; } @@ -196,20 +170,16 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) : 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"); + operation Oracle_MajorityFunction_Reference (x : Qubit[], y : Qubit) : Unit + is Adj { + // 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. + EqualityFactB(3 == Length(x), true, "x should have exactly 3 qubits"); - // Hint: represent f(x) in terms of AND and ⊕ operations - CCNOT(x[0], x[1], y); - CCNOT(x[0], x[2], y); - CCNOT(x[1], x[2], y); - } - - adjoint invert; + // Hint: represent f(x) in terms of AND and ⊕ operations + CCNOT(x[0], x[1], y); + CCNOT(x[0], x[2], y); + CCNOT(x[1], x[2], y); } @@ -225,15 +195,11 @@ 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) : Unit { - - body (...) { - ApplyToEachA(H, query); - X(answer); - H(answer); - } - - adjoint invert; + operation BV_StatePrep_Reference (query : Qubit[], answer : Qubit) : Unit + is Adj { + ApplyToEachA(H, query); + X(answer); + H(answer); } @@ -252,9 +218,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // |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) => Unit)) : Int[] { - - mutable r = new Int[N]; - + // allocate N qubits for input register and 1 qubit for output using ((x, y) = (Qubit[N], Qubit())) { @@ -269,18 +233,18 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // measure all qubits of the input register; // the result of each measurement is converted to an Int + mutable r = new Int[N]; for (i in 0 .. N - 1) { if (M(x[i]) != Zero) { - set r[i] = 1; + set r w/= i <- 1; } } // before releasing the qubits make sure they are all in |0⟩ state ResetAll(x); Reset(y); + return r; } - - return r; } @@ -339,11 +303,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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) => 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 ((x, y) = (Qubit[N], Qubit())) { // apply oracle to qubits in all 0 state Uf(x, y); @@ -356,19 +316,22 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // now y = Σᵢ 𝑟ᵢ + // Declare an Int array in which the result will be stored; + // the variable has to be mutable to allow updating it. + mutable r = new Int[N]; + // measure the output register let m = M(y); if (m == One) { // adjust parity of bit vector r - set r[0] = 1; + set r w/= 0 <- 1; } // before releasing the qubits make sure they are all in |0⟩ state ResetAll(x); Reset(y); + return r; } - - return r; } } diff --git a/DeutschJozsaAlgorithm/Tasks.qs b/DeutschJozsaAlgorithm/Tasks.qs index f413cfa9f5f..e1af693a7ea 100644 --- a/DeutschJozsaAlgorithm/Tasks.qs +++ b/DeutschJozsaAlgorithm/Tasks.qs @@ -3,7 +3,8 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -70,7 +71,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { 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 and k < Length(x), true, "k should be between 0 and N-1, inclusive"); + EqualityFactB(0 <= k and k < Length(x), true, "k should be between 0 and N-1, inclusive"); // ... } @@ -100,7 +101,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { 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"); + EqualityFactI(Length(x), Length(r), "Arrays should have the same length"); // ... } @@ -116,7 +117,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { 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"); + EqualityFactI(Length(x), Length(r), "Arrays should have the same length"); // ... } @@ -135,7 +136,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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 and P <= Length(x), true, "P should be between 1 and N, inclusive"); + EqualityFactB(1 <= P and 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 @@ -156,7 +157,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { 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"); + EqualityFactB(3 == Length(x), true, "x should have exactly 3 qubits"); // Hint: represent f(x) in terms of AND and ⊕ operations @@ -176,13 +177,9 @@ 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) : Unit { - - body (...) { + operation BV_StatePrep (query : Qubit[], answer : Qubit) : Unit + is Adj { // ... - } - - adjoint invert; } @@ -203,7 +200,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { operation BV_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. + // the variable has to be mutable to allow updating it. mutable r = new Int[N]; // ... @@ -226,7 +223,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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 + // Hint: use AllEqualityFactI 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. @@ -278,7 +275,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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 EqualityFactB 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. @@ -306,7 +303,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // it just needs to produce equivalent results. // Declare an Int array in which the result will be stored; - // the array has to be mutable to allow updating its elements. + // the variable has to be mutable to allow updating it. mutable r = new Int[N]; // ... diff --git a/DeutschJozsaAlgorithm/Tests.qs b/DeutschJozsaAlgorithm/Tests.qs index a5791ce499c..5933c60a952 100644 --- a/DeutschJozsaAlgorithm/Tests.qs +++ b/DeutschJozsaAlgorithm/Tests.qs @@ -9,9 +9,10 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; open Quantum.Kata.Utils; @@ -24,26 +25,22 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // ------------------------------------------------------ - operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { - - body (...) { - let N = Length(qs); - oracle(qs[0 .. N - 2], qs[N - 1]); - } - - adjoint invert; + operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj)) : Unit + is Adj { + let N = Length(qs); + oracle(qs[0 .. N - 2], qs[N - 1]); } // ------------------------------------------------------ operation AssertTwoOraclesAreEqual (nQubits : Range, oracle1 : ((Qubit[], Qubit) => Unit), - oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + oracle2 : ((Qubit[], Qubit) => Unit is Adj)) : Unit { let sol = ApplyOracle(_, oracle1); let refSol = ApplyOracleA(_, oracle2); for (i in nQubits) { - AssertOperationsEqualReferenced(sol, refSol, i + 1); + AssertOperationsEqualReferenced(i + 1, sol, refSol); } } @@ -85,7 +82,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // ------------------------------------------------------ operation AssertTwoOraclesWithIntAreEqual (r : Int[], oracle1 : ((Qubit[], Qubit, Int[]) => Unit), - oracle2 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint)) : Unit { + oracle2 : ((Qubit[], Qubit, Int[]) => Unit is Adj)) : Unit { AssertTwoOraclesAreEqual(Length(r) .. Length(r), oracle1(_, _, r), oracle2(_, _, r)); } @@ -102,7 +99,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // the mask with all 0's corresponds to Oracle_Zero for (i in 0 .. L - 1) { - set r[i] = 0; + set r w/= i <- 0; } for (i in 2 .. L) { @@ -111,9 +108,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // 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; + AssertTwoOraclesAreEqual(L .. L, Oracle_ProductFunction(_, _, r w/ i <- 1), Oracle_Kth_Qubit_Reference(_, _, i)); } set r = [1, 0, 1, 0, 1, 0]; @@ -184,7 +179,7 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { } // ------------------------------------------------------ - function AssertIntArrayEqual (actual : Int[], expected : Int[], message : String) : Unit { + function AllEqualityFactI (actual : Int[], expected : Int[], message : String) : Unit { let n = Length(actual); if (n != Length(expected)) { @@ -202,12 +197,12 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // ------------------------------------------------------ function IntArrFromPositiveInt (n : Int, bits : Int) : Int[] { - let rbool = BoolArrFromPositiveInt(n, bits); + let rbool = IntAsBoolArray(n, bits); mutable r = new Int[bits]; for (i in 0 .. bits - 1) { if (rbool[i]) { - set r[i] = 1; + set r w/= i <- 1; } } @@ -218,10 +213,10 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // ------------------------------------------------------ operation AssertBVAlgorithmWorks (r : Int[]) : Unit { let oracle = Oracle_ProductFunction_Reference(_, _, r); - AssertIntArrayEqual(BV_Algorithm(Length(r), oracle), r, "Bernstein-Vazirani algorithm failed"); + AllEqualityFactI(BV_Algorithm(Length(r), oracle), r, "Bernstein-Vazirani algorithm failed"); let nu = GetOracleCallsCount(oracle); - AssertBoolEqual(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); + EqualityFactB(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); } @@ -244,10 +239,10 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // ------------------------------------------------------ operation AssertDJAlgorithmWorks (N : Int, oracle : ((Qubit[], Qubit) => Unit), expected : Bool, msg : String) : Unit { - AssertBoolEqual(DJ_Algorithm(N, oracle), expected, msg); + EqualityFactB(DJ_Algorithm(N, oracle), expected, msg); let nu = GetOracleCallsCount(oracle); - AssertBoolEqual(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); + EqualityFactB(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); } @@ -284,11 +279,11 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { // check that the oracle was called once (later it will be called again by test harness) let nu = GetOracleCallsCount(givenOracle); - AssertBoolEqual(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); + EqualityFactB(nu <= 1, true, $"You are allowed to call the oracle at most once, and you called it {nu} times"); // 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."); + EqualityFactI(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); } @@ -318,3 +313,4 @@ namespace Quantum.Kata.DeutschJozsaAlgorithm { AssertNonameAlgorithmWorks([1, 0, 1, 0, 1, 0]); } } + diff --git a/Dockerfile b/Dockerfile index 29ea970cfa4..b619d1fabad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,7 @@ ENV PATH=$PATH:${HOME}/dotnet:${HOME}/.dotnet/tools \ DOTNET_ROOT=${HOME}/dotnet # install IQSharp -RUN dotnet tool install -g Microsoft.Quantum.IQSharp --version 0.5.1904.1302 +RUN dotnet tool install -g Microsoft.Quantum.IQSharp --version 0.6.1905.301 RUN dotnet iqsharp install --user --path-to-tool="$(which dotnet-iqsharp)" # Make sure the contents of our repo are in ${HOME} diff --git a/GHZGame/GHZGame.csproj b/GHZGame/GHZGame.csproj index fad1b532a5b..4393f66f09c 100644 --- a/GHZGame/GHZGame.csproj +++ b/GHZGame/GHZGame.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/GHZGame/ReferenceImplementation.qs b/GHZGame/ReferenceImplementation.qs index a8a17977f83..3f81e089ef3 100644 --- a/GHZGame/ReferenceImplementation.qs +++ b/GHZGame/ReferenceImplementation.qs @@ -10,9 +10,9 @@ namespace Quantum.Kata.GHZGame { + open Microsoft.Quantum.Math; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; ////////////////////////////////////////////////////////////////// @@ -42,7 +42,7 @@ namespace Quantum.Kata.GHZGame { operation PlayClassicalGHZ_Reference (strategy : (Bool => Bool), inputs : Bool[]) : Bool[] { mutable results = new Bool[Length(inputs)]; for (i in 0..Length(inputs) - 1) { - set results[i] = strategy(inputs[i]); + set results w/= i <- strategy(inputs[i]); } return results; } @@ -53,23 +53,21 @@ namespace Quantum.Kata.GHZGame { ////////////////////////////////////////////////////////////////// // Task 2.1. Entangled triple - operation CreateEntangledTriple_Reference (qs : Qubit[]) : Unit { - body (...) { - X(qs[0]); - X(qs[1]); + operation CreateEntangledTriple_Reference (qs : Qubit[]) : Unit + is Adj { + X(qs[0]); + X(qs[1]); - H(qs[0]); - H(qs[1]); - // At this point we have (|000⟩ - |010⟩ - |100⟩ + |110⟩) / 2 + H(qs[0]); + H(qs[1]); + // At this point we have (|000⟩ - |010⟩ - |100⟩ + |110⟩) / 2 - // Flip the sign of the last term - Controlled Z([qs[0]], qs[1]); + // Flip the sign of the last term + Controlled Z([qs[0]], qs[1]); - // Flip the state of the last qubit for the two middle terms - (ControlledOnBitString([false, true], X))([qs[0], qs[1]], qs[2]); - (ControlledOnBitString([true, false], X))([qs[0], qs[1]], qs[2]); - } - adjoint auto; + // Flip the state of the last qubit for the two middle terms + (ControlledOnBitString([false, true], X))([qs[0], qs[1]], qs[2]); + (ControlledOnBitString([true, false], X))([qs[0], qs[1]], qs[2]); } @@ -84,19 +82,18 @@ namespace Quantum.Kata.GHZGame { // Task 2.3. Play the GHZ game using the quantum strategy operation PlayQuantumGHZ_Reference (strategies : (Qubit => Bool)[]) : Bool[] { - mutable abc = new Bool[3]; using (qs = Qubit[3]) { CreateEntangledTriple_Reference(qs); - + + mutable abc = new Bool[3]; for (i in 0..2) { - set abc[i] = strategies[i](qs[i]); + set abc w/= i <- strategies[i](qs[i]); } ResetAll(qs); + return abc; } - - return abc; } } diff --git a/GHZGame/Tests.qs b/GHZGame/Tests.qs index 8ae39c423cc..eae100bd7f2 100644 --- a/GHZGame/Tests.qs +++ b/GHZGame/Tests.qs @@ -9,10 +9,11 @@ namespace Quantum.Kata.GHZGame { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; // All possible starting bits (r, s and t) that the referee can give // to Alice, Bob and Charlie. @@ -27,7 +28,7 @@ namespace Quantum.Kata.GHZGame { for (rst in RefereeBits()) { for (i in 0..1 <<< 3 - 1) { let abc = BoolArrFromPositiveInt(i, 3); - AssertBoolEqual( + EqualityFactB( WinCondition(rst, abc), WinCondition_Reference(rst, abc), $"Win condition is wrong for rst={rst}, abc={abc}"); @@ -47,17 +48,17 @@ namespace Quantum.Kata.GHZGame { set wins = wins + 1; } } - return ToDouble(wins) / ToDouble(N); + return IntAsDouble(wins) / IntAsDouble(N); } operation T12_RandomClassical_Test () : Unit { - AssertAlmostEqualTol(GetClassicalStrategySuccessRate(10000, RandomClassicalStrategy), 0.5, 0.02); + EqualityWithinToleranceFact(GetClassicalStrategySuccessRate(10000, RandomClassicalStrategy), 0.5, 0.02); } // ------------------------------------------------------ operation T13_BestClassical_Test () : Unit { - AssertAlmostEqualTol(GetClassicalStrategySuccessRate(10000, BestClassicalStrategy), 0.75, 0.02); + EqualityWithinToleranceFact(GetClassicalStrategySuccessRate(10000, BestClassicalStrategy), 0.75, 0.02); } @@ -74,7 +75,7 @@ namespace Quantum.Kata.GHZGame { for (mode in 0..3) { let result = PlayClassicalGHZ(TestStrategy(_, mode), rst); let expected = PlayClassicalGHZ_Reference(TestStrategy(_, mode), rst); - AssertBoolArrayEqual(result, expected, $"Unexpected result for rst={rst}"); + AllEqualityFactB(result, expected, $"Unexpected result for rst={rst}"); } } } @@ -83,7 +84,7 @@ namespace Quantum.Kata.GHZGame { // Part II. Quantum GHZ ////////////////////////////////////////////////////////////////// - operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit is Adj)) : Unit { using (qs = Qubit[N]) { // apply operation that needs to be tested taskImpl(qs); @@ -104,20 +105,20 @@ namespace Quantum.Kata.GHZGame { // ------------------------------------------------------ operation T22_QuantumStrategy_Test () : Unit { using (q = Qubit()) { - AssertBoolEqual(QuantumStrategy(false, q), false, "|0⟩ not measured as false"); + EqualityFactB(QuantumStrategy(false, q), false, "|0⟩ not measured as false"); Reset(q); X(q); - AssertBoolEqual(QuantumStrategy(false, q), true, "|1⟩ not measured as true"); + EqualityFactB(QuantumStrategy(false, q), true, "|1⟩ not measured as true"); Reset(q); H(q); - AssertBoolEqual(QuantumStrategy(true, q), false, "|+⟩ is not measured as false"); + EqualityFactB(QuantumStrategy(true, q), false, "|+⟩ is not measured as false"); Reset(q); X(q); H(q); - AssertBoolEqual(QuantumStrategy(true, q), true, "|-⟩ is not measured as true"); + EqualityFactB(QuantumStrategy(true, q), true, "|-⟩ is not measured as true"); Reset(q); } } @@ -129,7 +130,7 @@ namespace Quantum.Kata.GHZGame { let rst = (RefereeBits())[RandomInt(Length(RefereeBits()))]; let strategies = [QuantumStrategy(rst[0], _), QuantumStrategy(rst[1], _), QuantumStrategy(rst[2], _)]; let abc = PlayQuantumGHZ(strategies); - AssertBoolEqual(WinCondition_Reference(rst, abc), true, + EqualityFactB(WinCondition_Reference(rst, abc), true, $"Quantum strategy lost: for rst={rst} the players returned abc={abc}"); } } diff --git a/GroversAlgorithm/GroversAlgorithm.csproj b/GroversAlgorithm/GroversAlgorithm.csproj index 97e147cbbe3..891fde3db28 100755 --- a/GroversAlgorithm/GroversAlgorithm.csproj +++ b/GroversAlgorithm/GroversAlgorithm.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/GroversAlgorithm/ReferenceImplementation.qs b/GroversAlgorithm/ReferenceImplementation.qs index 4f7ebed6d22..76fc2a48743 100755 --- a/GroversAlgorithm/ReferenceImplementation.qs +++ b/GroversAlgorithm/ReferenceImplementation.qs @@ -10,90 +10,74 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Intrinsic; 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) : Unit { - - body (...) { - Controlled X(queryRegister, target); - } - - adjoint invert; + operation Oracle_AllOnes_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + Controlled X(queryRegister, target); } // Task 1.2. The |1010...⟩ oracle - 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); - } - - adjoint invert; + operation Oracle_AlternatingBits_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + + // 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); } - operation FlipOddPositionBits_Reference (register : Qubit[]) : Unit { + operation FlipOddPositionBits_Reference (register : Qubit[]) : Unit + is Adj { - body (...) { - // iterate over elements in odd positions (indexes are 0-based) - for (i in 1 .. 2 .. Length(register) - 1) { - X(register[i]); - } + // iterate over elements in odd positions (indexes are 0-based) + for (i in 1 .. 2 .. Length(register) - 1) { + X(register[i]); } - - adjoint invert; } // Task 1.3. Arbitrary bit pattern oracle - operation Oracle_ArbitraryPattern_Reference (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : Unit { - - body (...) { - (ControlledOnBitString(pattern, X))(queryRegister, target); - } - - adjoint invert; + operation Oracle_ArbitraryPattern_Reference (queryRegister : Qubit[], target : Qubit, pattern : Bool[]) : Unit + is Adj { + (ControlledOnBitString(pattern, X))(queryRegister, target); } // Task 1.4*. Oracle converter - operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint), register : Qubit[]) : Unit { + operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => Unit is Adj), register : Qubit[]) : Unit + is Adj { - body (...) { - using (target = Qubit()) { - // Put the target into the |-⟩ state - X(target); - H(target); + using (target = Qubit()) { + // 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); + // 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); - } + // Put the target back into |0⟩ so we can return it + H(target); + X(target); } - - adjoint invert; } - function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint)) : (Qubit[] => Unit : Adjoint) { + function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => Unit is Adj)) : (Qubit[] => Unit is Adj) { return OracleConverterImpl_Reference(markingOracle, _); } @@ -103,19 +87,16 @@ namespace Quantum.Kata.GroversAlgorithm { ////////////////////////////////////////////////////////////////// // Task 2.1. The Hadamard transform - operation HadamardTransform_Reference (register : Qubit[]) : Unit { + operation HadamardTransform_Reference (register : Qubit[]) : Unit + is Adj { - body (...) { - ApplyToEachA(H, register); + ApplyToEachA(H, register); - // ApplyToEach is a library routine that is equivalent to the following code: - // let nQubits = Length(register); - // for (idxQubit in 0..nQubits - 1) { - // H(register[idxQubit]); - // } - } - - adjoint invert; + // ApplyToEach is a library routine that is equivalent to the following code: + // let nQubits = Length(register); + // for (idxQubit in 0..nQubits - 1) { + // H(register[idxQubit]); + // } } @@ -149,16 +130,13 @@ namespace Quantum.Kata.GroversAlgorithm { // Task 2.3. The Grover iteration - operation GroverIteration_Reference (register : Qubit[], oracle : (Qubit[] => Unit : Adjoint)) : Unit { - - body (...) { - oracle(register); - HadamardTransform_Reference(register); - ConditionalPhaseFlip_Reference(register); - HadamardTransform_Reference(register); - } + operation GroverIteration_Reference (register : Qubit[], oracle : (Qubit[] => Unit is Adj)) : Unit + is Adj { - adjoint invert; + oracle(register); + HadamardTransform_Reference(register); + ConditionalPhaseFlip_Reference(register); + HadamardTransform_Reference(register); } @@ -167,18 +145,15 @@ namespace Quantum.Kata.GroversAlgorithm { ////////////////////////////////////////////////////////////////// // Task 3.1. Grover's search - operation GroversSearch_Reference (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint), iterations : Int) : Unit { + operation GroversSearch_Reference (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj), iterations : Int) : Unit + is Adj { - body (...) { - let phaseOracle = OracleConverter_Reference(oracle); - HadamardTransform_Reference(register); + let phaseOracle = OracleConverter_Reference(oracle); + HadamardTransform_Reference(register); - for (i in 1 .. iterations) { - GroverIteration_Reference(register, phaseOracle); - } + for (i in 1 .. iterations) { + GroverIteration_Reference(register, phaseOracle); } - - adjoint invert; } } diff --git a/GroversAlgorithm/Tasks.qs b/GroversAlgorithm/Tasks.qs index 7e3faa5d614..51bcc4ccef2 100755 --- a/GroversAlgorithm/Tasks.qs +++ b/GroversAlgorithm/Tasks.qs @@ -3,9 +3,10 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -18,7 +19,7 @@ namespace Quantum.Kata.GroversAlgorithm { // 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. + // - 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, @@ -94,7 +95,7 @@ namespace Quantum.Kata.GroversAlgorithm { 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"); + EqualityFactI(Length(queryRegister), Length(pattern), "Arrays should have the same length"); // ... } @@ -113,7 +114,7 @@ namespace Quantum.Kata.GroversAlgorithm { // 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) => Unit : Adjoint)) : (Qubit[] => Unit : Adjoint) { + function OracleConverter (markingOracle : ((Qubit[], Qubit) => Unit is Adj)) : (Qubit[] => Unit is Adj) { // Hint: Remember that you can define auxiliary operations. @@ -135,13 +136,9 @@ namespace Quantum.Kata.GroversAlgorithm { // // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation HadamardTransform (register : Qubit[]) : Unit + is Adj { + // ... } @@ -152,19 +149,16 @@ namespace Quantum.Kata.GroversAlgorithm { // 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[]) : 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. + operation ConditionalPhaseFlip (register : Qubit[]) : Unit + is Adj { + + // 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. + // Hint 2: You can use the same trick as in the oracle converter task. - // ... - } - - adjoint invert; + // ... } @@ -174,19 +168,16 @@ namespace Quantum.Kata.GroversAlgorithm { // 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[] => Unit : Adjoint)) : Unit { + operation GroverIteration (register : Qubit[], oracle : (Qubit[] => Unit is Adj)) : Unit + is Adj { - 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 + // 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 invert; + // ... } @@ -204,8 +195,8 @@ 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) => Unit : Adjoint), iterations : Int) : Unit { - // ... + operation GroversSearch (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj), iterations : Int) : Unit { + // ... } @@ -214,16 +205,17 @@ namespace Quantum.Kata.GroversAlgorithm { // 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 () : 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. - // Hint 2: Experiment with the number of iterations to see how it affects - // the probability of the algorithm finding the correct answer. + // 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. - // Hint 3: You can use the Message function to write the results to the console. + // Hint 2: Experiment with the number of iterations to see how it affects + // the probability of the algorithm finding the correct answer. - // ... + // Hint 3: You can use the Message function to write the results to the console. + + // ... } } diff --git a/GroversAlgorithm/Tests.qs b/GroversAlgorithm/Tests.qs index cba5feaa005..87924835714 100755 --- a/GroversAlgorithm/Tests.qs +++ b/GroversAlgorithm/Tests.qs @@ -9,30 +9,27 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // ------------------------------------------------------ // helper wrapper to represent oracle operation on input and output registers as an operation on an array of qubits - operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => Unit : Adjoint), qs : Qubit[]) : Unit { - - body (...) { - op(Most(qs), Tail(qs)); - } - - adjoint invert; + operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => Unit is Adj), qs : Qubit[]) : Unit + is Adj { + op(Most(qs), Tail(qs)); } // ------------------------------------------------------ // helper wrapper to test for operation equality on various register sizes - operation AssertRegisterOperationsEqual (testOp : (Qubit[] => Unit), refOp : (Qubit[] => Unit : Adjoint)) : Unit { + operation AssertRegisterOperationsEqual (testOp : (Qubit[] => Unit), refOp : (Qubit[] => Unit is Adj)) : Unit { for (n in 2 .. 10) { - AssertOperationsEqualReferenced(testOp, refOp, n); + AssertOperationsEqualReferenced(n, testOp, refOp); } } @@ -59,7 +56,7 @@ namespace Quantum.Kata.GroversAlgorithm { let pattern = BoolArrFromPositiveInt(RandomIntPow2(n), n); let testOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern(_, _, pattern), _); let refOp = QubitArrayWrapperOperation(Oracle_ArbitraryPattern_Reference(_, _, pattern), _); - AssertOperationsEqualReferenced(testOp, refOp, n + 1); + AssertOperationsEqualReferenced(n + 1, testOp, refOp); } } @@ -71,7 +68,7 @@ namespace Quantum.Kata.GroversAlgorithm { let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); let phaseOracleRef = OracleConverter_Reference(markingOracle); let phaseOracleSol = OracleConverter(markingOracle); - AssertOperationsEqualReferenced(phaseOracleSol, phaseOracleRef, n); + AssertOperationsEqualReferenced(n, phaseOracleSol, phaseOracleRef); } } @@ -96,7 +93,7 @@ namespace Quantum.Kata.GroversAlgorithm { let flipOracle = OracleConverter_Reference(markingOracle); let testOp = GroverIteration(_, flipOracle); let refOp = GroverIteration_Reference(_, flipOracle); - AssertOperationsEqualReferenced(testOp, refOp, n); + AssertOperationsEqualReferenced(n, testOp, refOp); } } @@ -108,7 +105,7 @@ namespace Quantum.Kata.GroversAlgorithm { let markingOracle = Oracle_ArbitraryPattern_Reference(_, _, pattern); let testOp = GroversSearch(_, markingOracle, 4); let refOp = GroversSearch_Reference(_, markingOracle, 4); - AssertOperationsEqualReferenced(testOp, refOp, n); + AssertOperationsEqualReferenced(n, testOp, refOp); } } diff --git a/JointMeasurements/JointMeasurements.csproj b/JointMeasurements/JointMeasurements.csproj index 12477ccb0be..c54f1b395c5 100644 --- a/JointMeasurements/JointMeasurements.csproj +++ b/JointMeasurements/JointMeasurements.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/JointMeasurements/ReferenceImplementation.qs b/JointMeasurements/ReferenceImplementation.qs index b3be7f36e7f..566bc2e171b 100644 --- a/JointMeasurements/ReferenceImplementation.qs +++ b/JointMeasurements/ReferenceImplementation.qs @@ -10,10 +10,11 @@ namespace Quantum.Kata.JointMeasurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Characterization; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // Task 1. Single-qubit measurement diff --git a/JointMeasurements/Tasks.qs b/JointMeasurements/Tasks.qs index 3d910d414cc..7a1369f726d 100644 --- a/JointMeasurements/Tasks.qs +++ b/JointMeasurements/Tasks.qs @@ -3,10 +3,10 @@ namespace Quantum.Kata.JointMeasurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// diff --git a/JointMeasurements/Tests.qs b/JointMeasurements/Tests.qs index 1303cf4bf3f..fbdf8b3b4ea 100644 --- a/JointMeasurements/Tests.qs +++ b/JointMeasurements/Tests.qs @@ -9,16 +9,16 @@ namespace Quantum.Kata.JointMeasurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // "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) => Unit : Adjoint), testImpl : (Qubit[] => Int), preserveState : Bool) : Unit { + operation DistinguishStates_MultiQubit (Nqubit : Int, Nstate : Int, statePrep : ((Qubit[], Int, Double) => Unit is Adj), testImpl : (Qubit[] => Int), preserveState : Bool) : Unit { let nTotal = 100; mutable nOk = 0; @@ -36,7 +36,7 @@ namespace Quantum.Kata.JointMeasurements { // get the solution's answer and verify that it's a match let ans = testImpl(qs); if (ans == state) { - set nOk = nOk + 1; + set nOk += 1; } if (preserveState) { @@ -50,29 +50,26 @@ namespace Quantum.Kata.JointMeasurements { } } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + EqualityFactI(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } // ------------------------------------------------------ - operation StatePrep_ParityMeasurement (qs : Qubit[], state : Int, alpha : Double) : Unit { + operation StatePrep_ParityMeasurement (qs : Qubit[], state : Int, alpha : Double) : Unit + is Adj { - body (...) { - // prep cos(alpha) * |0..0⟩ + sin(alpha) * |1..1⟩ - Ry(2.0 * alpha, qs[0]); - for (i in 1 .. Length(qs) - 1) { - CNOT(qs[0], qs[i]); - } + // prep cos(alpha) * |0..0⟩ + sin(alpha) * |1..1⟩ + Ry(2.0 * alpha, qs[0]); + 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) { - X(qs[i]); - } + if (state == 1) { + // flip the state of the last half of the qubits + for (i in 0 .. Length(qs) / 2 - 1) { + X(qs[i]); } } - - adjoint invert; } @@ -95,46 +92,38 @@ namespace Quantum.Kata.JointMeasurements { // ------------------------------------------------------ - operation StatePrep_WState_Arbitrary (qs : Qubit[]) : Unit { + operation StatePrep_WState_Arbitrary (qs : Qubit[]) : Unit + is Adj + Ctl { - body (...) { - let N = Length(qs); + let N = Length(qs); - if (N == 1) { - // base case of recursion: |1⟩ - X(qs[0]); - } - 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]); + if (N == 1) { + // base case of recursion: |1⟩ + X(qs[0]); + } + 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(IntAsDouble(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]); - X(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]); + X(qs[0]); } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; } - operation StatePrep_GHZOrWState (qs : Qubit[], state : Int, alpha : Double) : Unit { + operation StatePrep_GHZOrWState (qs : Qubit[], state : Int, alpha : Double) : Unit + is Adj { - body (...) { - if (state == 0) { - StatePrep_ParityMeasurement(qs, 0, alpha); - } else { - StatePrep_WState_Arbitrary(qs); - } + if (state == 0) { + StatePrep_ParityMeasurement(qs, 0, alpha); + } else { + StatePrep_WState_Arbitrary(qs); } - - adjoint invert; } @@ -146,22 +135,19 @@ namespace Quantum.Kata.JointMeasurements { // ------------------------------------------------------ - operation StatePrep_DifferentBasis (qs : Qubit[], state : Int, alpha : Double) : Unit { + operation StatePrep_DifferentBasis (qs : Qubit[], state : Int, alpha : Double) : Unit + is Adj { - body (...) { - // prep cos(alpha) * |00⟩ + sin(alpha) * |11⟩ - Ry(2.0 * alpha, qs[0]); - CNOT(qs[0], qs[1]); + // 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); + if (state == 1) { + X(qs[1]); } - - adjoint invert; + + // convert to X basis + ApplyToEachA(H, qs); } @@ -172,13 +158,9 @@ namespace Quantum.Kata.JointMeasurements { // ------------------------------------------------------ // prepare state |A⟩ = cos(α) * |0⟩ + sin(α) * |1⟩ - operation StatePrep_A (alpha : Double, q : Qubit) : Unit { - - body (...) { - Ry(2.0 * alpha, q); - } - - adjoint invert; + operation StatePrep_A (alpha : Double, q : Qubit) : Unit + is Adj { + Ry(2.0 * alpha, q); } @@ -189,7 +171,7 @@ namespace Quantum.Kata.JointMeasurements { using (qs = Qubit[2]) { for (i in 0 .. 36) { - let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; // prepare A state StatePrep_A(alpha, qs[0]); @@ -221,8 +203,8 @@ namespace Quantum.Kata.JointMeasurements { 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); + AssertOperationsEqualReferenced(2, CNOTWrapper, ControlledX_General_Reference); + AssertOperationsEqualReferenced(2, ControlledX_General, ControlledX_General_Reference); } } diff --git a/Measurements/Measurements.csproj b/Measurements/Measurements.csproj index d90dbd5d6da..ff1aff6b213 100644 --- a/Measurements/Measurements.csproj +++ b/Measurements/Measurements.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/Measurements/Measurements.ipynb b/Measurements/Measurements.ipynb index 0e6dbb1b7a8..26e9ed36b8f 100644 --- a/Measurements/Measurements.ipynb +++ b/Measurements/Measurements.ipynb @@ -32,7 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "%package Microsoft.Quantum.Katas::0.5.1904.1302" + "%package Microsoft.Quantum.Katas::0.6.1905.301" ] }, { @@ -42,13 +42,13 @@ "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", ">
\n", "> How to install the right IQ# version\n", - "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.5.1904.1302, the installation steps are as follows:\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.6.1905.301, the installation steps are as follows:\n", ">\n", "> 1. Stop the kernel.\n", "> 2. Uninstall the existing version of IQ#:\n", "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", "> 3. Install the matching version:\n", - "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.5.1904.1302\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.6.1905.301\n", "> 4. Reinstall the kernel:\n", "> dotnet iqsharp install\n", "> 5. Restart the Notebook.\n", diff --git a/Measurements/ReferenceImplementation.qs b/Measurements/ReferenceImplementation.qs index f389deddb1d..cc2c590d585 100644 --- a/Measurements/ReferenceImplementation.qs +++ b/Measurements/ReferenceImplementation.qs @@ -10,10 +10,12 @@ namespace Quantum.Kata.Measurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -139,7 +141,7 @@ namespace Quantum.Kata.Measurements { for (q in qs) { if (M(q) == One) { - set countOnes = countOnes + 1; + set countOnes += 1; } } @@ -165,7 +167,7 @@ namespace Quantum.Kata.Measurements { for (q in qs) { if (M(q) == One) { - set countOnes = countOnes + 1; + set countOnes += 1; } } @@ -429,7 +431,6 @@ namespace Quantum.Kata.Measurements { // |+⟩ | 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 @@ -437,28 +438,16 @@ namespace Quantum.Kata.Measurements { // 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; - } + // result is One only if the state was |+⟩ + return result == One ? 1 | -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; - } + // result is One only if the state was |0⟩ + return result == One ? 0 | -1; } - - return output; } @@ -468,7 +457,7 @@ namespace Quantum.Kata.Measurements { // |A⟩ = 1/sqrt(2) (|0⟩ + |1⟩), // |B⟩ = 1/sqrt(2) (|0⟩ + ω |1⟩), // |C⟩ = 1/sqrt(2) (|0⟩ + ω² |1⟩). - // where ω = exp(2π/3) denotes a primitive, complex 3rd root of unity. + // where ω = exp(2π/3) denotes a primitive, complex 3rd root of unity. // Output: 1 or 2 if qubit was in the |A⟩ state, // 0 or 2 if qubit was in the |B⟩ state, // 0 or 1 if qubit was in the |C⟩ state. @@ -489,7 +478,6 @@ namespace Quantum.Kata.Measurements { // a 4x4 unitary. Using the "Rader trick" we can now block decompose the 3x3 DFT and obtain two // 2x2 blocks which we can then implement using controlled single qubit gates. We present // the final resulting circuit without additional commentary. - mutable output = 0; let alpha = ArcCos(Sqrt(2.0 / 3.0)); using (a = Qubit()) { @@ -510,20 +498,18 @@ namespace Quantum.Kata.Measurements { // dispatch on the cases if (res0 == Zero and res1 == Zero) { - set output = 0; + return 0; } elif (res0 == One and res1 == Zero) { - set output = 1; + return 1; } elif (res0 == Zero and res1 == One) { - set output = 2; + return 2; } else { // this should never occur - set output = 3; + return 3; } } - - return output; } } diff --git a/Measurements/Tasks.qs b/Measurements/Tasks.qs index 7088a969c3e..adb30677aa8 100644 --- a/Measurements/Tasks.qs +++ b/Measurements/Tasks.qs @@ -3,10 +3,10 @@ namespace Quantum.Kata.Measurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -270,7 +270,7 @@ namespace Quantum.Kata.Measurements { // |A⟩ = 1/sqrt(2) (|0⟩ + |1⟩), // |B⟩ = 1/sqrt(2) (|0⟩ + ω |1⟩), // |C⟩ = 1/sqrt(2) (|0⟩ + ω² |1⟩), - // where ω = exp(2iπ/3) denotes a primitive, complex 3rd root of unity. + // where ω = exp(2iπ/3) denotes a primitive, complex 3rd root of unity. // Output: 1 or 2 if the qubit was in the |A⟩ state, // 0 or 2 if the qubit was in the |B⟩ state, // 0 or 1 if the qubit was in the |C⟩ state. diff --git a/Measurements/Tests.qs b/Measurements/Tests.qs index 7ce942b08f8..3bf647ec4c4 100644 --- a/Measurements/Tests.qs +++ b/Measurements/Tests.qs @@ -9,11 +9,11 @@ namespace Quantum.Kata.Measurements { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; open Quantum.Kata.Utils; @@ -38,7 +38,7 @@ namespace Quantum.Kata.Measurements { // 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; + set nOk += 1; } // we're not checking the state of the qubit after the operation @@ -46,15 +46,13 @@ namespace Quantum.Kata.Measurements { } } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + EqualityFactI(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 { + if (state != 0) { // convert |0⟩ to |1⟩ X(q); } @@ -70,7 +68,7 @@ namespace Quantum.Kata.Measurements { operation T102_InitializeQubit_Test () : Unit { using (qs = Qubit[1]) { for (i in 0 .. 36) { - let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; Ry(2.0 * alpha, qs[0]); // Test Task 1 @@ -126,7 +124,7 @@ namespace Quantum.Kata.Measurements { DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitA(PI() / 4.0, _)); for (i in 0 .. 10) { - let alpha = (PI() * ToDouble(i)) / 10.0; + let alpha = (PI() * IntAsDouble(i)) / 10.0; DistinguishTwoStates_OneQubit(StatePrep_IsQubitA(alpha, _, _), IsQubitA(alpha, _)); } } @@ -158,12 +156,12 @@ namespace Quantum.Kata.Measurements { // get the solution's answer and verify that it's a match let ans = testImpl(qs); if (ans == state) { - set nOk = nOk + 1; + set nOk += 1; } // if we have a max number of measurements per solution run specified, check that it is not exceeded if (measurementsPerRun > 0) { let nm = GetOracleCallsCount(M) + GetOracleCallsCount(Measure); - AssertBoolEqual(nm <= 1, true, $"You are allowed to do at most one measurement, and you did {nm}"); + EqualityFactB(nm <= 1, true, $"You are allowed to do at most one measurement, and you did {nm}"); } // we're not checking the state of the qubit after the operation @@ -171,7 +169,7 @@ namespace Quantum.Kata.Measurements { } } - AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + EqualityFactI(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); } @@ -221,11 +219,8 @@ namespace Quantum.Kata.Measurements { operation StatePrep_TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[], state : Int) : Unit { - if (state == 0) { - StatePrep_Bitstring(qs, bits1); - } else { - StatePrep_Bitstring(qs, bits2); - } + let bits = state == 0 ? bits1 | bits2; + StatePrep_Bitstring(qs, bits); } @@ -243,31 +238,25 @@ namespace Quantum.Kata.Measurements { // ------------------------------------------------------ - operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit { - - body (...) { - let N = Length(qs); + operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit + is Adj + Ctl { + let N = Length(qs); - if (N == 1) { - // base case of recursion: |1⟩ - X(qs[0]); - } 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]); + if (N == 1) { + // base case of recursion: |1⟩ + X(qs[0]); + } 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(IntAsDouble(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]); - X(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]); + X(qs[0]); } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; } @@ -289,16 +278,13 @@ namespace Quantum.Kata.Measurements { // ------------------------------------------------------ - operation GHZ_State_Reference (qs : Qubit[]) : Unit { + operation GHZ_State_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - H(qs[0]); - for (i in 1 .. Length(qs) - 1) { - CNOT(qs[0], qs[i]); - } + H(qs[0]); + for (i in 1 .. Length(qs) - 1) { + CNOT(qs[0], qs[i]); } - - adjoint invert; } @@ -393,23 +379,20 @@ namespace Quantum.Kata.Measurements { // ------------------------------------------------------ - operation StatePrep_ThreeQubitMeasurement (qs : Qubit[], state : Int) : Unit { + operation StatePrep_ThreeQubitMeasurement (qs : Qubit[], state : Int) : Unit + is Adj { - body (...) { - WState_Arbitrary_Reference(qs); + WState_Arbitrary_Reference(qs); - if (state == 0) { - // prep 1/sqrt(3) ( |100⟩ + ω |010⟩ + ω² |001⟩ ) - R1(2.0 * PI() / 3.0, qs[1]); - R1(4.0 * PI() / 3.0, qs[2]); - } else { - // prep 1/sqrt(3) ( |100⟩ + ω² |010⟩ + ω |001⟩ ) - R1(4.0 * PI() / 3.0, qs[1]); - R1(2.0 * PI() / 3.0, qs[2]); - } + if (state == 0) { + // prep 1/sqrt(3) ( |100⟩ + ω |010⟩ + ω² |001⟩ ) + R1(2.0 * PI() / 3.0, qs[1]); + R1(4.0 * PI() / 3.0, qs[2]); + } else { + // prep 1/sqrt(3) ( |100⟩ + ω² |010⟩ + ω |001⟩ ) + R1(4.0 * PI() / 3.0, qs[1]); + R1(2.0 * PI() / 3.0, qs[2]); } - - adjoint invert; } operation T113_ThreeQubitMeasurement_Test () : Unit { @@ -423,9 +406,7 @@ namespace Quantum.Kata.Measurements { operation StatePrep_IsQubitZeroOrPlus (q : Qubit, state : Int) : Unit { - if (state == 0) { - // convert |0⟩ to |0⟩ - } else { + if (state != 0) { // convert |0⟩ to |+⟩ H(q); } @@ -449,7 +430,7 @@ namespace Quantum.Kata.Measurements { // 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; + set nOk += 1; } // we're not checking the state of the qubit after the operation @@ -457,7 +438,7 @@ namespace Quantum.Kata.Measurements { } } - if (ToDouble(nOk) < threshold * ToDouble(nTotal)) { + if (IntAsDouble(nOk) < threshold * IntAsDouble(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}%."; } } @@ -504,15 +485,15 @@ namespace Quantum.Kata.Measurements { // keep track of the number of inconclusive answers given if (ans == -1) { - set nInconc = nInconc + 1; + set nInconc += 1; } if (ans == 0 and state == 0) { - set nConclOne = nConclOne + 1; + set nConclOne += 1; } if (ans == 1 and state == 1) { - set nConclPlus = nConclPlus + 1; + set nConclPlus += 1; } // check if upon conclusive result the answer is actually correct @@ -525,15 +506,15 @@ namespace Quantum.Kata.Measurements { } } - if (ToDouble(nInconc) > thresholdInconcl * ToDouble(nTotal)) { + if (IntAsDouble(nInconc) > thresholdInconcl * IntAsDouble(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)) { + if (IntAsDouble(nConclOne) < thresholdConcl * IntAsDouble(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)) { + if (IntAsDouble(nConclPlus) < thresholdConcl * IntAsDouble(nTotal)) { fail $"Only {nConclPlus} test runs out of {nTotal} returned conclusive |+> which does not meet the required threshold of at least {thresholdConcl * 100.0}%."; } } diff --git a/PhaseEstimation/CounterSimulator.cs b/PhaseEstimation/CounterSimulator.cs index 20052007db1..a260a2cab0e 100644 --- a/PhaseEstimation/CounterSimulator.cs +++ b/PhaseEstimation/CounterSimulator.cs @@ -114,7 +114,7 @@ public override Qubit Apply() return base.Apply(); } - public override QArray Apply(long count) + public override IQArray Apply(long count) { _sim._qubitsAllocated += count; if (_sim._qubitsAllocated > _sim._maxQubitsAllocated) @@ -140,7 +140,7 @@ public override void Apply(Qubit q) base.Apply(q); } - public override void Apply(QArray qubits) + public override void Apply(IQArray qubits) { _sim._qubitsAllocated -= qubits.Length; base.Apply(qubits); diff --git a/PhaseEstimation/PhaseEstimation.csproj b/PhaseEstimation/PhaseEstimation.csproj index 9e1df1ff514..9f8eb11d31a 100644 --- a/PhaseEstimation/PhaseEstimation.csproj +++ b/PhaseEstimation/PhaseEstimation.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/PhaseEstimation/ReferenceImplementation.qs b/PhaseEstimation/ReferenceImplementation.qs index b5121ee85f3..2ac8cb1524e 100644 --- a/PhaseEstimation/ReferenceImplementation.qs +++ b/PhaseEstimation/ReferenceImplementation.qs @@ -10,11 +10,15 @@ namespace Quantum.Kata.PhaseEstimation { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Characterization; + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Oracles; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -22,39 +26,32 @@ namespace Quantum.Kata.PhaseEstimation { ////////////////////////////////////////////////////////////////// // Task 1.1. Inputs to QPE: eigenstates of Z/S/T gates. - operation Eigenstates_ZST_Reference (q : Qubit, state : Int) : Unit { + operation Eigenstates_ZST_Reference (q : Qubit, state : Int) : Unit + is Adj { - body (...) { - if (state == 1) { - X(q); - } + if (state == 1) { + X(q); } - - adjoint auto; } // ------------------------------------------------------ - operation UnitaryPowerImpl_Reference (U : (Qubit => Unit : Adjoint, Controlled), power : Int, q : Qubit) : Unit { - body (...) { - for (i in 1..power) { - U(q); - } + operation UnitaryPowerImpl_Reference (U : (Qubit => Unit is Adj + Ctl), power : Int, q : Qubit) : Unit + is Adj + Ctl { + for (i in 1..power) { + U(q); } - adjoint auto; - controlled auto; - controlled adjoint auto; } // Task 1.2. Inputs to QPE: powers of Z/S/T gates. - function UnitaryPower_Reference (U : (Qubit => Unit : Adjoint, Controlled), power : Int) : (Qubit => Unit : Adjoint, Controlled) { + function UnitaryPower_Reference (U : (Qubit => Unit is Adj + Ctl), power : Int) : (Qubit => Unit is Adj + Ctl) { return UnitaryPowerImpl_Reference(U, power, _); } // ------------------------------------------------------ // Task 1.3. Validate inputs to QPE - operation AssertIsEigenstate_Reference (U : (Qubit => Unit), P : (Qubit => Unit : Adjoint)) : Unit { + operation AssertIsEigenstate_Reference (U : (Qubit => Unit), P : (Qubit => Unit is Adj)) : Unit { using (q = Qubit()) { // Prepare the state |ψ⟩ P(q); @@ -69,22 +66,17 @@ namespace Quantum.Kata.PhaseEstimation { // ------------------------------------------------------ - operation Oracle_Reference (U : (Qubit => Unit : Adjoint, Controlled), power : Int, target : Qubit[]) : Unit { - body (...) { - for (i in 1 .. power) { - U(target[0]); - } + operation Oracle_Reference (U : (Qubit => Unit is Adj + Ctl), power : Int, target : Qubit[]) : Unit + is Adj + Ctl{ + for (i in 1 .. power) { + U(target[0]); } - adjoint auto; - controlled auto; - controlled adjoint auto; } // Task 1.4. QPE for single-qubit unitaries - operation QPE_Reference (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint), n : Int) : Double { + operation QPE_Reference (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj), n : Int) : Double { // Construct a phase estimation oracle from the unitary let oracle = DiscreteOracle(Oracle_Reference(U, _, _)); - mutable phase = -1.0; // Allocate qubits to hold the eigenstate of U and the phase in a big endian register using ((eigenstate, phaseRegister) = (Qubit[1], Qubit[n])) { let phaseRegisterBE = BigEndian(phaseRegister); @@ -93,13 +85,12 @@ namespace Quantum.Kata.PhaseEstimation { // Call library QuantumPhaseEstimation(oracle, eigenstate, phaseRegisterBE); // Read out the phase - set phase = ToDouble(MeasureIntegerBE(phaseRegisterBE)) / ToDouble(1 <<< n); + let phase = IntAsDouble(MeasureIntegerBE(phaseRegisterBE)) / IntAsDouble(1 <<< n); ResetAll(eigenstate); ResetAll(phaseRegister); + return phase; } - - return phase; } @@ -108,80 +99,70 @@ namespace Quantum.Kata.PhaseEstimation { ////////////////////////////////////////////////////////////////// // Task 2.1. Single-bit phase estimation - operation SingleBitPE_Reference (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint)) : Int { - mutable eigenvalue = 0; + operation SingleBitPE_Reference (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj)) : Int { using ((control, eigenstate) = (Qubit(), Qubit())) { // prepare the eigenstate |ψ⟩ P(eigenstate); H(control); - (Controlled U)([control], eigenstate); + Controlled U([control], eigenstate); H(control); - set eigenvalue = M(control) == Zero ? 1 | -1; - + let eigenvalue = M(control) == Zero ? 1 | -1; ResetAll([control, eigenstate]); + return eigenvalue; } - return eigenvalue; } // Task 2.2. Two bit phase estimation - operation TwoBitPE_Reference (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint)) : Double { + operation TwoBitPE_Reference (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj)) : Double { + // Start by using the same circuit as in task 2.1. // For eigenvalues +1 and -1, it produces measurement results Zero and One, respectively, 100% of the time; // for eigenvalues +i and -i, it produces both results with 50% probability, so a different circuit is required. - mutable (nZero, nOne) = (0, 0); using ((control, eigenstate) = (Qubit(), Qubit())) { // prepare the eigenstate |ψ⟩ P(eigenstate); + mutable (measuredZero, measuredOne) = (false, false); mutable iter = 0; repeat { - set iter = iter + 1; + set iter += 1; H(control); - (Controlled U)([control], eigenstate); + Controlled U([control], eigenstate); H(control); - if (MResetZ(control) == Zero) { - set nZero = nZero + 1; - } else { - set nOne = nOne + 1; - } - - // repeat the loop until we get both Zero and One measurement outcomes - // or until we're reasonably certain that we won't get a different outcome - } until (iter == 10 or nZero > 0 and nOne > 0) + let meas = MResetZ(control); + set (measuredZero, measuredOne) = (meas == Zero, meas == One); + } + // repeat the loop until we get both Zero and One measurement outcomes + // or until we're reasonably certain that we won't get a different outcome + until (iter == 10 or measuredZero and measuredOne) fixup {} - Reset(eigenstate); - } - if (nOne == 0) { // all measurements yielded Zero => eigenvalue +1 - return 0.0; - } - if (nZero == 0) { // all measurements yielded One => eigenvalue -1 - return 0.5; + if (not measuredZero or not measuredOne) { + return measuredOne ? 0.5 | 0.0; + } } // To distinguish between eigenvalues i and -i, we need a circuit with an extra S gate on control qubit - mutable eigenvalue = -1.0; using ((control, eigenstate) = (Qubit(), Qubit())) { // prepare the eigenstate |ψ⟩ P(eigenstate); H(control); - (Controlled U)([control], eigenstate); + Controlled U([control], eigenstate); S(control); H(control); - set eigenvalue = MResetZ(control) == Zero ? 0.75 | 0.25; - + let eigenvalue = MResetZ(control) == Zero ? 0.75 | 0.25; Reset(eigenstate); + return eigenvalue; } - return eigenvalue; } } diff --git a/PhaseEstimation/Tasks.qs b/PhaseEstimation/Tasks.qs index ebb77c4695e..5d2cd15aa2b 100644 --- a/PhaseEstimation/Tasks.qs +++ b/PhaseEstimation/Tasks.qs @@ -3,12 +3,12 @@ namespace Quantum.Kata.PhaseEstimation { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Diagnostics; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + ////////////////////////////////////////////////////////////////// // Welcome! @@ -41,13 +41,9 @@ namespace Quantum.Kata.PhaseEstimation { // Goal: // Prepare one of the eigenstates of Z gate (which are the same as eigenstates of S or T gates): // eigenstate |0⟩ if state = 0, or eigenstate |1⟩ if state = 1. - operation Eigenstates_ZST (q : Qubit, state : Int) : Unit { - - body (...) { - // ... - } - - adjoint auto; + operation Eigenstates_ZST (q : Qubit, state : Int) : Unit + is Adj { + // ... } @@ -57,7 +53,7 @@ namespace Quantum.Kata.PhaseEstimation { // 2) a positive integer power. // Output: // A single-qubit unitary equal to U raised to the given power. - function UnitaryPower (U : (Qubit => Unit : Adjoint, Controlled), power : Int) : (Qubit => Unit : Adjoint, Controlled) { + function UnitaryPower (U : (Qubit => Unit is Adj + Ctl), power : Int) : (Qubit => Unit is Adj + Ctl) { // Hint: Remember that you can define auxiliary operations. // ... @@ -76,7 +72,7 @@ namespace Quantum.Kata.PhaseEstimation { // Goal: // Assert that the given state is an eigenstate of the given unitary, // i.e., do nothing if it is, and throw an exception if it is not. - operation AssertIsEigenstate (U : (Qubit => Unit), P : (Qubit => Unit : Adjoint)) : Unit { + operation AssertIsEigenstate (U : (Qubit => Unit), P : (Qubit => Unit is Adj)) : Unit { // ... } @@ -90,7 +86,7 @@ namespace Quantum.Kata.PhaseEstimation { // Output: // The phase of the eigenvalue that corresponds to the eigenstate |ψ⟩, with n bits of precision. // The phase should be between 0 and 1. - operation QPE (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint), n : Int) : Double { + operation QPE (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj), n : Int) : Double { // ... return -1.0; } @@ -138,7 +134,7 @@ namespace Quantum.Kata.PhaseEstimation { // The eigenvalue which corresponds to the eigenstate |ψ⟩ (+1 or -1). // // You are allowed to allocate exactly two qubits and call Controlled U exactly once. - operation SingleBitPE (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint)) : Int { + operation SingleBitPE (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj)) : Int { // Note: It is possible to use the QPE implementation from task 1.4 to solve this task, // but we suggest you implement the circuit by hand for the sake of learning. @@ -158,7 +154,7 @@ namespace Quantum.Kata.PhaseEstimation { // The returned value has to be accurate within the absolute error of 0.001. // // You are allowed to allocate exactly two qubits and call Controlled U multiple times. - operation TwoBitPE (U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint)) : Double { + operation TwoBitPE (U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj)) : Double { // Hint: Start by applying the same circuit as in task 2.1. // What are the possible outcomes for each eigenvalue? // What eigenvalues you can and can not distinguish using this circuit? diff --git a/PhaseEstimation/Tests.qs b/PhaseEstimation/Tests.qs index ee307effcd9..baf7a324bd8 100644 --- a/PhaseEstimation/Tests.qs +++ b/PhaseEstimation/Tests.qs @@ -9,18 +9,18 @@ namespace Quantum.Kata.PhaseEstimation { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// // Part I. Quantum phase estimation (QPE) ////////////////////////////////////////////////////////////////// - operation AssertEqualOnZeroState1 (testImpl : (Qubit => Unit), refImpl : (Qubit => Unit : Adjoint)) : Unit { + operation AssertEqualOnZeroState1 (testImpl : (Qubit => Unit), refImpl : (Qubit => Unit is Adj)) : Unit { using (q = Qubit()) { // apply operation that needs to be tested testImpl(q); @@ -42,23 +42,17 @@ namespace Quantum.Kata.PhaseEstimation { // ------------------------------------------------------ // helper wrapper to represent operation on one qubit as an operation on an array of qubits - operation ArrayWrapperOperation1 (op : (Qubit => Unit : Adjoint, Controlled), qs : Qubit[]) : Unit { - - body (...) { - op(qs[0]); - } - - adjoint auto; - controlled auto; - controlled adjoint auto; + operation ArrayWrapperOperation1 (op : (Qubit => Unit is Adj + Ctl), qs : Qubit[]) : Unit + is Adj + Ctl { + op(qs[0]); } operation T12_UnitaryPower_Test () : Unit { for (U in [Z, S, T]) { for (power in 1..5) { - AssertOperationsEqualReferenced(ArrayWrapperOperation1(UnitaryPower(U, power), _), - ArrayWrapperOperation1(UnitaryPower_Reference(U, power), _), 1); + AssertOperationsEqualReferenced(1, ArrayWrapperOperation1(UnitaryPower(U, power), _), + ArrayWrapperOperation1(UnitaryPower_Reference(U, power), _)); } } } @@ -78,14 +72,14 @@ namespace Quantum.Kata.PhaseEstimation { // ------------------------------------------------------ operation T14_QPE_Test () : Unit { - AssertAlmostEqualTol(QPE(Z, I, 1), 0.0, 0.25); - AssertAlmostEqualTol(QPE(Z, X, 1), 0.5, 0.25); + EqualityWithinToleranceFact(QPE(Z, I, 1), 0.0, 0.25); + EqualityWithinToleranceFact(QPE(Z, X, 1), 0.5, 0.25); - AssertAlmostEqualTol(QPE(S, I, 2), 0.0, 0.125); - AssertAlmostEqualTol(QPE(S, X, 2), 0.25, 0.125); + EqualityWithinToleranceFact(QPE(S, I, 2), 0.0, 0.125); + EqualityWithinToleranceFact(QPE(S, X, 2), 0.25, 0.125); - AssertAlmostEqualTol(QPE(T, I, 3), 0.0, 0.0625); - AssertAlmostEqualTol(QPE(T, X, 3), 0.125, 0.0625); + EqualityWithinToleranceFact(QPE(T, I, 3), 0.0, 0.0625); + EqualityWithinToleranceFact(QPE(T, X, 3), 0.125, 0.0625); } @@ -93,18 +87,18 @@ namespace Quantum.Kata.PhaseEstimation { // Part II. Iterative phase estimation ////////////////////////////////////////////////////////////////// - operation Test1BitPEOnOnePair(U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint), expected : Int) : Unit { + operation Test1BitPEOnOnePair(U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj), expected : Int) : Unit { ResetQubitCount(); ResetOracleCallsCount(); let actual = SingleBitPE(U, P); - AssertIntEqual(actual, expected, $"Unexpected return for ({U}, {P}): expected {expected}, got {actual}"); + EqualityFactI(actual, expected, $"Unexpected return for ({U}, {P}): expected {expected}, got {actual}"); let nq = GetMaxQubitCount(); - AssertIntEqual(nq, 2, $"You are allowed to allocate exactly 2 qubits, and you allocated {nq}"); + EqualityFactI(nq, 2, $"You are allowed to allocate exactly 2 qubits, and you allocated {nq}"); let nu = GetOracleCallsCount(Controlled U); - AssertIntEqual(nu, 1, $"You are allowed to call Controlled U exactly once, and you called it {nu} times"); + EqualityFactI(nu, 1, $"You are allowed to call Controlled U exactly once, and you called it {nu} times"); } operation T21_SingleBitPE_Test () : Unit { @@ -116,14 +110,14 @@ namespace Quantum.Kata.PhaseEstimation { // ------------------------------------------------------ - operation Test2BitPEOnOnePair(U : (Qubit => Unit : Adjoint, Controlled), P : (Qubit => Unit : Adjoint), expected : Double) : Unit { + operation Test2BitPEOnOnePair(U : (Qubit => Unit is Adj + Ctl), P : (Qubit => Unit is Adj), expected : Double) : Unit { ResetQubitCount(); let actual = TwoBitPE(U, P); - AssertAlmostEqualTol(actual, expected, 0.001); + EqualityWithinToleranceFact(actual, expected, 0.001); let nq = GetMaxQubitCount(); - AssertIntEqual(nq, 2, $"You are allowed to allocate exactly 2 qubits, and you allocated {nq}"); + EqualityFactI(nq, 2, $"You are allowed to allocate exactly 2 qubits, and you allocated {nq}"); } operation T22_TwoBitPE_Test () : Unit { diff --git a/QEC_BitFlipCode/QEC_BitFlipCode.csproj b/QEC_BitFlipCode/QEC_BitFlipCode.csproj index eb08ff14ee4..f4d924b48c6 100644 --- a/QEC_BitFlipCode/QEC_BitFlipCode.csproj +++ b/QEC_BitFlipCode/QEC_BitFlipCode.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/QEC_BitFlipCode/ReferenceImplementation.qs b/QEC_BitFlipCode/ReferenceImplementation.qs index 785ab7aa737..ea6460b716b 100644 --- a/QEC_BitFlipCode/ReferenceImplementation.qs +++ b/QEC_BitFlipCode/ReferenceImplementation.qs @@ -10,7 +10,8 @@ namespace Quantum.Kata.QEC_BitFlipCode { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -21,13 +22,9 @@ namespace Quantum.Kata.QEC_BitFlipCode { // Task 2. Encoding Codewords - operation Encode_Reference (register : Qubit[]) : Unit { - - body (...) { - ApplyToEachA(CNOT(Head(register), _), Rest(register)); - } - - adjoint invert; + operation Encode_Reference (register : Qubit[]) : Unit + is Adj { + ApplyToEachA(CNOT(Head(register), _), Rest(register)); } diff --git a/QEC_BitFlipCode/Tasks.qs b/QEC_BitFlipCode/Tasks.qs index e705e283d45..a5c9e5e208e 100644 --- a/QEC_BitFlipCode/Tasks.qs +++ b/QEC_BitFlipCode/Tasks.qs @@ -3,7 +3,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; diff --git a/QEC_BitFlipCode/Tests.qs b/QEC_BitFlipCode/Tests.qs index 805c7464b9a..1124cddeff6 100644 --- a/QEC_BitFlipCode/Tests.qs +++ b/QEC_BitFlipCode/Tests.qs @@ -9,12 +9,13 @@ namespace Quantum.Kata.QEC_BitFlipCode { + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; 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; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Bitwise; ////////////////////////////////////////////////////////////////////////// @@ -29,23 +30,20 @@ namespace Quantum.Kata.QEC_BitFlipCode { } - operation StatePrep_Bitmask (qs : Qubit[], bits : Int) : Unit { + operation StatePrep_Bitmask (qs : Qubit[], bits : Int) : Unit + is Adj { - body (...) { - if (bits / 4 == 1) { - X(qs[0]); - } + if (bits / 4 == 1) { + X(qs[0]); + } - if ((bits / 2) % 2 == 1) { - X(qs[1]); - } + if ((bits / 2) % 2 == 1) { + X(qs[1]); + } - if (bits % 2 == 1) { - X(qs[2]); - } + if (bits % 2 == 1) { + X(qs[2]); } - - adjoint invert; } @@ -65,33 +63,30 @@ namespace Quantum.Kata.QEC_BitFlipCode { } - operation StatePrep_TwoBitmasks (qs : Qubit[], bits1 : Int[], bits2 : Int[]) : Unit { + operation StatePrep_TwoBitmasks (qs : Qubit[], bits1 : Int[], bits2 : Int[]) : Unit + is Adj { - body (...) { - let firstDiff = FindFirstDiff_Reference(bits1, bits2); - H(qs[firstDiff]); + let firstDiff = FindFirstDiff_Reference(bits1, bits2); + H(qs[firstDiff]); - for (i in 0 .. Length(qs) - 1) { - if (bits1[i] == bits2[i]) { - if (bits1[i] == 1) { + for (i in 0 .. Length(qs) - 1) { + if (bits1[i] == bits2[i]) { + if (bits1[i] == 1) { + X(qs[i]); + } + } else { + if (i > firstDiff) { + CNOT(qs[firstDiff], qs[i]); + if (bits1[i] != bits1[firstDiff]) { X(qs[i]); } - } else { - if (i > firstDiff) { - CNOT(qs[firstDiff], qs[i]); - if (bits1[i] != bits1[firstDiff]) { - X(qs[i]); - } - } } } } - - adjoint invert; } - operation TestParityOnState (statePrep : (Qubit[] => Unit : Adjoint), parity : Int, stateStr : String) : Unit { + operation TestParityOnState (statePrep : (Qubit[] => Unit is Adj), parity : Int, stateStr : String) : Unit { using (register = Qubit[3]) { // prepare basis state to test on @@ -99,7 +94,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { let res = MeasureParity(register); // check that the returned parity is correct - AssertBoolEqual(res == Zero, parity == 0, $"Failed on {stateStr}."); + EqualityFactB(res == Zero, parity == 0, $"Failed on {stateStr}."); // check that the state has not been modified Adjoint statePrep(register); @@ -138,9 +133,9 @@ namespace Quantum.Kata.QEC_BitFlipCode { ////////////////////////////////////////////////////////////////////////// operation AssertEqualOnZeroState ( - statePrep : (Qubit[] => Unit : Adjoint), + statePrep : (Qubit[] => Unit is Adj), testImpl : (Qubit[] => Unit), - refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + refImpl : (Qubit[] => Unit is Adj)) : Unit { using (qs = Qubit[3]) { // prepare state statePrep(qs); @@ -158,19 +153,15 @@ namespace Quantum.Kata.QEC_BitFlipCode { } - operation StatePrep_Rotate (qs : Qubit[], alpha : Double) : Unit { - - body (...) { - Ry(2.0 * alpha, qs[0]); - } - - adjoint invert; + operation StatePrep_Rotate (qs : Qubit[], alpha : Double) : Unit + is Adj { + Ry(2.0 * alpha, qs[0]); } operation T02_Encode_Test () : Unit { for (i in 0 .. 36) { - let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; AssertEqualOnZeroState(StatePrep_Rotate(_, alpha), Encode, Encode_Reference); } } @@ -180,31 +171,28 @@ namespace Quantum.Kata.QEC_BitFlipCode { // Task 03 ////////////////////////////////////////////////////////////////////////// - operation StatePrep_WithError (qs : Qubit[], alpha : Double, hasError : Bool) : Unit { + operation StatePrep_WithError (qs : Qubit[], alpha : Double, hasError : Bool) : Unit + is Adj { - body (...) { - StatePrep_Rotate(qs, alpha); - Encode_Reference(qs); + StatePrep_Rotate(qs, alpha); + Encode_Reference(qs); - if (hasError) { - X(qs[0]); - } + if (hasError) { + X(qs[0]); } - - adjoint invert; } operation T03_DetectErrorOnLeftQubit_Test () : Unit { using (register = Qubit[3]) { for (i in 0 .. 36) { - let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; StatePrep_WithError(register, alpha, false); - AssertResultEqual(DetectErrorOnLeftQubit(register), Zero, "Failed on a state without X error."); + EqualityFactR(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."); + EqualityFactR(DetectErrorOnLeftQubit(register), One, "Failed on a state with X error."); Adjoint StatePrep_WithError(register, alpha, true); AssertAllZero(register); } @@ -217,7 +205,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { ////////////////////////////////////////////////////////////////////////// operation BindErrorCorrectionRoundImpl ( - encoder : (Qubit[] => Unit : Adjoint), + encoder : (Qubit[] => Unit is Adj), error : Pauli[], logicalOp : (Qubit[] => Unit), correction : (Qubit[] => Unit), @@ -246,7 +234,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { function BindErrorCorrectionRound ( - encoder : (Qubit[] => Unit : Adjoint), + encoder : (Qubit[] => Unit is Adj), error : Pauli[], logicalOp : (Qubit[] => Unit), correction : (Qubit[] => Unit)) : (Qubit[] => Unit) { @@ -269,7 +257,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { let errors = PauliErrors(); for (idxError in 0 .. 1) { - AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); + AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), NoOp); } } @@ -295,7 +283,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { statePrep(Head(register)); Encode_Reference(register); ApplyPauli(errors[idxError], register); - AssertIntEqual(DetectErrorOnAnyQubit(register), idxError, $"Failed on state with {errorStr}."); + EqualityFactI(DetectErrorOnAnyQubit(register), idxError, $"Failed on state with {errorStr}."); ApplyPauli(errors[idxError], register); Adjoint Encode_Reference(register); Adjoint statePrep(Head(register)); @@ -316,7 +304,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { for (idxError in 0 .. Length(errors) - 1) { Message($"Task 06: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), NoOp, 1); + AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), NoOp); } } @@ -332,7 +320,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { for (idxError in 0 .. Length(errors) - 1) { Message($"Task 07: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyPauli([PauliX], _), 1); + AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), ApplyPauli([PauliX], _)); } } @@ -348,7 +336,7 @@ namespace Quantum.Kata.QEC_BitFlipCode { for (idxError in 0 .. Length(errors) - 1) { Message($"Task 08: Testing on {errors[idxError]}..."); - AssertOperationsEqualReferenced(partialBind(errors[idxError]), ApplyToEachA(Z, _), 1); + AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), ApplyToEachA(Z, _)); } } diff --git a/SimonsAlgorithm/ReferenceImplementation.qs b/SimonsAlgorithm/ReferenceImplementation.qs index 120af5d7b42..8bcc288f815 100644 --- a/SimonsAlgorithm/ReferenceImplementation.qs +++ b/SimonsAlgorithm/ReferenceImplementation.qs @@ -10,7 +10,7 @@ namespace Quantum.Kata.SimonsAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -19,69 +19,57 @@ namespace Quantum.Kata.SimonsAlgorithm { ////////////////////////////////////////////////////////////////// // Task 1.1. f(x) = x₀ ⊕ ... ⊕ xₙ₋₁ (parity of the number of bits set to 1) - operation Oracle_CountBits_Reference (x : Qubit[], y : Qubit) : Unit { + operation Oracle_CountBits_Reference (x : Qubit[], y : Qubit) : Unit + is Adj { - body (...) { - let N = Length(x); + let N = Length(x); - for (i in 0 .. N - 1) { - CNOT(x[i], y); - } + for (i in 0 .. N - 1) { + CNOT(x[i], y); } - - adjoint invert; } // Task 1.2. Bitwise right shift - operation Oracle_BitwiseRightShift_Reference (x : Qubit[], y : Qubit[]) : Unit { + operation Oracle_BitwiseRightShift_Reference (x : Qubit[], y : Qubit[]) : Unit + is Adj { - body (...) { - let N = Length(x); + 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 invert; } // Task 1.3. Linear operator - operation Oracle_OperatorOutput_Reference (x : Qubit[], y : Qubit, A : Int[]) : Unit { + operation Oracle_OperatorOutput_Reference (x : Qubit[], y : Qubit, A : Int[]) : Unit + is Adj { - body (...) { - let N = Length(x); + let N = Length(x); - for (i in 0 .. N - 1) { - if (A[i] == 1) { - CNOT(x[i], y); - } + for (i in 0 .. N - 1) { + if (A[i] == 1) { + CNOT(x[i], y); } } - - adjoint invert; } // Task 1.4. Multidimensional linear operator - operation Oracle_MultidimensionalOperatorOutput_Reference (x : Qubit[], y : Qubit[], A : Int[][]) : Unit { + operation Oracle_MultidimensionalOperatorOutput_Reference (x : Qubit[], y : Qubit[], A : Int[][]) : Unit + is Adj { - body (...) { - let N1 = Length(y); - let N2 = Length(x); + 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) { - CNOT(x[j], y[i]); - } + for (i in 0 .. N1 - 1) { + for (j in 0 .. N2 - 1) { + if ((A[i])[j] == 1) { + CNOT(x[j], y[i]); } } } - - adjoint invert; } @@ -90,21 +78,15 @@ namespace Quantum.Kata.SimonsAlgorithm { ////////////////////////////////////////////////////////////////// // Task 2.1. State preparation for Simon's algorithm - operation SA_StatePrep_Reference (query : Qubit[]) : Unit { - - body (...) { - ApplyToEachA(H, query); - } - - adjoint invert; + operation SA_StatePrep_Reference (query : Qubit[]) : Unit + is Adj { + ApplyToEachA(H, query); } // Task 2.2. Quantum part of Simon's algorithm operation Simon_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit[]) => Unit)) : Int[] { - - mutable j = new Int[N]; - + // allocate input and answer registers with N qubits each using ((x, y) = (Qubit[N], Qubit[N])) { // prepare qubits in the right state @@ -118,18 +100,18 @@ namespace Quantum.Kata.SimonsAlgorithm { // measure all qubits of the input register; // the result of each measurement is converted to an Int + mutable j = new Int[N]; for (i in 0 .. N - 1) { if (M(x[i]) == One) { - set j[i] = 1; + set j w/= i <- 1; } } // before releasing the qubits make sure they are all in |0⟩ states ResetAll(x); ResetAll(y); + return j; } - - return j; } } diff --git a/SimonsAlgorithm/SimonsAlgorithm.csproj b/SimonsAlgorithm/SimonsAlgorithm.csproj index 612ea319b29..c6c92fac08f 100644 --- a/SimonsAlgorithm/SimonsAlgorithm.csproj +++ b/SimonsAlgorithm/SimonsAlgorithm.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/SimonsAlgorithm/Tasks.qs b/SimonsAlgorithm/Tasks.qs index 918b015be28..21fa3fb1727 100644 --- a/SimonsAlgorithm/Tasks.qs +++ b/SimonsAlgorithm/Tasks.qs @@ -3,7 +3,8 @@ namespace Quantum.Kata.SimonsAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -29,13 +30,9 @@ namespace Quantum.Kata.SimonsAlgorithm { // 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) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation Oracle_CountBits (x : Qubit[], y : Qubit) : Unit + is Adj { + // ... } @@ -45,13 +42,9 @@ namespace Quantum.Kata.SimonsAlgorithm { // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation Oracle_BitwiseRightShift (x : Qubit[], y : Qubit[]) : Unit + is Adj { + // ... } @@ -62,17 +55,14 @@ namespace Quantum.Kata.SimonsAlgorithm { // 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[]) : Unit { + operation Oracle_OperatorOutput (x : Qubit[], y : Qubit, A : Int[]) : Unit + is Adj { - 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"); + // 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. + EqualityFactI(Length(x), Length(A), "Arrays x and A should have the same length"); - // ... - } - - adjoint invert; + // ... } @@ -86,18 +76,15 @@ namespace Quantum.Kata.SimonsAlgorithm { // 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[][]) : Unit { + operation Oracle_MultidimensionalOperatorOutput (x : Qubit[], y : Qubit[], A : Int[][]) : Unit + is Adj { - 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"); + // The following lines enforce the constraints on the input arrays. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + EqualityFactI(Length(x), Length(A[0]), "Arrays x and A[0] should have the same length"); + EqualityFactI(Length(y), Length(A), "Arrays y and A should have the same length"); - // ... - } - - adjoint invert; + // ... } @@ -110,13 +97,9 @@ namespace Quantum.Kata.SimonsAlgorithm { // 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[]) : Unit { - - body (...) { - // ... - } - - adjoint invert; + operation SA_StatePrep (query : Qubit[]) : Unit + is Adj { + // ... } @@ -143,7 +126,7 @@ namespace Quantum.Kata.SimonsAlgorithm { 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. + // the variable has to be mutable to allow updating it. mutable b = new Int[N]; // ... diff --git a/SimonsAlgorithm/Tests.cs b/SimonsAlgorithm/Tests.cs index 127092aefd1..876e95e11da 100644 --- a/SimonsAlgorithm/Tests.cs +++ b/SimonsAlgorithm/Tests.cs @@ -46,20 +46,18 @@ public Instance(List> transformation, List kernel, int instance public BooleanVector Kernel => new BooleanVector(kernel); - public QArray> Transformation => new QArray>( + public IQArray> Transformation => new QArray>( transformation.Select( vector => new QArray(vector))); - public QArray> ExtendedTransformation + public IQArray> ExtendedTransformation { get { - var array = new QArray>( - transformation.Select( - vector => new QArray(vector))) - { - new QArray(transformation.Last()) - }; + var array = (IQArray>)new QArray>( + transformation.Select(vector => new QArray(vector)) + ); + array = QArray>.Add (array, new QArray>(new QArray(transformation.Last()))); return array; } } @@ -114,7 +112,7 @@ public void Test(Instance instance) var sim = new OracleCounterSimulator(); var len = instance.Kernel.Count; - var saver = new List>(); + var saver = new List>(); for (int i = 0; i < len * 4; ++i) { diff --git a/SimonsAlgorithm/Tests.qs b/SimonsAlgorithm/Tests.qs index d21b1c7ee10..3f614fe8882 100644 --- a/SimonsAlgorithm/Tests.qs +++ b/SimonsAlgorithm/Tests.qs @@ -9,44 +9,36 @@ namespace Quantum.Kata.SimonsAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; // ------------------------------------------------------ - operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { - - body (...) { - let N = Length(qs); - oracle(qs[0 .. N - 2], qs[N - 1]); - } - - adjoint invert; + operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj)) : Unit + is Adj { + let N = Length(qs); + oracle(qs[0 .. N - 2], qs[N - 1]); } - 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]); - } - - adjoint invert; + operation ApplyOracleWithOutputArrA (qs : Qubit[], oracle : ((Qubit[], Qubit[]) => Unit is Adj), outputSize : Int) : Unit + is Adj { + let N = Length(qs); + oracle(qs[0 .. (N - 1) - outputSize], qs[N - outputSize .. N - 1]); } // ------------------------------------------------------ operation AssertTwoOraclesAreEqual ( nQubits : Range, - oracle1 : ((Qubit[], Qubit) => Unit : Adjoint), - oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + oracle1 : ((Qubit[], Qubit) => Unit is Adj), + oracle2 : ((Qubit[], Qubit) => Unit is Adj)) : Unit { let sol = ApplyOracleA(_, oracle1); let refSol = ApplyOracleA(_, oracle2); for (i in nQubits) { - AssertOperationsEqualReferenced(sol, refSol, i + 1); + AssertOperationsEqualReferenced(i+1, sol, refSol); } } @@ -54,11 +46,11 @@ namespace Quantum.Kata.SimonsAlgorithm { operation AssertTwoOraclesWithOutputArrAreEqual ( inputSize : Int, outputSize : Int, - oracle1 : ((Qubit[], Qubit[]) => Unit : Adjoint), - oracle2 : ((Qubit[], Qubit[]) => Unit : Adjoint)) : Unit { + oracle1 : ((Qubit[], Qubit[]) => Unit is Adj), + oracle2 : ((Qubit[], Qubit[]) => Unit is Adj)) : Unit { let sol = ApplyOracleWithOutputArrA(_, oracle1, outputSize); let refSol = ApplyOracleWithOutputArrA(_, oracle2, outputSize); - AssertOperationsEqualReferenced(sol, refSol, inputSize + outputSize); + AssertOperationsEqualReferenced(inputSize + outputSize, sol, refSol); } @@ -77,7 +69,7 @@ namespace Quantum.Kata.SimonsAlgorithm { // ------------------------------------------------------ - operation AssertTwoOraclesWithIntArrAreEqual (A : Int[], oracle1 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint), oracle2 : ((Qubit[], Qubit, Int[]) => Unit : Adjoint)) : Unit { + operation AssertTwoOraclesWithIntArrAreEqual (A : Int[], oracle1 : ((Qubit[], Qubit, Int[]) => Unit is Adj), oracle2 : ((Qubit[], Qubit, Int[]) => Unit is Adj)) : Unit { AssertTwoOraclesAreEqual(Length(A) .. Length(A), oracle1(_, _, A), oracle2(_, _, A)); } @@ -109,8 +101,8 @@ namespace Quantum.Kata.SimonsAlgorithm { // ------------------------------------------------------ operation AssertTwoOraclesWithIntMatrixAreEqual ( A : Int[][], - oracle1 : ((Qubit[], Qubit[], Int[][]) => Unit : Adjoint), - oracle2 : ((Qubit[], Qubit[], Int[][]) => Unit : Adjoint)) : Unit { + oracle1 : ((Qubit[], Qubit[], Int[][]) => Unit is Adj), + oracle2 : ((Qubit[], Qubit[], Int[][]) => Unit is Adj)) : Unit { let inputSize = Length(A[0]); let outputSize = Length(A); AssertTwoOraclesWithOutputArrAreEqual(inputSize, outputSize, oracle1(_, _, A), oracle2(_, _, A)); @@ -119,11 +111,11 @@ namespace Quantum.Kata.SimonsAlgorithm { operation AssertTwoOraclesWithDifferentOutputsAreEqual ( inputSize : Int, - oracle1 : ((Qubit[], Qubit[]) => Unit : Adjoint), - oracle2 : ((Qubit[], Qubit) => Unit : Adjoint)) : Unit { + oracle1 : ((Qubit[], Qubit[]) => Unit is Adj), + oracle2 : ((Qubit[], Qubit) => Unit is Adj)) : Unit { let sol = ApplyOracleWithOutputArrA(_, oracle1, 1); let refSol = ApplyOracleA(_, oracle2); - AssertOperationsEqualReferenced(sol, refSol, inputSize + 1); + AssertOperationsEqualReferenced(inputSize + 1, sol, refSol); } diff --git a/SolveSATWithGrover/CounterSimulator.cs b/SolveSATWithGrover/CounterSimulator.cs index c76080ad811..17a112265ff 100644 --- a/SolveSATWithGrover/CounterSimulator.cs +++ b/SolveSATWithGrover/CounterSimulator.cs @@ -114,7 +114,7 @@ public override Qubit Apply() return base.Apply(); } - public override QArray Apply(long count) + public override IQArray Apply(long count) { _sim._qubitsAllocated += count; if (_sim._qubitsAllocated > _sim._maxQubitsAllocated) @@ -140,7 +140,7 @@ public override void Apply(Qubit q) base.Apply(q); } - public override void Apply(QArray qubits) + public override void Apply(IQArray qubits) { _sim._qubitsAllocated -= qubits.Length; base.Apply(qubits); diff --git a/SolveSATWithGrover/ReferenceImplementation.qs b/SolveSATWithGrover/ReferenceImplementation.qs index 1ad4797253a..5c307b8470b 100644 --- a/SolveSATWithGrover/ReferenceImplementation.qs +++ b/SolveSATWithGrover/ReferenceImplementation.qs @@ -10,10 +10,12 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -21,160 +23,123 @@ namespace Quantum.Kata.GroversAlgorithm { ////////////////////////////////////////////////////////////////// // Task 1.1. The AND oracle: f(x) = x₀ ∧ x₁ - operation Oracle_And_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - CCNOT(queryRegister[0], queryRegister[1], target); - } - - adjoint auto; + operation Oracle_And_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + CCNOT(queryRegister[0], queryRegister[1], target); } // AND oracle for an arbitrary number of qubits in query register - operation Oracle_And_Reference (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - Controlled X(queryRegister, target); - } - - adjoint auto; + operation Oracle_And_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + Controlled X(queryRegister, target); } // ------------------------------------------------------ // Task 1.2. The OR oracle: f(x) = x₀ ∨ x₁ - operation Oracle_Or_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // x₀ ∨ x₁ = ¬ (¬x₀ ∧ ¬x₁) - // First, flip target if both qubits are in |0⟩ state - X(queryRegister[0]); - X(queryRegister[1]); - CCNOT(queryRegister[0], queryRegister[1], target); - // Return query register to the starting state - X(queryRegister[0]); - X(queryRegister[1]); - // Then flip target again to get negation - X(target); - } - - adjoint auto; + operation Oracle_Or_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // x₀ ∨ x₁ = ¬ (¬x₀ ∧ ¬x₁) + // First, flip target if both qubits are in |0⟩ state + X(queryRegister[0]); + X(queryRegister[1]); + CCNOT(queryRegister[0], queryRegister[1], target); + // Return query register to the starting state + X(queryRegister[0]); + X(queryRegister[1]); + // Then flip target again to get negation + X(target); } // OR oracle for an arbitrary number of qubits in query register - operation Oracle_Or_Reference (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // x₀ ∨ x₁ = ¬ (¬x₀ ∧ ¬x₁) - // First, flip target if both qubits are in |0⟩ state - (ControlledOnInt(0, X))(queryRegister, target); - // Then flip target again to get negation - X(target); - } - - adjoint auto; + operation Oracle_Or_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // x₀ ∨ x₁ = ¬ (¬x₀ ∧ ¬x₁) + // First, flip target if both qubits are in |0⟩ state + (ControlledOnInt(0, X))(queryRegister, target); + // Then flip target again to get negation + X(target); } // ------------------------------------------------------ // Task 1.3. The XOR oracle: f(x) = x₀ ⊕ x₁ - operation Oracle_Xor_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - CNOT(queryRegister[0], target); - CNOT(queryRegister[1], target); - } - - adjoint auto; + operation Oracle_Xor_Reference_2 (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + CNOT(queryRegister[0], target); + CNOT(queryRegister[1], target); } // XOR oracle for an arbitrary number of qubits in query register - operation Oracle_Xor_Reference (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - ApplyToEachA(CNOT(_, target), queryRegister); - } - - adjoint auto; + operation Oracle_Xor_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + ApplyToEachA(CNOT(_, target), queryRegister); } // Alternative solution to task 1.3, based on representation as a 2-SAT problem - operation Oracle_Xor_2SAT (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // x₀ ⊕ x₁ = (x₀ ∨ x₁) ∧ (¬x₀ ∨ ¬x₁) - // Allocate 2 ancilla qubits to store results of clause evaluation - using ((a1, a2) = (Qubit(), Qubit())) { - // The first clause is exactly the Or oracle - Oracle_Or_Reference(queryRegister, a1); - // The second clause is the Or oracle, applied to negations of the variables - WithA(ApplyToEachA(X, _), Oracle_Or_Reference(_, a2), queryRegister); - // To calculate the final answer, apply the And oracle with the ancilla qubits as inputs - Oracle_And_Reference([a1, a2], target); - // Uncompute the values of the ancillas before releasing them (no measuring!) - Adjoint WithA(ApplyToEachA(X, _), Oracle_Or_Reference(_, a2), queryRegister); - Adjoint Oracle_Or_Reference(queryRegister, a1); - } + operation Oracle_Xor_2SAT (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // x₀ ⊕ x₁ = (x₀ ∨ x₁) ∧ (¬x₀ ∨ ¬x₁) + // Allocate 2 auxillary qubits to store results of clause evaluation + using ((a1, a2) = (Qubit(), Qubit())) { + // The first clause is exactly the Or oracle + Oracle_Or_Reference(queryRegister, a1); + // The second clause is the Or oracle, applied to negations of the variables + WithA(ApplyToEachA(X, _), Oracle_Or_Reference(_, a2), queryRegister); + // To calculate the final answer, apply the And oracle with the ancilla qubits as inputs + Oracle_And_Reference([a1, a2], target); + // Uncompute the values of the ancillas before releasing them (no measuring!) + Adjoint WithA(ApplyToEachA(X, _), Oracle_Or_Reference(_, a2), queryRegister); + Adjoint Oracle_Or_Reference(queryRegister, a1); } - - adjoint auto; } // ------------------------------------------------------ // Task 1.4. Alternating bits oracle: f(x) = (x₀ ⊕ x₁) ∧ (x₁ ⊕ x₂) ∧ ... ∧ (xₙ₋₂ ⊕ xₙ₋₁) - operation Oracle_AlternatingBits_Reference (queryRegister : Qubit[], target : Qubit) : Unit { + operation Oracle_AlternatingBits_Reference (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { - body (...) { - // Allocate N-1 qubits to store results of clauses evaluation - let N = Length(queryRegister); - using (anc = Qubit[N-1]) { - // Evaluate all XOR clauses (using XOR oracle) - for (i in 0..N-2) { - Oracle_Xor_Reference(queryRegister[i..i+1], anc[i]); - } + // Allocate N-1 qubits to store results of clauses evaluation + let N = Length(queryRegister); + using (anc = Qubit[N-1]) { + // Evaluate all XOR clauses (using XOR oracle) + for (i in 0..N-2) { + Oracle_Xor_Reference(queryRegister[i..i+1], anc[i]); + } - // Evaluate the overall formula as an AND oracle (can use reference depending on the implementation) - Controlled X(anc, target); + // Evaluate the overall formula as an AND oracle (can use reference depending on the implementation) + Controlled X(anc, target); - // Uncompute - for (i in 0..N-2) { - Adjoint Oracle_Xor_Reference(queryRegister[i..i+1], anc[i]); - } + // Uncompute + for (i in 0..N-2) { + Adjoint Oracle_Xor_Reference(queryRegister[i..i+1], anc[i]); } } - - adjoint auto; } // Answer-based solution for alternating bits oracle - operation FlipAlternatingPositionBits_Reference (register : Qubit[], firstIndex : Int) : Unit { + operation FlipAlternatingPositionBits_Reference (register : Qubit[], firstIndex : Int) : Unit + is Adj { - body (...) { - // iterate over elements in every second position, starting with firstIndex (indexes are 0-based) - for (i in firstIndex .. 2 .. Length(register) - 1) { - X(register[i]); - } + // iterate over elements in every second position, starting with firstIndex (indexes are 0-based) + for (i in firstIndex .. 2 .. Length(register) - 1) { + X(register[i]); } - - adjoint auto; } - operation Oracle_AlternatingBits_Answer (queryRegister : Qubit[], target : Qubit) : Unit { + operation Oracle_AlternatingBits_Answer (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { - body (...) { - // similar to task 1.2 from GroversAlgorithm kata: - // first mark the state with 1s in even positions (starting with the first qubit, index 0), - // then mark the state with 1s in odd positions - for (firstIndex in 0..1) { - FlipAlternatingPositionBits_Reference(queryRegister, firstIndex); - Controlled X(queryRegister, target); - Adjoint FlipAlternatingPositionBits_Reference(queryRegister, firstIndex); - } + // similar to task 1.2 from GroversAlgorithm kata: + // first mark the state with 1s in even positions (starting with the first qubit, index 0), + // then mark the state with 1s in odd positions + for (firstIndex in 0..1) { + FlipAlternatingPositionBits_Reference(queryRegister, firstIndex); + Controlled X(queryRegister, target); + Adjoint FlipAlternatingPositionBits_Reference(queryRegister, firstIndex); } - - adjoint auto; } // ------------------------------------------------------ @@ -191,9 +156,9 @@ namespace Quantum.Kata.GroversAlgorithm { let (index, isTrue) = clause[varIndex]; // Add the variable used in the clause to the list of variables which we'll need to call the OR oracle let qt = queryRegister[index]; - set qubits[varIndex] = queryRegister[index]; + set qubits w/= varIndex <- queryRegister[index]; // If the negation of the variable is present in the formula, mark the qubit as needing a flip - set flip[varIndex] = not isTrue; + set flip w/= varIndex <- not isTrue; } // Actually calculate the clause (flip the necessary qubits, call OR oracle, flip them back) @@ -207,14 +172,10 @@ namespace Quantum.Kata.GroversAlgorithm { // Task 1.5. 2-SAT problem oracle: f(x) = ∧ᵢ (yᵢ₀ ∨ yᵢ₁), yᵢₖ = either xᵢₖ or ¬xᵢₖ operation Oracle_2SAT_Reference (queryRegister : Qubit[], target : Qubit, - problem : (Int, Bool)[][]) : Unit { - - body (...) { + problem : (Int, Bool)[][]) : Unit + is Adj { // This is exactly the upcoming task 1.6, so using the general SAT oracle Oracle_SAT_Reference(queryRegister, target, problem); - } - - adjoint auto; } @@ -222,18 +183,15 @@ namespace Quantum.Kata.GroversAlgorithm { // Task 1.6. General SAT problem oracle: f(x) = ∧ᵢ (∨ₖ yᵢₖ), yᵢₖ = either xᵢₖ or ¬xᵢₖ operation Oracle_SAT_Reference (queryRegister : Qubit[], target : Qubit, - problem : (Int, Bool)[][]) : Unit { + problem : (Int, Bool)[][]) : Unit + is Adj { - body (...) { - // Similar to task 1.4. - // Allocate qubits to store results of clauses evaluation - using (anc = Qubit[Length(problem)]) { - // Compute clauses, evaluate the overall formula as an AND oracle (can use reference depending on the implementation) and uncompute - WithA(EvaluateOrClauses(queryRegister, _, problem), Controlled X(_, target), anc); - } + // Similar to task 1.4. + // Allocate qubits to store results of clauses evaluation + using (anc = Qubit[Length(problem)]) { + // Compute clauses, evaluate the overall formula as an AND oracle (can use reference depending on the implementation) and uncompute + WithA(EvaluateOrClauses(queryRegister, _, problem), Controlled X(_, target), anc); } - - adjoint auto; } @@ -241,30 +199,29 @@ namespace Quantum.Kata.GroversAlgorithm { // Part II. Using Grover's algorithm for problems with multiple solutions ////////////////////////////////////////////////////////////////// - operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint), register : Qubit[]) : Unit { - body (...) { - using (target = Qubit()) { - // Put the target into the |-⟩ state - X(target); - H(target); + operation OracleConverterImpl_Reference (markingOracle : ((Qubit[], Qubit) => Unit is Adj), register : Qubit[]) : Unit + is Adj { + + using (target = Qubit()) { + // 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); + // 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); - } + // Put the target back into |0⟩ so we can return it + H(target); + X(target); } - adjoint invert; } - function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => Unit : Adjoint)) : (Qubit[] => Unit : Adjoint) { + function OracleConverter_Reference (markingOracle : ((Qubit[], Qubit) => Unit is Adj)) : (Qubit[] => Unit is Adj) { return OracleConverterImpl_Reference(markingOracle, _); } - operation GroversAlgorithm_Loop (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit : Adjoint), iterations : Int) : Unit { + operation GroversAlgorithm_Loop (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj), iterations : Int) : Unit { let phaseOracle = OracleConverter_Reference(oracle); ApplyToEach(H, register); @@ -280,7 +237,7 @@ namespace Quantum.Kata.GroversAlgorithm { // Task 2.2. Universal implementation of Grover's algorithm - operation GroversAlgorithm_Reference (N : Int, oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Bool[] { + operation GroversAlgorithm_Reference (N : Int, oracle : ((Qubit[], Qubit) => Unit is Adj)) : Bool[] { // In this task you don't know the optimal number of iterations upfront, // so it makes sense to try different numbers of iterations. // This way, even if you don't hit the "correct" number of iterations on one of your tries, @@ -305,7 +262,7 @@ namespace Quantum.Kata.GroversAlgorithm { ResetAll(register); } until (correct or iter > 100) // the fail-safe to avoid going into an infinite loop fixup { - set iter = iter * 2; + set iter *= 2; } if (not correct) { fail "Failed to find an answer"; diff --git a/SolveSATWithGrover/SolveSATWithGrover.csproj b/SolveSATWithGrover/SolveSATWithGrover.csproj index 97e147cbbe3..891fde3db28 100644 --- a/SolveSATWithGrover/SolveSATWithGrover.csproj +++ b/SolveSATWithGrover/SolveSATWithGrover.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/SolveSATWithGrover/Tasks.qs b/SolveSATWithGrover/Tasks.qs index c88c5e6a7da..61ed8bf2215 100644 --- a/SolveSATWithGrover/Tasks.qs +++ b/SolveSATWithGrover/Tasks.qs @@ -3,10 +3,11 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -47,13 +48,9 @@ namespace Quantum.Kata.GroversAlgorithm { // Leave the query register in the same state it started in. // Stretch goal: Can you implement the oracle so that it would work // for queryRegister containing an arbitrary number of qubits? - operation Oracle_And (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // ... - } - - adjoint auto; + operation Oracle_And (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // ... } @@ -67,13 +64,9 @@ namespace Quantum.Kata.GroversAlgorithm { // Leave the query register in the same state it started in. // Stretch goal: Can you implement the oracle so that it would work // for queryRegister containing an arbitrary number of qubits? - operation Oracle_Or (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // ... - } - - adjoint auto; + operation Oracle_Or (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // ... } @@ -87,13 +80,9 @@ namespace Quantum.Kata.GroversAlgorithm { // Leave the query register in the same state it started in. // Stretch goal: Can you implement the oracle so that it would work // for queryRegister containing an arbitrary number of qubits? - operation Oracle_Xor (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // ... - } - - adjoint auto; + operation Oracle_Xor (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // ... } @@ -108,13 +97,9 @@ namespace Quantum.Kata.GroversAlgorithm { // |10101...⟩ and |01010...⟩ // It is possible (and quite straightforward) to implement this oracle based on this observation; // however, for the purposes of learning to write oracles to solve SAT problems we recommend using the representation above. - operation Oracle_AlternatingBits (queryRegister : Qubit[], target : Qubit) : Unit { - - body (...) { - // ... - } - - adjoint auto; + operation Oracle_AlternatingBits (queryRegister : Qubit[], target : Qubit) : Unit + is Adj { + // ... } @@ -149,13 +134,9 @@ namespace Quantum.Kata.GroversAlgorithm { // Leave the query register in the same state it started in. operation Oracle_2SAT (queryRegister : Qubit[], target : Qubit, - problem : (Int, Bool)[][]) : Unit { - - body (...) { + problem : (Int, Bool)[][]) : Unit + is Adj { // ... - } - - adjoint auto; } @@ -183,13 +164,9 @@ namespace Quantum.Kata.GroversAlgorithm { // Leave the query register in the same state it started in. operation Oracle_SAT (queryRegister : Qubit[], target : Qubit, - problem : (Int, Bool)[][]) : Unit { - - body (...) { - // ... - } - - adjoint auto; + problem : (Int, Bool)[][]) : Unit + is Adj { + // ... } @@ -227,7 +204,7 @@ namespace Quantum.Kata.GroversAlgorithm { // in a way that would be robust to accidental failures, but you knew the optimal number of iterations // (the number that minimizes the probability of such failure). // In this task you also need to make your implementation robust to not knowing the optimal number of iterations. - operation GroversAlgorithm (N : Int, oracle : ((Qubit[], Qubit) => Unit : Adjoint)) : Bool[] { + operation GroversAlgorithm (N : Int, oracle : ((Qubit[], Qubit) => Unit is Adj)) : Bool[] { // ... return new Bool[N]; } diff --git a/SolveSATWithGrover/Tests.qs b/SolveSATWithGrover/Tests.qs index 6e2afe7df65..e14d6ffa024 100644 --- a/SolveSATWithGrover/Tests.qs +++ b/SolveSATWithGrover/Tests.qs @@ -9,30 +9,27 @@ namespace Quantum.Kata.GroversAlgorithm { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // ------------------------------------------------------ // helper wrapper to represent oracle operation on input and output registers as an operation on an array of qubits - operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => Unit : Adjoint), qs : Qubit[]) : Unit { - - body (...) { - op(Most(qs), Tail(qs)); - } - - adjoint invert; + operation QubitArrayWrapperOperation (op : ((Qubit[], Qubit) => Unit is Adj), qs : Qubit[]) : Unit + is Adj { + op(Most(qs), Tail(qs)); } // ------------------------------------------------------ // helper wrapper to test for operation equality on various register sizes - operation AssertRegisterOperationsEqual (testOp : (Qubit[] => Unit), refOp : (Qubit[] => Unit : Adjoint)) : Unit { + operation AssertRegisterOperationsEqual (testOp : (Qubit[] => Unit), refOp : (Qubit[] => Unit is Adj)) : Unit { for (n in 2 .. 10) { - AssertOperationsEqualReferenced(testOp, refOp, n); + AssertOperationsEqualReferenced(n, testOp, refOp); } } @@ -77,7 +74,7 @@ namespace Quantum.Kata.GroversAlgorithm { let testOp = QubitArrayWrapperOperation(Oracle_And, _); let refOp = QubitArrayWrapperOperation(Oracle_And_Reference, _); - AssertOperationsEqualReferenced(testOp, refOp, 3); + AssertOperationsEqualReferenced(3, testOp, refOp); } @@ -91,7 +88,7 @@ namespace Quantum.Kata.GroversAlgorithm { let testOp = QubitArrayWrapperOperation(Oracle_Or, _); let refOp = QubitArrayWrapperOperation(Oracle_Or_Reference, _); - AssertOperationsEqualReferenced(testOp, refOp, 3); + AssertOperationsEqualReferenced(3, testOp, refOp); } @@ -105,7 +102,7 @@ namespace Quantum.Kata.GroversAlgorithm { let testOp = QubitArrayWrapperOperation(Oracle_Xor, _); let refOp = QubitArrayWrapperOperation(Oracle_Xor_Reference, _); - AssertOperationsEqualReferenced(testOp, refOp, 3); + AssertOperationsEqualReferenced(3, testOp, refOp); } @@ -126,7 +123,7 @@ namespace Quantum.Kata.GroversAlgorithm { for (n in 2 .. 5) { AssertOracleImplementsFunction(n, Oracle_AlternatingBits, AlternatingBits); - AssertOperationsEqualReferenced(testOp, refOp, n + 1); + AssertOperationsEqualReferenced(n + 1, testOp, refOp); } } @@ -153,12 +150,14 @@ namespace Quantum.Kata.GroversAlgorithm { let nVar = RandomInt(5) + 3; let nClause = RandomInt(2 * nVar) + 1; mutable problem = new (Int, Bool)[][nClause]; + for (j in 0..nClause-1) { mutable nVarInClause = is2SAT ? 2 | (RandomInt(4) + 1); if (nVarInClause > nVar) { set nVarInClause = nVar; } - set problem[j] = new (Int, Bool)[nVarInClause]; + + mutable problemRow = new (Int, Bool)[nVarInClause]; mutable usedVariables = new Bool[nVar]; // Make sure variables in each clause are distinct for (k in 0..nVarInClause-1) { @@ -167,32 +166,30 @@ namespace Quantum.Kata.GroversAlgorithm { set nextInd = RandomInt(nVar); } until (not usedVariables[nextInd]) fixup {} - set problem[j][k] = (nextInd, RandomInt(2) > 0); - set usedVariables[nextInd] = true; + set problemRow w/= k <- (nextInd, RandomInt(2) > 0); + set usedVariables w/= nextInd <- true; } + set problem w/= j <- problemRow; } return (nVar, problem); } - operation Run2SATTests (oracle : ((Qubit[], Qubit, (Int, Bool)[][]) => Unit : Adjoint)) : Unit { + operation Run2SATTests (oracle : ((Qubit[], Qubit, (Int, Bool)[][]) => Unit is Adj)) : Unit { // Cross-tests: // OR oracle - AssertOperationsEqualReferenced( + AssertOperationsEqualReferenced(3, QubitArrayWrapperOperation(oracle(_, _, [[(0, true), (1, true)]]), _), - QubitArrayWrapperOperation(Oracle_Or_Reference, _), - 3); + QubitArrayWrapperOperation(Oracle_Or_Reference, _)); // XOR oracle - AssertOperationsEqualReferenced( + AssertOperationsEqualReferenced(3, QubitArrayWrapperOperation(oracle(_, _, [[(0, true), (1, true)], [(1, false), (0, false)]]), _), - QubitArrayWrapperOperation(Oracle_Xor_Reference, _), - 3); + QubitArrayWrapperOperation(Oracle_Xor_Reference, _)); // AlternatingBits oracle for 3 qubits - AssertOperationsEqualReferenced( + AssertOperationsEqualReferenced(4, QubitArrayWrapperOperation(oracle(_, _, [[(1, false), (2, false)], [(0, true), (1, true)], [(1, false), (0, false)], [(2, true), (1, true)]]), _), - QubitArrayWrapperOperation(Oracle_AlternatingBits_Reference, _), - 4); + QubitArrayWrapperOperation(Oracle_AlternatingBits_Reference, _)); // Standalone tests for (i in 1..10) { @@ -201,10 +198,9 @@ namespace Quantum.Kata.GroversAlgorithm { AssertOracleImplementsFunction(nVar, oracle(_, _, problem), F_SAT(_, problem)); - AssertOperationsEqualReferenced( + AssertOperationsEqualReferenced(nVar + 1, QubitArrayWrapperOperation(oracle(_, _, problem), _), - QubitArrayWrapperOperation(Oracle_SAT_Reference(_, _, problem), _), - nVar + 1 + QubitArrayWrapperOperation(Oracle_SAT_Reference(_, _, problem), _) ); } } @@ -226,10 +222,9 @@ namespace Quantum.Kata.GroversAlgorithm { AssertOracleImplementsFunction(nVar, Oracle_SAT(_, _, problem), F_SAT(_, problem)); - AssertOperationsEqualReferenced( + AssertOperationsEqualReferenced(nVar + 1, QubitArrayWrapperOperation(Oracle_SAT(_, _, problem), _), - QubitArrayWrapperOperation(Oracle_SAT_Reference(_, _, problem), _), - nVar + 1 + QubitArrayWrapperOperation(Oracle_SAT_Reference(_, _, problem), _) ); } } diff --git a/SuperdenseCoding/ReferenceImplementation.qs b/SuperdenseCoding/ReferenceImplementation.qs index a6a73579c6d..2c0f1b091b8 100644 --- a/SuperdenseCoding/ReferenceImplementation.qs +++ b/SuperdenseCoding/ReferenceImplementation.qs @@ -10,31 +10,28 @@ namespace Quantum.Kata.SuperdenseCoding { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; // Task 1. Entangled pair - operation CreateEntangledPair_Reference (qs : Qubit[]) : Unit { + operation CreateEntangledPair_Reference (qs : Qubit[]) : Unit + is Adj { - 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]); + // 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⟩ + // 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 invert; + // 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 |Φ⁺⟩ } @@ -62,9 +59,6 @@ namespace Quantum.Kata.SuperdenseCoding { // Task 3. Decode the message (Bob's task) 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 @@ -83,16 +77,12 @@ namespace Quantum.Kata.SuperdenseCoding { // |Ψ⁻⟩ = (|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; + return [M(qAlice) == One, M(qBob) == One]; } // Task 4. Superdense coding protocol end-to-end 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]) { @@ -111,13 +101,12 @@ namespace Quantum.Kata.SuperdenseCoding { // 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]); + let decoded_bits = DecodeMessageFromQubits_Reference(qs[1], qs[0]); - // Make sure that we return qubits back in 0 state. + // Make sure that we return qubits back in 0 state before returning the decoded bits. ResetAll(qs); + return decoded_bits; } - - return decoded_bits; } } diff --git a/SuperdenseCoding/SuperdenseCoding.csproj b/SuperdenseCoding/SuperdenseCoding.csproj index 0339aca9d90..962d3f489f6 100644 --- a/SuperdenseCoding/SuperdenseCoding.csproj +++ b/SuperdenseCoding/SuperdenseCoding.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/SuperdenseCoding/Tasks.qs b/SuperdenseCoding/Tasks.qs index 3da609e4a22..e21cd1d8f25 100644 --- a/SuperdenseCoding/Tasks.qs +++ b/SuperdenseCoding/Tasks.qs @@ -3,7 +3,8 @@ namespace Quantum.Kata.SuperdenseCoding { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -42,7 +43,7 @@ namespace Quantum.Kata.SuperdenseCoding { 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."); + EqualityFactI(Length(qs), 2, "The array should have exactly 2 qubits."); // ... } @@ -76,7 +77,7 @@ namespace Quantum.Kata.SuperdenseCoding { // The state of the qubits in the end of the operation doesn't matter. 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. + // the variable has to be mutable to allow updating it. mutable decoded_bits = new Bool[2]; // ... diff --git a/SuperdenseCoding/Tests.qs b/SuperdenseCoding/Tests.qs index 66d144ecb75..e65560fe5d1 100644 --- a/SuperdenseCoding/Tests.qs +++ b/SuperdenseCoding/Tests.qs @@ -9,13 +9,13 @@ namespace Quantum.Kata.SuperdenseCoding { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; // ------------------------------------------------------ - operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit is Adj)) : Unit { using (qs = Qubit[N]) { // apply operation that needs to be tested taskImpl(qs); @@ -39,18 +39,16 @@ namespace Quantum.Kata.SuperdenseCoding { // Helper operation that runs superdense coding protocol using two building blocks // specified as first two parameters. 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]); + let result = decodeOp(qs[1], qs[0]); // Make sure that we return qubits back in 0 state. ResetAll(qs); + return result; } - - return result; } @@ -58,18 +56,16 @@ namespace Quantum.Kata.SuperdenseCoding { // 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[])) : 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; + let data = [1 == n / 2, 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}"); + AllEqualityFactB(result, data, $"The message {data} was transfered incorrectly as {result}"); } } } diff --git a/Superposition/ReferenceImplementation.qs b/Superposition/ReferenceImplementation.qs index c19f217e9d2..78878c891df 100644 --- a/Superposition/ReferenceImplementation.qs +++ b/Superposition/ReferenceImplementation.qs @@ -10,36 +10,31 @@ namespace Quantum.Kata.Superposition { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.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[]) : Unit { - - body (...) { - H(qs[0]); - } - - adjoint invert; + operation PlusState_Reference (qs : Qubit[]) : Unit + is Adj { + H(qs[0]); } // 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[]) : Unit { - - body (...) { - X(qs[0]); - H(qs[0]); - } - - adjoint invert; + operation MinusState_Reference (qs : Qubit[]) : Unit + is Adj { + X(qs[0]); + H(qs[0]); } @@ -48,71 +43,58 @@ namespace Quantum.Kata.Superposition { // 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) : Unit { - - body (...) { - // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive - Ry(2.0 * alpha, qs[0]); - } - - adjoint invert; + operation UnequalSuperposition_Reference (qs : Qubit[], alpha : Double) : Unit + is Adj { + + // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive + Ry(2.0 * alpha, qs[0]); } // Task 4. Superposition of all basis vectors on two qubits - 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 invert; + operation AllBasisVectors_TwoQubits_Reference (qs : Qubit[]) : Unit + is Adj { + + // 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]); } // Task 5. Superposition of basis vectors with phases - operation AllBasisVectorsWithPhases_TwoQubits_Reference (qs : Qubit[]) : Unit { + operation AllBasisVectorsWithPhases_TwoQubits_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - // Question: - // Is this state separable? + // 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. + // 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⟩, - // so the transformations that we need are: + // 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 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 invert; + // Qubit 1 is taken into |+⟩ and then z-rotated into |i⟩. + H(qs[1]); + S(qs[1]); } // 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[]) : Unit { - - body (...) { - H(qs[0]); - CNOT(qs[0], qs[1]); - } - - adjoint invert; + operation BellState_Reference (qs : Qubit[]) : Unit + is Adj { + H(qs[0]); + CNOT(qs[0], qs[1]); } @@ -125,40 +107,34 @@ 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) : Unit { + operation AllBellStates_Reference (qs : Qubit[], index : Int) : Unit + is Adj { - body (...) { - H(qs[0]); - CNOT(qs[0], qs[1]); + 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 - Z(qs[1]); - } - if (index / 2 == 1) { - X(qs[1]); - } + // now we have |00⟩ + |11⟩ - modify it based on index arg + if (index % 2 == 1) { + // negative phase + Z(qs[1]); + } + if (index / 2 == 1) { + X(qs[1]); } - - 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[]) : Unit { + operation GHZ_State_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - H(qs[0]); + H(qs[0]); - for (i in 1 .. Length(qs) - 1) { - CNOT(qs[0], qs[i]); - } + for (q in Rest(qs)) { + CNOT(qs[0], q); } - - adjoint invert; } @@ -166,15 +142,12 @@ namespace Quantum.Kata.Superposition { // 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[]) : Unit { + operation AllBasisVectorsSuperposition_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - for (i in 0 .. Length(qs) - 1) { - H(qs[i]); - } + for (q in qs) { + H(q); } - - adjoint invert; } @@ -182,20 +155,17 @@ namespace Quantum.Kata.Superposition { // Task 10. |00⟩ + |01⟩ + |10⟩ state // Input: 2 qubits in |00⟩ state. // Goal: create the state (|00⟩ + |01⟩ + |10⟩) / sqrt(3) on these qubits. - operation ThreeStates_TwoQubits_Reference (qs : Qubit[]) : Unit { + operation ThreeStates_TwoQubits_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - // Follow Niel's answer at https://quantumcomputing.stackexchange.com/a/2313/ + // Follow Niel's answer at https://quantumcomputing.stackexchange.com/a/2313/ - // Rotate first qubit to (sqrt(2) |0⟩ + |1⟩) / sqrt(3) (task 1.4 from BasicGates kata) - let theta = ArcSin(1.0 / Sqrt(3.0)); - Ry(2.0 * theta, qs[0]); + // Rotate first qubit to (sqrt(2) |0⟩ + |1⟩) / sqrt(3) (task 1.4 from BasicGates kata) + let theta = ArcSin(1.0 / Sqrt(3.0)); + Ry(2.0 * theta, qs[0]); - // Split the state sqrt(2) |0⟩ ⊗ |0⟩ into |00⟩ + |01⟩ - (ControlledOnInt(0, H))([qs[0]], qs[1]); - } - - adjoint auto; + // Split the state sqrt(2) |0⟩ ⊗ |0⟩ into |00⟩ + |01⟩ + (ControlledOnInt(0, H))([qs[0]], qs[1]); } // Alternative solution, based on post-selection @@ -226,24 +196,21 @@ namespace Quantum.Kata.Superposition { // 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[]) : Unit { + operation ZeroAndBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[]) : Unit + is Adj { - 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"); + EqualityFactI(Length(bits), Length(qs), "Arrays should have the same length"); + EqualityFactB(bits[0], true, "First bit of the input bit string should be set to true"); - // Hadamard first qubit - H(qs[0]); + // 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) { - if (bits[i]) { - CNOT(qs[0], qs[i]); - } + // iterate through the bit string and CNOT to qubits corresponding to true bits + for (i in 1 .. Length(qs) - 1) { + if (bits[i]) { + CNOT(qs[0], qs[i]); } } - - adjoint invert; } @@ -270,35 +237,32 @@ namespace Quantum.Kata.Superposition { } - operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Unit { + operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Unit + is Adj { - body (...) { - // find the index of the first bit at which the bit strings are different - let firstDiff = FindFirstDiff_Reference(bits1, bits2); + // 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]); + // 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) { - if (bits1[i] == bits2[i]) { - // if two bits are the same apply X or nothing - if (bits1[i]) { + // iterate through the bit strings again setting the final state of qubits + 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]) { + X(qs[i]); + } + } else { + // if two bits are different, set their difference using CNOT + if (i > firstDiff) { + CNOT(qs[firstDiff], qs[i]); + if (bits1[i] != bits1[firstDiff]) { X(qs[i]); } - } else { - // if two bits are different, set their difference using CNOT - if (i > firstDiff) { - CNOT(qs[firstDiff], qs[i]); - if (bits1[i] != bits1[firstDiff]) { - X(qs[i]); - } - } } } } - - adjoint invert; } @@ -312,36 +276,33 @@ namespace Quantum.Kata.Superposition { // bit values false and true correspond to |0⟩ and |1⟩ states. // // Goal: create an equal superposition of the four basis states given by the bit strings. - operation FourBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[][]) : Unit { - body (...) { - let N = Length(qs); + operation FourBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[][]) : Unit + is Adj { + let N = Length(qs); - using (anc = Qubit[2]) { - // Put two ancillas into equal superposition of 2-qubit basis states - ApplyToEachA(H, anc); + using (anc = Qubit[2]) { + // Put two ancillas into equal superposition of 2-qubit basis states + ApplyToEachA(H, anc); - // Set up the right pattern on the main qubits with control on ancillas - for (i in 0 .. 3) { - for (j in 0 .. N - 1) { - if ((bits[i])[j]) { - (ControlledOnInt(i, X))(anc, qs[j]); - } + // Set up the right pattern on the main qubits with control on ancillas + for (i in 0 .. 3) { + for (j in 0 .. N - 1) { + if ((bits[i])[j]) { + (ControlledOnInt(i, X))(anc, qs[j]); } } + } - // Uncompute the ancillas, using patterns on main qubits as control - for (i in 0 .. 3) { - if (i % 2 == 1) { - (ControlledOnBitString(bits[i], X))(qs, anc[0]); - } - if (i / 2 == 1) { - (ControlledOnBitString(bits[i], X))(qs, anc[1]); - } + // Uncompute the ancillas, using patterns on main qubits as control + for (i in 0 .. 3) { + if (i % 2 == 1) { + (ControlledOnBitString(bits[i], X))(qs, anc[0]); + } + if (i / 2 == 1) { + (ControlledOnBitString(bits[i], X))(qs, anc[1]); } } } - - adjoint auto; } @@ -351,36 +312,33 @@ namespace Quantum.Kata.Superposition { // 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[]) : Unit { + operation WState_PowerOfTwo_Reference (qs : Qubit[]) : Unit + is Adj { - body (...) { - let N = Length(qs); + let N = Length(qs); - if (N == 1) { - // base of recursion: |1⟩ - X(qs[0]); - } else { - let K = N / 2; + 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]); + // create W state on the first K qubits + 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]); + // the next K qubits are in |0...0⟩ state + // allocate ancilla in |+⟩ state + using (anc = Qubit()) { + H(anc); - for (i in 0 .. K - 1) { - Controlled SWAP(anc, (qs[i], qs[i + K])); - } - for (i in K .. N - 1) { - CNOT(qs[i], anc[0]); - } + for (i in 0 .. K - 1) { + Controlled SWAP([anc], (qs[i], qs[i + K])); + } + for (i in K .. N - 1) { + CNOT(qs[i], anc); } } } - - adjoint invert; } @@ -392,31 +350,26 @@ namespace Quantum.Kata.Superposition { // 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[]) : Unit { + operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit + is Adj + Ctl { - body (...) { - let N = Length(qs); + let N = Length(qs); - if (N == 1) { - // base case of recursion: |1⟩ - X(qs[0]); - } 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]); + if (N == 1) { + // base case of recursion: |1⟩ + X(qs[0]); + } 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(IntAsDouble(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]); - X(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]); + X(qs[0]); } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; } @@ -432,21 +385,18 @@ namespace Quantum.Kata.Superposition { // Given a qubit in |0⟩ state and a denominator N, // transform the qubit to state sqrt((N-1) / N) |0⟩ + sqrt(1/N) |1⟩. - operation FractionSuperposition(denominator : Int, q : Qubit) : Unit { - body (...) { - if (denominator == 1) { - X(q); - } else { - // represent the target state as cos(theta) * |0⟩ + sin(theta) * |1⟩, as in task 1.3 - let denom = ToDouble(denominator); - let num = denom - 1.0; - let theta = ArcCos(Sqrt(num / denom)); - Ry(2.0 * theta, q); - } + operation FractionSuperposition(denominator : Int, q : Qubit) : Unit + is Adj + Ctl { + + if (denominator == 1) { + X(q); + } else { + // represent the target state as cos(theta) * |0⟩ + sin(theta) * |1⟩, as in task 1.3 + let denom = IntAsDouble(denominator); + let num = denom - 1.0; + let theta = ArcCos(Sqrt(num / denom)); + Ry(2.0 * theta, q); } - adjoint invert; - controlled distribute; - controlled adjoint distribute; } @@ -463,7 +413,7 @@ namespace Quantum.Kata.Superposition { mutable P = 1; for (i in 1 .. 6) { if (P < N) { - set P = P * 2; + set P *= 2; } } diff --git a/Superposition/Superposition.csproj b/Superposition/Superposition.csproj index c523aae0daa..4b9a28d8652 100644 --- a/Superposition/Superposition.csproj +++ b/Superposition/Superposition.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/Superposition/Superposition.ipynb b/Superposition/Superposition.ipynb index 19c4405c7b4..ddd95b32b7a 100644 --- a/Superposition/Superposition.ipynb +++ b/Superposition/Superposition.ipynb @@ -33,7 +33,7 @@ "metadata": {}, "outputs": [], "source": [ - "%package Microsoft.Quantum.Katas::0.5.1904.1302" + "%package Microsoft.Quantum.Katas::0.6.1905.301" ] }, { @@ -43,13 +43,13 @@ "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", ">
\n", "> How to install the right IQ# version\n", - "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.5.1904.1302, the installation steps are as follows:\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.6.1905.301, the installation steps are as follows:\n", ">\n", "> 1. Stop the kernel.\n", "> 2. Uninstall the existing version of IQ#:\n", "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", "> 3. Install the matching version:\n", - "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.5.1904.1302\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.6.1905.301\n", "> 4. Reinstall the kernel:\n", "> dotnet iqsharp install\n", "> 5. Restart the Notebook.\n", @@ -125,7 +125,7 @@ "
\n", "
\n", " Need a hint? Click here\n", - " Experiment with rotation gates from Microsoft.Quantum.Primitive namespace.\n", + " Experiment with rotation gates from Microsoft.Quantum.Intrinsic namespace.\n", " Note that all rotation operators rotate the state by half of its angle argument.\n", "
" ] @@ -321,7 +321,7 @@ "
\n", "
\n", " Need a hint? Click here\n", - " If you need trigonometric functions, you can find them in Microsoft.Quantum.Extensions.Math namespace; you'll need to add
open Microsoft.Quantum.Extensions.Math;
to the code before the operation definition.\n", + " If you need trigonometric functions, you can find them in Microsoft.Quantum.Math namespace; you'll need to add
open Microsoft.Quantum.Math;
to the code before the operation definition.\n", "
" ] }, @@ -480,7 +480,7 @@ "
\n", "
\n", " Need a hint? Click here\n", - " You can modify the signature of the given operation to specify its controlled variant.\n", + " You can modify the signature of the given operation to specify its controlled specialization.\n", "
" ] }, diff --git a/Superposition/Tasks.qs b/Superposition/Tasks.qs index f014a70203a..9aaece8d8a6 100644 --- a/Superposition/Tasks.qs +++ b/Superposition/Tasks.qs @@ -3,11 +3,12 @@ namespace Quantum.Kata.Superposition { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + ////////////////////////////////////////////////////////////////// // Welcome! @@ -69,7 +70,7 @@ namespace Quantum.Kata.Superposition { 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."); + EqualityFactI(Length(qs), 2, "The array should have exactly 2 qubits."); // ... } @@ -81,7 +82,7 @@ namespace Quantum.Kata.Superposition { 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."); + EqualityFactI(Length(qs), 2, "The array should have exactly 2 qubits."); // Hint: Is this state separable? // ... @@ -149,8 +150,8 @@ namespace Quantum.Kata.Superposition { 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"); + EqualityFactI(Length(bits), Length(qs), "Arrays should have the same length"); + EqualityFactB(bits[0], true, "First bit of the input bit string should be set to true"); // ... } diff --git a/Superposition/Tests.qs b/Superposition/Tests.qs index 5e17d304cad..b280ab4f2fb 100644 --- a/Superposition/Tests.qs +++ b/Superposition/Tests.qs @@ -9,15 +9,15 @@ namespace Quantum.Kata.Superposition { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // ------------------------------------------------------ - operation AssertEqualOnZeroState (N : Int, testImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit : Adjoint)) : Unit { + operation AssertEqualOnZeroState (N : Int, testImpl : (Qubit[] => Unit), refImpl : (Qubit[] => Unit is Adj)) : Unit { using (qs = Qubit[N]) { // apply operation that needs to be tested testImpl(qs); @@ -53,7 +53,7 @@ namespace Quantum.Kata.Superposition { AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.75 * PI()), MinusState_Reference); for (i in 1 .. 36) { - let alpha = ((2.0 * PI()) * ToDouble(i)) / 36.0; + let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0; AssertEqualOnZeroState(1, UnequalSuperposition(_, alpha), UnequalSuperposition_Reference(_, alpha)); } } @@ -182,7 +182,7 @@ namespace Quantum.Kata.Superposition { repeat { mutable ok = true; for (i in 0 .. 3) { - set numbers[i] = RandomInt(1 <<< N); + set numbers w/= i <- RandomInt(1 <<< N); for (j in 0 .. i - 1) { if (numbers[i] == numbers[j]) { set ok = false; @@ -195,7 +195,7 @@ namespace Quantum.Kata.Superposition { // convert numbers to bit strings for (i in 0 .. 3) { - set bits[i] = BoolArrFromPositiveInt(numbers[i], N); + set bits w/= i <- IntAsBoolArray(numbers[i], N); } AssertEqualOnZeroState(N, FourBitstringSuperposition(_, bits), FourBitstringSuperposition_Reference(_, bits)); diff --git a/Teleportation/ReferenceImplementation.qs b/Teleportation/ReferenceImplementation.qs index d13c3c25456..2e2e9989153 100644 --- a/Teleportation/ReferenceImplementation.qs +++ b/Teleportation/ReferenceImplementation.qs @@ -9,10 +9,11 @@ namespace Quantum.Kata.Teleportation { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Preparation; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// @@ -20,14 +21,10 @@ namespace Quantum.Kata.Teleportation { ////////////////////////////////////////////////////////////////// // Task 1.1. Entangled pair - operation Entangle_Reference (qAlice : Qubit, qBob : Qubit) : Unit { - - body (...) { - H(qAlice); - CNOT(qAlice, qBob); - } - - adjoint invert; + operation Entangle_Reference (qAlice : Qubit, qBob : Qubit) : Unit + is Adj { + H(qAlice); + CNOT(qAlice, qBob); } @@ -63,17 +60,16 @@ namespace Quantum.Kata.Teleportation { // 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]); + let classicalBits = SendMessage_Reference(qAlice, qs[0]); Reset(qs[0]); + return classicalBits; } - return classicalBits; } @@ -151,25 +147,22 @@ namespace Quantum.Kata.Teleportation { ////////////////////////////////////////////////////////////////// // Task 4.1. Entangled trio - operation EntangleThreeQubits_Reference (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : Unit { + operation EntangleThreeQubits_Reference (qAlice : Qubit, qBob : Qubit, qCharlie : Qubit) : Unit + is Adj { - body (...) { - H(qAlice); - H(qBob); - X(qCharlie); + H(qAlice); + H(qBob); + X(qCharlie); - CCNOT(qAlice, qBob, qCharlie); + CCNOT(qAlice, qBob, qCharlie); - X(qAlice); - X(qBob); + X(qAlice); + X(qBob); - CCNOT(qAlice, qBob, qCharlie); + CCNOT(qAlice, qBob, qCharlie); - X(qAlice); - X(qBob); - } - - adjoint invert; + X(qAlice); + X(qBob); } diff --git a/Teleportation/Tasks.qs b/Teleportation/Tasks.qs index 6e4877c36c5..de1f9da021a 100644 --- a/Teleportation/Tasks.qs +++ b/Teleportation/Tasks.qs @@ -3,10 +3,10 @@ namespace Quantum.Kata.Teleportation { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; ////////////////////////////////////////////////////////////////// diff --git a/Teleportation/Teleportation.csproj b/Teleportation/Teleportation.csproj index 1c5a0953b78..2bfb50e0a92 100644 --- a/Teleportation/Teleportation.csproj +++ b/Teleportation/Teleportation.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/Teleportation/Tests.qs b/Teleportation/Tests.qs index c82b35e73ef..ada8a628890 100644 --- a/Teleportation/Tests.qs +++ b/Teleportation/Tests.qs @@ -9,9 +9,9 @@ namespace Quantum.Kata.Teleportation { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Testing; + open Microsoft.Quantum.Diagnostics; // ------------------------------------------------------ @@ -79,7 +79,7 @@ namespace Quantum.Kata.Teleportation { // which makes testing easier. operation TeleportTestHelper ( teleportOp : ((Qubit, Qubit, Qubit) => Unit), - setupPsiOp : (Qubit => Unit : Adjoint)) : Unit { + setupPsiOp : (Qubit => Unit is Adj)) : Unit { using (qs = Qubit[3]) { let qMessage = qs[0]; @@ -169,7 +169,7 @@ namespace Quantum.Kata.Teleportation { 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."); + EqualityFactB(receivedState, sentState, $"Sent and received states were not equal for {sentState} eigenstate in {basis} basis."); ResetAll([qAlice, qBob]); } } diff --git a/UnitaryPatterns/CounterSimulator.cs b/UnitaryPatterns/CounterSimulator.cs index 78e21089c1a..e939e72f174 100644 --- a/UnitaryPatterns/CounterSimulator.cs +++ b/UnitaryPatterns/CounterSimulator.cs @@ -111,7 +111,7 @@ public override Qubit Apply() return base.Apply(); } - public override QArray Apply(long count) + public override IQArray Apply(long count) { _sim._qubitsAllocated += count; if (_sim._qubitsAllocated > _sim._maxQubitsAllocated) @@ -137,7 +137,7 @@ public override void Apply(Qubit q) base.Apply(q); } - public override void Apply(QArray qubits) + public override void Apply(IQArray qubits) { _sim._qubitsAllocated -= qubits.Length; base.Apply(qubits); diff --git a/UnitaryPatterns/ReferenceImplementation.qs b/UnitaryPatterns/ReferenceImplementation.qs index da98a692d1b..c4feac833ae 100644 --- a/UnitaryPatterns/ReferenceImplementation.qs +++ b/UnitaryPatterns/ReferenceImplementation.qs @@ -10,10 +10,11 @@ namespace Quantum.Kata.UnitaryPatterns { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // Task 1. Main diagonal @@ -78,20 +79,17 @@ namespace Quantum.Kata.UnitaryPatterns { // Task 10. Increasing blocks - operation IncreasingBlocks_Reference (qs : Qubit[]) : Unit { - body (...) { - let N = Length(qs); - // for N = 1, we need an identity - if (N > 1) { - // do the bottom-right quarter - ApplyToEachCA(Controlled H([Tail(qs)], _), Most(qs)); - // do the top-left quarter by calling the same operation recursively - (ControlledOnInt(0, IncreasingBlocks_Reference))([Tail(qs)], Most(qs)); - } + operation IncreasingBlocks_Reference (qs : Qubit[]) : Unit + is Adj + Ctl { + + let N = Length(qs); + // for N = 1, we need an identity + if (N > 1) { + // do the bottom-right quarter + ApplyToEachCA(Controlled H([Tail(qs)], _), Most(qs)); + // do the top-left quarter by calling the same operation recursively + (ControlledOnInt(0, IncreasingBlocks_Reference))([Tail(qs)], Most(qs)); } - adjoint auto; - controlled auto; - controlled adjoint auto; } @@ -116,27 +114,25 @@ namespace Quantum.Kata.UnitaryPatterns { operation Decrement (qs : Qubit[]) : Unit { X(qs[0]); for (i in 1..Length(qs)-1) { - (Controlled X)(qs[0..i-1], qs[i]); + Controlled X(qs[0..i-1], qs[i]); } } // Helper operation: antidiagonal - operation Reflect (qs : Qubit[]) : Unit { - body (...) { - ApplyToEachC(X, qs); - } - controlled auto; + operation Reflect (qs : Qubit[]) : Unit + is Ctl { + ApplyToEachC(X, qs); } // Main operation for Task 13 operation TIE_Fighter_Reference (qs : Qubit[]) : Unit { let n = Length(qs); X(qs[n-1]); - (Controlled Reflect)([qs[n-1]], qs[0..(n-2)]); + Controlled Reflect([qs[n-1]], qs[0..(n-2)]); X(qs[n-1]); Decrement(qs[0..(n-2)]); H(qs[n-1]); - (Controlled Reflect)([qs[n-1]], qs[0..(n-2)]); + Controlled Reflect([qs[n-1]], qs[0..(n-2)]); } @@ -152,7 +148,7 @@ namespace Quantum.Kata.UnitaryPatterns { CNOT(qs[2], qs[0]); CCNOT(qs[0], qs[2], qs[1]); X(qs[2]); - (Controlled H)([qs[2]], qs[1]); + Controlled H([qs[2]], qs[1]); X(qs[2]); H(qs[0]); CNOT(qs[1], qs[2]); @@ -173,64 +169,63 @@ namespace Quantum.Kata.UnitaryPatterns { // Helper function for Embed_2x2_Operator: performs a Clifford to implement a base change // that maps basis states index1 to 111...10 and index2 to 111...11 (in big endian notation, i.e., LSB in qs[n-1]) - operation Embedding_Perm (index1 : Int, index2 : Int, qs : Qubit[]) : Unit { - body (...) { - let n = Length(qs); - let bits1 = BoolArrFromPositiveInt(index1, n); - let bits2 = BoolArrFromPositiveInt(index2, n); - // find the index of the first bit at which the bit strings are different - let diff = FirstDiff(bits1, bits2); - - // we care only about 2 inputs: basis state of bits1 and bits2 - - // make sure that the state corresponding to bits1 has qs[diff] set to |0⟩ - if (bits1[diff]) { - X(qs[diff]); - } + operation Embedding_Perm (index1 : Int, index2 : Int, qs : Qubit[]) : Unit + is Adj { + + let n = Length(qs); + let bits1 = BoolArrFromPositiveInt(index1, n); + let bits2 = BoolArrFromPositiveInt(index2, n); + // find the index of the first bit at which the bit strings are different + let diff = FirstDiff(bits1, bits2); + + // we care only about 2 inputs: basis state of bits1 and bits2 + + // make sure that the state corresponding to bits1 has qs[diff] set to |0⟩ + if (bits1[diff]) { + X(qs[diff]); + } - // iterate through the bit strings again, setting the final state of qubits - for (i in 0..n-1) { - if (bits1[i] == bits2[i]) { - // if two bits are the same, set both to 1 using X or nothing + // iterate through the bit strings again, setting the final state of qubits + for (i in 0..n-1) { + if (bits1[i] == bits2[i]) { + // if two bits are the same, set both to 1 using X or nothing + if (not bits1[i]) { + X(qs[i]); + } + } else { + // if two bits are different, set both to 1 using CNOT + if (i > diff) { if (not bits1[i]) { - X(qs[i]); + X(qs[diff]); + CNOT(qs[diff], qs[i]); + X(qs[diff]); } - } else { - // if two bits are different, set both to 1 using CNOT - if (i > diff) { - if (not bits1[i]) { - X(qs[diff]); - CNOT(qs[diff], qs[i]); - X(qs[diff]); - } - if (not bits2[i]) { - CNOT(qs[diff], qs[i]); - } + if (not bits2[i]) { + CNOT(qs[diff], qs[i]); } } } + } - // move the differing bit to the last qubit - if (diff < n-1) { - SWAP(qs[n-1], qs[diff]); - } + // move the differing bit to the last qubit + if (diff < n-1) { + SWAP(qs[n-1], qs[diff]); } - adjoint auto; } // Helper function: apply the 2x2 unitary operator at the sub-matrix given by indices for 2 rows/columns - operation Embed_2x2_Operator (U : (Qubit => Unit : Controlled), index1 : Int, index2 : Int, qs : Qubit[]) : Unit { + operation Embed_2x2_Operator (U : (Qubit => Unit is Ctl), index1 : Int, index2 : Int, qs : Qubit[]) : Unit { Embedding_Perm(index1, index2, qs); - (Controlled U)(Most(qs), Tail(qs)); - (Adjoint Embedding_Perm)(index1, index2, qs); + Controlled U(Most(qs), Tail(qs)); + Adjoint Embedding_Perm(index1, index2, qs); } // Putting everything together: the target pattern is produced by a sequence of controlled H gates. operation Hessenberg_Matrix_Reference (qs : Qubit[]) : Unit { let n = Length(qs); - for (i in 2^n-2..-1..0) { + for (i in 2^n-2..-1..0) { Embed_2x2_Operator(H, i, i+1, qs); } } diff --git a/UnitaryPatterns/Tasks.qs b/UnitaryPatterns/Tasks.qs index d4bed64033a..031148c2b4e 100644 --- a/UnitaryPatterns/Tasks.qs +++ b/UnitaryPatterns/Tasks.qs @@ -3,10 +3,11 @@ namespace Quantum.Kata.UnitaryPatterns { - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + ////////////////////////////////////////////////////////////////// // Welcome! @@ -288,7 +289,7 @@ namespace Quantum.Kata.UnitaryPatterns { // ..X..X.. // XX....XX // XX....XX - operation Creeper (qs : Qubit[]) : Unit { + operation Creeper (qs : Qubit[]) : Unit { // ... } diff --git a/UnitaryPatterns/Tests.qs b/UnitaryPatterns/Tests.qs index 309d50d5922..ab96432b960 100644 --- a/UnitaryPatterns/Tests.qs +++ b/UnitaryPatterns/Tests.qs @@ -9,12 +9,12 @@ namespace Quantum.Kata.UnitaryPatterns { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Convert; - open Microsoft.Quantum.Extensions.Math; - open Microsoft.Quantum.Extensions.Testing; - open Microsoft.Quantum.Extensions.Diagnostics; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; // ------------------------------------------------------ @@ -45,20 +45,16 @@ namespace Quantum.Kata.UnitaryPatterns { op(qs); // Make sure the solution didn't use any measurements - AssertIntEqual(GetOracleCallsCount(M), 0, "You are not allowed to use measurements in this task"); - AssertIntEqual(GetOracleCallsCount(Measure), 0, "You are not allowed to use measurements in this task"); + EqualityFactI(GetOracleCallsCount(M), 0, "You are not allowed to use measurements in this task"); + EqualityFactI(GetOracleCallsCount(Measure), 0, "You are not allowed to use measurements in this task"); // Test that the result matches the k-th column // DumpMachine($"C:/Tmp/dump{N}_{k}.txt"); for (j in 0 .. size - 1) { let nonZero = pattern(size, j, k); - if (nonZero) { - AssertProbInt(j, 0.5 + ε, LittleEndian(qs), 0.5); - } - else { - AssertProbInt(j, 0.0, LittleEndian(qs), ε); - } + let (expected, tol) = nonZero ? (0.5 + ε, 0.5) | (0.0, ε); + AssertProbInt(j, expected, LittleEndian(qs), tol); } ResetAll(qs); @@ -285,10 +281,7 @@ namespace Quantum.Kata.UnitaryPatterns { [ false, false, true, false, false, true, false, false], [ true, true, false, false, false, false, true, true], [ true, true, false, false, false, false, true, true] ]; - if (size != 8) { - return false; - } - return A[row][col]; + return size != 8 ? false | A[row][col]; } diff --git a/UnitaryPatterns/UnitaryPatterns.csproj b/UnitaryPatterns/UnitaryPatterns.csproj index d5d55a2bc51..50118958153 100644 --- a/UnitaryPatterns/UnitaryPatterns.csproj +++ b/UnitaryPatterns/UnitaryPatterns.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 x64 @@ -7,9 +7,9 @@ - - - + + + diff --git a/scripts/validate-notebooks.ps1 b/scripts/validate-notebooks.ps1 new file mode 100644 index 00000000000..b9eecbaf694 --- /dev/null +++ b/scripts/validate-notebooks.ps1 @@ -0,0 +1,51 @@ +# Script to validate Jupyter notebooks. + +# +# This function takes a folder with Katas. Copies the corresponding +# jupyter notebook into a "Check.ipynb" that replaces the %kata magic +# with a %check_kata magic that runs the kata not with the user-supplied code +# but with the Reference implementation. +# If the execution fails or has warnings, the execution of the notebook +# will fail. +function validate { + Param($folder) + pushd $folder + + # Name of the notebook that will be used for checking katas. + $CheckNotebook = 'Check.ipynb' + + # Make sure old versions are removed. + if (Test-Path $CheckNotebook) { + Remove-Item $CheckNotebook + } + + # Find the name of the kata's notebook. + $Notebook = (Get-ChildItem *.ipynb) + Write-Host "Checking notebook $Notebook." + + # convert %kata to %check_kata. run Jupyter nbconvert to execute the kata. + (Get-Content $Notebook -Raw) | ForEach-Object { $_.replace('%kata', '%check_kata') } | Set-Content $CheckNotebook -NoNewline + jupyter nbconvert $CheckNotebook --execute --ExecutePreprocessor.timeout=120 + + # if jupyter returns an error code, return that this notebook is invalid: + $invalid = ($LastExitCode -ne 0) + Write-Host "Result: " $LastExitCode + + popd + return $invalid +} + +# List of Katas to verify: +$errors = $false +$errors = (validate ..\Measurements) -or $errors +$errors = (validate ..\BasicGates) -or $errors +$errors = (validate ..\Superposition) -or $errors +$errors = (validate ..\DeutschJozsaAlgorithm) -or $errors + +$result = 0 +if ($errors) { + Write-Host "##vso[task.logissue type=error;]Validation errors for Jupyter notebooks." + $result = 1 +} + +exit($result) \ No newline at end of file diff --git a/utilities/Common/Common.csproj b/utilities/Common/Common.csproj index dd439e20814..518afeb32c1 100644 --- a/utilities/Common/Common.csproj +++ b/utilities/Common/Common.csproj @@ -7,7 +7,7 @@ - + diff --git a/utilities/Common/CounterSimulator.cs b/utilities/Common/CounterSimulator.cs index 611c585cd7b..3015c3a1863 100644 --- a/utilities/Common/CounterSimulator.cs +++ b/utilities/Common/CounterSimulator.cs @@ -119,7 +119,7 @@ public override Qubit Apply() return base.Apply(); } - public override QArray Apply(long count) + public override IQArray Apply(long count) { _sim._qubitsAllocated += count; if (_sim._qubitsAllocated > _sim._maxQubitsAllocated) @@ -148,7 +148,7 @@ public override void Apply(Qubit q) base.Apply(q); } - public override void Apply(QArray qubits) + public override void Apply(IQArray qubits) { _sim._qubitsAllocated -= qubits.Length; base.Apply(qubits); diff --git a/utilities/DumpUnitary/DumpUnitary.csproj b/utilities/DumpUnitary/DumpUnitary.csproj index e68681b0bbb..cf2469de0a0 100644 --- a/utilities/DumpUnitary/DumpUnitary.csproj +++ b/utilities/DumpUnitary/DumpUnitary.csproj @@ -1,4 +1,4 @@ - + Exe netcoreapp2.0 @@ -9,7 +9,7 @@ - - + + diff --git a/utilities/DumpUnitary/Operations.qs b/utilities/DumpUnitary/Operations.qs index d1e75d73eb5..35c8f26a8b5 100644 --- a/utilities/DumpUnitary/Operations.qs +++ b/utilities/DumpUnitary/Operations.qs @@ -3,9 +3,10 @@ namespace Quantum.DumpUnitary { - open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Extensions.Diagnostics; + open Microsoft.Quantum.Diagnostics; + operation DumpUnitaryToFiles (N : Int, unitary : (Qubit[] => Unit)) : Unit { let size = 1 <<< N; diff --git a/utilities/Microsoft.Quantum.Katas/CheckKataMagic.cs b/utilities/Microsoft.Quantum.Katas/CheckKataMagic.cs new file mode 100644 index 00000000000..e13f1f06130 --- /dev/null +++ b/utilities/Microsoft.Quantum.Katas/CheckKataMagic.cs @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Jupyter.Core; +using Microsoft.Quantum.IQSharp; +using Microsoft.Quantum.IQSharp.Common; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Katas +{ + public class CheckKataMagic : MagicSymbol + { + /// + /// IQ# Magic that checks that the reference implementation of a Kata's test runs successfully. + /// + public CheckKataMagic(IOperationResolver resolver, ICompilerService compiler, ILogger logger) + { + this.Name = $"%check_kata"; + this.Documentation = new Documentation() { Summary = "Checks the resference implementaiton of a single kata's test." }; + this.Kind = SymbolKind.Magic; + this.Execute = this.Run; + + this.Resolver = resolver; + this.Compiler = compiler; + this.Logger = logger; + } + + /// + /// The Resolver lets us find compiled Q# operations from the workspace + /// + protected IOperationResolver Resolver { get; } + + /// + /// The list of user-defined Q# code snippets from the notebook. + /// + protected ICompilerService Compiler { get; } + + protected ILogger Logger { get; } + + /// + /// What this Magic does when triggered. It will: + /// - find the Test to execute based on the provided name, + /// - semi-compile the code after to identify the name of the operation with the user's answer. + /// - call simulate to execute the test. + /// + public virtual ExecutionResult Run(string input, IChannel channel) + { + channel = channel.WithNewLines(); + + // Expect exactly two arguments, the name of the Kata and the user's answer (code). + var args = input?.Split(new char[] { ' ', '\n', '\t' }, 2); + if (args == null || args.Length != 2) + { + channel.Stderr("Invalid parameters. Usage: `%kata Test \"Q# operation\"`"); + return ExecuteStatus.Error.ToExecutionResult(); + } + + var name = args[0]; + var code = args[1]; + + var test = FindTest(name); + if (test == null) + { + channel.Stderr($"Invalid test name: {name}"); + return ExecuteStatus.Error.ToExecutionResult(); + } + + var userAnswer = Compile(code, channel); + if (userAnswer == null) { return ExecuteStatus.Error.ToExecutionResult(); } + + return Simulate(test, userAnswer, channel) + ? "Success!".ToExecutionResult() + : ExecuteStatus.Error.ToExecutionResult(); + } + + /// + /// Compiles the given code. Checks there is only one operation defined in the code, + /// and returns its corresponding OperationInfo + /// + public virtual string Compile(string code, IChannel channel) + { + try + { + var result = Compiler.IdentifyElements(code); + + // Gets the names of all the operations found for this snippet + var opsNames = + result + .Where(e => e.IsQsCallable) + .Select(e => e.ToFullName().WithoutNamespace(Microsoft.Quantum.IQSharp.Snippets.SNIPPETS_NAMESPACE)) + .OrderBy(o => o) + .ToArray(); + + if (opsNames.Length > 1) + { + channel.Stdout("Expecting only one Q# operation in code. Using the first one"); + } + + return opsNames.First(); + } + catch (CompilationErrorsException c) + { + foreach (var m in c.Errors) channel.Stderr(m); + return null; + } + catch (Exception e) + { + Logger?.LogWarning(e, "Unexpected error."); + channel.Stderr(e.Message); + return null; + } + } + + /// + /// Executes the given test by replacing the userAnswer with its reference implementation. + /// It is expected that the test will succeed with no warnings. + /// + public virtual bool Simulate(OperationInfo test, string userAnswer, IChannel channel) + { + // The skeleton answer used to compile the workspace + var skeletonAnswer = FindSkeletonAnswer(test, userAnswer); + if (skeletonAnswer == null) + { + channel.Stderr($"Invalid task name: {userAnswer}"); + return false; + } + + // The reference implementation + var referenceAnswer = FindReferenceImplementation(test, userAnswer); + if (referenceAnswer == null) + { + channel.Stderr($"Reference answer not found: {userAnswer}"); + return false; + } + + try + { + var qsim = CreateSimulator(); + var hasWarnings = false; + + qsim.DisableLogToConsole(); + qsim.Register(skeletonAnswer.RoslynType, referenceAnswer.RoslynType, typeof(ICallable)); + qsim.OnLog += (msg) => + { + hasWarnings = msg?.StartsWith("[WARNING]") ?? hasWarnings; + channel.Stdout(msg); + }; + + var value = test.RunAsync(qsim, null).Result; + + if (qsim is IDisposable dis) { dis.Dispose(); } + + return !hasWarnings; + } + catch (AggregateException agg) + { + foreach (var e in agg.InnerExceptions) { channel.Stderr(e.Message); } + channel.Stderr($"Try again!"); + return false; + } + catch (Exception e) + { + channel.Stderr(e.Message); + channel.Stderr($"Try again!"); + return false; + } + } + + /// + /// Creates the instance of the simulator to use to run the test + /// (for now always CounterSimulator from the same package). + /// + public virtual SimulatorBase CreateSimulator() => + new CounterSimulator(); + + /// + /// Returns the OperationInfo for the test to run. + /// + public virtual OperationInfo FindTest(string testName) => + Resolver.Resolve(testName); + + /// + /// Returns the original shell for the test's answer in the workspace for the given userAnswer. + /// It does this by finding another operation with the same name as the `userAnswer` but in the + /// test's namespace + /// + public virtual OperationInfo FindSkeletonAnswer(OperationInfo test, string userAnswer) => + Resolver.Resolve($"{test.Header.QualifiedName.Namespace.Value}.{userAnswer}"); + + /// + /// Returns the reference implementation for the test's answer in the workspace for the given userAnswer. + /// It does this by finding another operation with the same name as the `userAnswer` but in the + /// test's namespace and with _Reference added to the userAnswer's name. + /// + public virtual OperationInfo FindReferenceImplementation(OperationInfo test, string userAnswer) => + Resolver.Resolve($"{test.Header.QualifiedName.Namespace.Value}.{userAnswer}_Reference"); + } +} + diff --git a/utilities/Microsoft.Quantum.Katas/KataMagic.cs b/utilities/Microsoft.Quantum.Katas/KataMagic.cs index 788480f6e56..061c1ffa717 100644 --- a/utilities/Microsoft.Quantum.Katas/KataMagic.cs +++ b/utilities/Microsoft.Quantum.Katas/KataMagic.cs @@ -20,7 +20,7 @@ public class KataMagic : MagicSymbol public KataMagic(IOperationResolver resolver, ISnippets snippets, ILogger logger) { this.Name = $"%kata"; - this.Documentation = new Documentation() { Summary = "Executes a single kata.", Full = "## Executes a single kata.\n##Usage: \n%kata Test \"q# operation\"" }; + this.Documentation = new Documentation() { Summary = "Executes a single test.", Full = "## Executes a single test.\n##Usage: \n%kata Test \"q# operation\"" }; this.Kind = SymbolKind.Magic; this.Execute = this.Run; @@ -43,9 +43,9 @@ public KataMagic(IOperationResolver resolver, ISnippets snippets, ILogger /// What this Magic does when triggered. It will: - /// - find the Kata to execute based on the Kata name, + /// - find the Test to execute based on the given name, /// - compile the code after found after the name as the user's answer. - /// - run (simulate) the kata. + /// - run (simulate) the test and report its result. /// public virtual ExecutionResult Run(string input, IChannel channel) { @@ -62,8 +62,8 @@ public virtual ExecutionResult Run(string input, IChannel channel) var name = args[0]; var code = args[1]; - var kata = FindKata(name); - if (kata == null) + var test = FindTest(name); + if (test == null) { channel.Stderr($"Invalid test name: {name}"); return ExecuteStatus.Error.ToExecutionResult(); @@ -72,7 +72,7 @@ public virtual ExecutionResult Run(string input, IChannel channel) var userAnswer = Compile(code, channel); if (userAnswer == null) { return ExecuteStatus.Error.ToExecutionResult(); } - return Simulate(kata, userAnswer, channel) + return Simulate(test, userAnswer, channel) ? "Success!".ToExecutionResult() : ExecuteStatus.Error.ToExecutionResult(); } @@ -123,10 +123,10 @@ public virtual OperationInfo Compile(string code, IChannel channel) /// (by calling `FindRawAnswer`) and replace its implementation with the userAnswer /// in the simulator. /// - public virtual bool Simulate(OperationInfo kata, OperationInfo userAnswer, IChannel channel) + public virtual bool Simulate(OperationInfo test, OperationInfo userAnswer, IChannel channel) { - var rawAnswer = FindRawAnswer(kata, userAnswer); - if (rawAnswer == null) + var skeletonAnswer = FindSkeletonAnswer(test, userAnswer); + if (skeletonAnswer == null) { channel.Stderr($"Invalid task: {userAnswer.FullName}"); return false; @@ -137,10 +137,10 @@ public virtual bool Simulate(OperationInfo kata, OperationInfo userAnswer, IChan var qsim = CreateSimulator(); qsim.DisableLogToConsole(); - qsim.Register(rawAnswer.RoslynType, userAnswer.RoslynType, typeof(ICallable)); + qsim.Register(skeletonAnswer.RoslynType, userAnswer.RoslynType, typeof(ICallable)); qsim.OnLog += channel.Stdout; - var value = kata.RunAsync(qsim, null).Result; + var value = test.RunAsync(qsim, null).Result; if (qsim is IDisposable dis) { dis.Dispose(); } @@ -161,25 +161,25 @@ public virtual bool Simulate(OperationInfo kata, OperationInfo userAnswer, IChan } /// - /// Creates the instance of the simulator to use to run the Kata + /// Creates the instance of the simulator to use to run the Test /// (for now always CounterSimulator from the same package). /// public virtual SimulatorBase CreateSimulator() => new CounterSimulator(); /// - /// Returns the OperationInfo for the Kata to run. + /// Returns the OperationInfo with the Test to run based on the given name. /// - public virtual OperationInfo FindKata(string kataName) => - Resolver.Resolve(kataName); + public virtual OperationInfo FindTest(string testName) => + Resolver.Resolve(testName); /// - /// Returns the original shell for the Kata's answer in the workspace for the given userAnswer. + /// Returns the original shell for the test's answer in the workspace for the given userAnswer. /// It does this by finding another operation with the same name as the `userAnswer` but in the - /// Kata's namespace + /// test's namespace /// - public virtual OperationInfo FindRawAnswer(OperationInfo kata, OperationInfo userAnswer) => - Resolver.Resolve($"{kata.Header.QualifiedName.Namespace.Value}.{userAnswer.FullName}"); + public virtual OperationInfo FindSkeletonAnswer(OperationInfo test, OperationInfo userAnswer) => + Resolver.Resolve($"{test.Header.QualifiedName.Namespace.Value}.{userAnswer.FullName}"); } } diff --git a/utilities/Microsoft.Quantum.Katas/Microsoft.Quantum.Katas.csproj b/utilities/Microsoft.Quantum.Katas/Microsoft.Quantum.Katas.csproj index 9c4bc084d9d..5101a9fcd8e 100644 --- a/utilities/Microsoft.Quantum.Katas/Microsoft.Quantum.Katas.csproj +++ b/utilities/Microsoft.Quantum.Katas/Microsoft.Quantum.Katas.csproj @@ -18,7 +18,7 @@ - +