diff --git a/katas/content/distinguishing_states/Common.qs b/katas/content/distinguishing_states/Common.qs deleted file mode 100644 index 151919f5a9..0000000000 --- a/katas/content/distinguishing_states/Common.qs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Kata.Verification { - open Microsoft.Quantum.Convert; - open Microsoft.Quantum.Random; - - operation DistinguishStates_MultiQubit_Threshold (nQubit : Int, - nState : Int, - threshold : Double, - statePrep : ((Qubit, Int) => Unit), - testImpl : (Qubit => Bool)) : Bool { - let nTotal = 1000; - mutable nOk = 0; - - use qs = Qubit[nQubit]; - for i in 1 .. nTotal { - // get a random integer to define the state of the qubits - let state = DrawRandomInt(0, nState - 1); - - // do state prep: convert |0⟩ to outcome with return equal to state - statePrep(qs[0], state); - - // get the solution's answer and verify that it's a match - let ans = testImpl(qs[0]); - if ans == (state == 0) { - set nOk += 1; - } - - // we're not checking the state of the qubit after the operation - ResetAll(qs); - } - - if IntAsDouble(nOk) < threshold * IntAsDouble(nTotal) { - Message($"{nTotal - nOk} test runs out of {nTotal} returned incorrect state, which does not meet the required threshold of at least {threshold * 100.0}%."); - Message("Incorrect."); - return false; - } else { - Message("Correct!"); - return true; - } - } -} diff --git a/katas/content/distinguishing_states/index.md b/katas/content/distinguishing_states/index.md index d7dcabc1f3..62ae5a0a00 100644 --- a/katas/content/distinguishing_states/index.md +++ b/katas/content/distinguishing_states/index.md @@ -51,8 +51,25 @@ This kata is designed to get you familiar with the concept of measurements and u "title": "|0〉 or |+〉?", "path": "./zero_plus/", "qsDependencies": [ - "../KatasLibrary.qs", - "./Common.qs" + "../KatasLibrary.qs" + ] +}) + +@[exercise]({ + "id": "distinguishing_states__zero_plus_inc", + "title": "|0〉, |+〉 or Inconclusive?", + "path": "./zero_plus_inc/", + "qsDependencies": [ + "../KatasLibrary.qs" + ] +}) + +@[exercise]({ + "id": "distinguishing_states__peres_wooters_game", + "title": "Peres/Wooters game", + "path": "./peres_wooters_game/", + "qsDependencies": [ + "../KatasLibrary.qs" ] }) diff --git a/katas/content/distinguishing_states/peres_wooters_game/Placeholder.qs b/katas/content/distinguishing_states/peres_wooters_game/Placeholder.qs new file mode 100644 index 0000000000..a6102e85db --- /dev/null +++ b/katas/content/distinguishing_states/peres_wooters_game/Placeholder.qs @@ -0,0 +1,6 @@ +namespace Kata { + operation IsQubitNotInABC(q : Qubit) : Int { + // Implement your solution here... + return -1; + } +} \ No newline at end of file diff --git a/katas/content/distinguishing_states/peres_wooters_game/Solution.qs b/katas/content/distinguishing_states/peres_wooters_game/Solution.qs new file mode 100644 index 0000000000..002cf9fbb5 --- /dev/null +++ b/katas/content/distinguishing_states/peres_wooters_game/Solution.qs @@ -0,0 +1,35 @@ +namespace Kata { + open Microsoft.Quantum.Math; + operation IsQubitNotInABC (q : Qubit) : Int { + let alpha = ArcCos(Sqrt(2.0 / 3.0)); + + use a = Qubit(); + Z(q); + CNOT(a, q); + Controlled H([q], a); + S(a); + X(q); + + ApplyControlledOnInt(0, Ry, [a], (-2.0 * alpha, q)); + CNOT(a, q); + Controlled H([q], a); + CNOT(a, q); + + let res0 = MResetZ(a); + let res1 = M(q); + + if (res0 == Zero and res1 == Zero) { + return 0; + } + elif (res0 == One and res1 == Zero) { + return 1; + } + elif (res0 == Zero and res1 == One) { + return 2; + } + else { + // this should never occur + return 3; + } + } +} \ No newline at end of file diff --git a/katas/content/distinguishing_states/peres_wooters_game/Verification.qs b/katas/content/distinguishing_states/peres_wooters_game/Verification.qs new file mode 100644 index 0000000000..8bf13708b1 --- /dev/null +++ b/katas/content/distinguishing_states/peres_wooters_game/Verification.qs @@ -0,0 +1,71 @@ +namespace Kata.Verification { + open Microsoft.Quantum.Katas; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Random; + open Microsoft.Quantum.Math; + + operation StatePrep_IsQubitNotInABC (q : Qubit, state : Int) : Unit { + let alpha = (2.0 * PI()) / 3.0; + H(q); + + if state == 0 { + // convert |0⟩ to 1/sqrt(2) (|0⟩ + |1⟩) + } + elif state == 1 { + // convert |0⟩ to 1/sqrt(2) (|0⟩ + ω |1⟩), where ω = exp(2iπ/3) + R1(alpha, q); + } + else { + // convert |0⟩ to 1/sqrt(2) (|0⟩ + ω² |1⟩), where ω = exp(2iπ/3) + R1(2.0 * alpha, q); + } + } + + + @EntryPoint() + operation CheckSolution () : Bool { + let nTotal = 1000; + mutable bad_value = 0; + mutable wrong_state = 0; + + use qs = Qubit[1]; + + for i in 1 .. nTotal { + // get a random integer to define the state of the qubits + let state = DrawRandomInt(0, 2); + + // do state prep: convert |0⟩ to outcome with return equal to state + StatePrep_IsQubitNotInABC(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = Kata.IsQubitNotInABC(qs[0]); + + // check that the value of ans is 0, 1 or 2 + if (ans < 0 or ans > 2) { + set bad_value += 1; + } + + // check if upon conclusive result the answer is actually correct + if ans == state { + set wrong_state += 1; + } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); + } + + if bad_value == 0 and wrong_state == 0 { + Message("Correct!"); + return true; + } else { + if bad_value > 0 { + Message($"Solution returned values other than 0, 1 or 2 {bad_value} times."); + } + if wrong_state > 0 { + Message($"Solution gave incorrect response {wrong_state} times"); + } + Message("Incorrect."); + return false; + } + } +} \ No newline at end of file diff --git a/katas/content/distinguishing_states/peres_wooters_game/index.md b/katas/content/distinguishing_states/peres_wooters_game/index.md new file mode 100644 index 0000000000..7d0495138c --- /dev/null +++ b/katas/content/distinguishing_states/peres_wooters_game/index.md @@ -0,0 +1,17 @@ +**Input:** A qubit which is guaranteed to be in one of the three states: + +* $\ket{A} = \frac{1}{\sqrt{2}} \big( \ket{0} + \ket{1} \big)$, +* $\ket{B} = \frac{1}{\sqrt{2}} \big( \ket{0} + \omega \ket{1} \big)$, +* $\ket{C}= \frac{1}{\sqrt{2}} \big( \ket{0} + \omega^2 \ket{1} \big)$, + +Here $\omega = e^{2i \pi/ 3}$. + +**Output:** + +* 1 or 2 if the qubit was in the $\ket{A}$ state, +* 0 or 2 if the qubit was in the $\ket{B}$ state, +* 0 or 1 if the qubit was in the $\ket{C}$ state. + +You are never allowed to give an incorrect answer. Your solution will be called multiple times, with one of the states picked with equal probability every time. + +The state of the qubit at the end of the operation does not matter. \ No newline at end of file diff --git a/katas/content/distinguishing_states/peres_wooters_game/solution.md b/katas/content/distinguishing_states/peres_wooters_game/solution.md new file mode 100644 index 0000000000..cbdd243f4a --- /dev/null +++ b/katas/content/distinguishing_states/peres_wooters_game/solution.md @@ -0,0 +1,89 @@ +> The task is a game inspired by a quantum detection problem due to Holevo ("Information-theoretical aspects of quantum measurement", A. Holevo) and Peres/Wootters ("Optimal detection of quantum information", A. Peres and W. K. Wootters). In the game, player A thinks of a number (0, 1 or 2) and the opponent, player B, tries to guess any number but the one chosen by player A. +> +> Classically, if you just made a guess, you'd have to ask two questions to be right $100\%$ of the time. If instead, player A prepares a qubit with 0, 1, or 2 encoded into three single qubit states that are at an angle of 120 degrees with respect to each other and then hands the state to the opponent, then player B can apply a Positive Operator Valued Measure (POVM) consisting of 3 states that are perpendicular to the states chosen by player A. +> It can be shown that this allows B to be right $100\%$ of the time with only 1 measurement, which is something that is not achievable with a von Neumann measurement on 1 qubit. +See also ("Quantum Theory: Concepts and Methods", A. Peres) for a nice description of the optimal POVM. + +Next, we address how we can implement the mentioned POVM by way of a von Neumann measurement, and then how to implement said von Neumann measurement in Q\#. First, we note that the POVM elements are given by the columns of the following matrix: + +$$M = \frac{1}{\sqrt{2}}\left(\begin{array}{rrr} +1 & 1 & 1 \\ +1 & \omega & \omega^2 +\end{array} +\right)$$ + +where $\omega = e^{2 \pi i/3}$ denotes a primitive third root of unity. Our task will be to implement the rank 1 POVM given by the columns of $M$ via a von Neumann measurement. This can be done by \"embedding\" $M$ into a larger unitary matrix (taking complex conjugate and transpose): + +$$M' = \frac{1}{\sqrt{3}}\left(\begin{array}{cccc} +1 & -1 & 1 & 0 \\ +1 & -\omega^2 & \omega & 0 \\ +1 & -\omega & \omega^2 & 0 \\ +0 & 0 & 0 & -i\sqrt{3} +\end{array} +\right)$$ + +Notice that applying $M'$ to input states given by column $i$ of $M$ (padded with two zeros to make it a vector of length $4$), where $i=0, 1, 2$ will never return the label $i$ as the corresponding vectors are perpendicular. + +We are therefore left with the problem of implementing $M'$ as a sequence of elementary quantum gates. Notice that + +$$M' \cdot {\rm diag}(1,-1,1,-1) = M' \cdot (\mathbf{1}_2 \otimes Z) = +\frac{1}{\sqrt{3}}\left(\begin{array}{cccc} +1 & 1 & 1 & 0 \\ +1 & \omega^2 & \omega & 0 \\ +1 & \omega & \omega^2 & 0 \\ +0 & 0 & 0 & i\sqrt{3} +\end{array} +\right)$$ + +Using a technique used in the Rader (also sometimes called Rader-Winograd) decomposition of the discrete Fourier transform ("Discrete Fourier transforms when the number of data samples is prime", C. M. Rader), which reduces it to a cyclic convolution, we apply a $2\times 2$ Fourier transform on the indices $i,j=1,2$ of this matrix (i.e. a block matrix which consists of a direct sum of blocks $\mathbf{1}_1$, $H$, and $\mathbf{1}_1$ which we abbreviate in short as ${\rm diag}(1,H,1)$). + +> To implement this in Q#, we can use the following sequence of gates, applied to a 2-qubit array: +> +> ``` +> CNOT(qs[1], qs[0]); +> Controlled H([qs[0]], qs[1]); +> CNOT(qs[1], qs[0]); +> ``` + +This yields + +$${\rm diag}(1, H, 1) \cdot M' \cdot (\mathbf{1}_2 \otimes Z) \cdot {\rm diag}(1, H, 1) = +\left(\begin{array}{rrrr} +\frac{1}{\sqrt3} & \sqrt{\frac23} & 0 & 0 \\ +\sqrt{\frac23} & -\frac{1}{\sqrt3} & 0 & 0 \\ +0 & 0 & i & 0 \\ +0 & 0 & 0 & i +\end{array} +\right)$$ + +This implies that after multiplication with the diagonal operator $(S^\dagger \otimes \mathbf{1}_2)$, we are left with + +$${\rm diag}(1, H, 1) \cdot M' \cdot (\mathbf{1}_2 \otimes Z) \cdot {\rm diag}(1, H, 1)\cdot (S^\dagger \otimes \mathbf{1}_2) = +\left(\begin{array}{rrrr} +\frac{1}{\sqrt3} & \sqrt{\frac23} & 0 & 0 \\ +\sqrt{\frac23} & -\frac{1}{\sqrt3} & 0 & 0 \\ +0 & 0 & 1 & 0 \\ +0 & 0 & 0 & 1 +\end{array} +\right)$$ + +which is a zero-controlled rotation $R$ around the $Y$-axis by an angle given by $\arccos \sqrt{\frac23}$ (plus reordering of rows and columns). + +> In Q#, we can implement this matrix as the following sequence of gates, applied to a 2-qubit array: +> ``` +> CNOT(qs[1], qs[0]); +> X(qs[0]); +> let alpha = ArcCos(Sqrt(2.0 / 3.0)); +> (ControlledOnInt(0, Ry))([qs[1]], (-2.0 * alpha, qs[0])); +> ``` + +Putting everything together, we can implement the matrix $M'$ by applying the inverses of gates: + +$$M' = {\rm diag}(1,H,1) \cdot R \cdot (S \otimes \mathbf{1}_2) \cdot {\rm diag}(1,H,1) \cdot (\mathbf{1}_2 \otimes Z)$$ + +Noting finally, that to apply this sequence of unitaries to a column vector, we have to apply it in reverse when writing it as a program (as actions on vectors are left-associative). + +@[solution]({ + "id": "distinguishing_states__peres_wooters_game_solution", + "codePath": "Solution.qs" +}) \ No newline at end of file diff --git a/katas/content/distinguishing_states/zero_plus/Verification.qs b/katas/content/distinguishing_states/zero_plus/Verification.qs index a2c68bc2e6..59db1ee2bf 100644 --- a/katas/content/distinguishing_states/zero_plus/Verification.qs +++ b/katas/content/distinguishing_states/zero_plus/Verification.qs @@ -1,5 +1,8 @@ namespace Kata.Verification { open Microsoft.Quantum.Katas; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Random; + operation SetQubitZeroOrPlus (q : Qubit, state : Int) : Unit { if state != 0 { @@ -9,6 +12,35 @@ namespace Kata.Verification { @EntryPoint() operation CheckSolution () : Bool { - return DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, SetQubitZeroOrPlus, Kata.IsQubitZeroOrPlus); + let nTotal = 1000; + mutable nOk = 0; + let threshold = 0.8; + + use qs = Qubit[1]; + for i in 1 .. nTotal { + // get a random integer to define the state of the qubits + let state = DrawRandomInt(0, 1); + + // do state prep: convert |0⟩ to outcome with return equal to state + SetQubitZeroOrPlus(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = Kata.IsQubitZeroOrPlus(qs[0]); + if ans == (state == 0) { + set nOk += 1; + } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); + } + + if IntAsDouble(nOk) < threshold * IntAsDouble(nTotal) { + Message($"{nTotal - nOk} test runs out of {nTotal} returned incorrect state, which does not meet the required threshold of at least {threshold * 100.0}%."); + Message("Incorrect."); + return false; + } else { + Message("Correct!"); + return true; + } } } diff --git a/katas/content/distinguishing_states/zero_plus_inc/Placeholder.qs b/katas/content/distinguishing_states/zero_plus_inc/Placeholder.qs new file mode 100644 index 0000000000..0a522d9dce --- /dev/null +++ b/katas/content/distinguishing_states/zero_plus_inc/Placeholder.qs @@ -0,0 +1,6 @@ +namespace Kata { + operation IsQubitZeroPlusOrInconclusive(q : Qubit) : Int { + // Implement your solution here... + return -2; + } +} diff --git a/katas/content/distinguishing_states/zero_plus_inc/Solution.qs b/katas/content/distinguishing_states/zero_plus_inc/Solution.qs new file mode 100644 index 0000000000..a6a6060f4d --- /dev/null +++ b/katas/content/distinguishing_states/zero_plus_inc/Solution.qs @@ -0,0 +1,20 @@ +namespace Kata { + open Microsoft.Quantum.Random; + operation IsQubitZeroPlusOrInconclusive(q : Qubit) : Int { + // Pick a random basis + let basis = DrawRandomInt(0, 1); + if basis == 0 { + // use standard basis + let result = M(q); + // result is One only if the state was |+⟩ + return result == One ? 1 | -1; + } + else { + // use Hadamard basis + H(q); + let result = M(q); + // result is One only if the state was |0⟩ + return result == One ? 0 | -1; + } + } +} diff --git a/katas/content/distinguishing_states/zero_plus_inc/Verification.qs b/katas/content/distinguishing_states/zero_plus_inc/Verification.qs new file mode 100644 index 0000000000..5b9191684e --- /dev/null +++ b/katas/content/distinguishing_states/zero_plus_inc/Verification.qs @@ -0,0 +1,80 @@ +namespace Kata.Verification { + open Microsoft.Quantum.Katas; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Random; + + operation SetQubitZeroOrPlus (q : Qubit, state : Int) : Unit { + if state != 0 { + H(q); + } + } + + @EntryPoint() + operation CheckSolution() : Bool { + let nTotal = 10000; + let thresholdInconcl = 0.8; + let thresholdConcl = 0.1; + mutable isCorrect = true; + + // counts total inconclusive answers + mutable nInconc = 0; + + // counts total conclusive |0⟩ state identifications + mutable nConclOne = 0; + + // counts total conclusive |+> state identifications + mutable nConclPlus = 0; + + use qs = Qubit[1]; + for i in 1 .. nTotal { + + // get a random integer to define the state of the qubits + let state = DrawRandomInt(0, 1); + + // do state prep: convert |0⟩ to outcome with return equal to state + SetQubitZeroOrPlus(qs[0], state); + + // get the solution's answer and analyze it + let ans = Kata.IsQubitZeroPlusOrInconclusive(qs[0]); + + // keep track of the number of inconclusive answers given + if ans == -1 { + set nInconc += 1; + } + + if (ans == 0 and state == 0) { + set nConclOne += 1; + } + + if (ans == 1 and state == 1) { + set nConclPlus += 1; + } + + ResetAll(qs); + } + + if IntAsDouble(nInconc) > thresholdInconcl * IntAsDouble(nTotal) { + Message($"{nInconc} test runs out of {nTotal} returned inconclusive which does not meet the required threshold of at most {thresholdInconcl * 100.0}%."); + set isCorrect = false; + } + + if IntAsDouble(nConclOne) < thresholdConcl * IntAsDouble(nTotal) { + Message($"{nConclOne} test runs out of {nTotal} returned conclusive |0⟩ which does not meet the required threshold of at least {thresholdConcl * 100.0}%."); + set isCorrect = false; + } + + if IntAsDouble(nConclPlus) < thresholdConcl * IntAsDouble(nTotal) { + Message($"{nConclPlus} test runs out of {nTotal} returned conclusive |+⟩ which does not meet the required threshold of at least {thresholdConcl * 100.0}%."); + set isCorrect = false; + } + + if (isCorrect) { + Message("Correct!"); + return true; + } else { + Message("Incorrect"); + return false; + } + } + +} diff --git a/katas/content/distinguishing_states/zero_plus_inc/index.md b/katas/content/distinguishing_states/zero_plus_inc/index.md new file mode 100644 index 0000000000..26a3f168bd --- /dev/null +++ b/katas/content/distinguishing_states/zero_plus_inc/index.md @@ -0,0 +1,18 @@ +**Input:** A qubit which is guaranteed to be in either the $\ket{0}$ or the $\ket{+}$ state. + +**Output:** +* 0 if the qubit was in the $\ket{0}$ state, +* 1 if it was in the $\ket{+}$ state, +* -1 if you can't decide, i.e., an "inconclusive" result. + +Your solution: + +* should never give 0 or 1 answer incorrectly (i.e., identify $\ket{0}$ as 1 or $\ket{+}$ as 0), +* will be called multiple times, with one of the states picked with equal probability every time, +* may give an inconclusive (-1) answer in at most 80% of all the cases, +* must correctly identify the $\ket{0}$ state as 0 in at least 10% of all the cases, +* must correctly identify the $\ket{1}$ state as 1 in at least 10% of all the cases. + +The state of the qubit at the end of the operation does not matter. + +> This task is an example of unambiguous state discrimination. \ No newline at end of file diff --git a/katas/content/distinguishing_states/zero_plus_inc/solution.md b/katas/content/distinguishing_states/zero_plus_inc/solution.md new file mode 100644 index 0000000000..bc2fa25700 --- /dev/null +++ b/katas/content/distinguishing_states/zero_plus_inc/solution.md @@ -0,0 +1,55 @@ +A simple strategy that gives an inconclusive result with probability 0.75 and never errs in case it yields a conclusive result can be obtained from randomizing the choice of measurement basis between the computational basis and the Hadamard basis. + +Notice that when measured in the standard basis, the state $\ket{0}$ will always lead to the outcome "0", and the state $\ket{+}$ will lead to outcomes "0" and "1" with probability $\frac12$ each. This means that if we measure "1", we can with certainty conclude that the state was $\ket{+}$. + +A similar argument applies to the scenario where we measure in the Hadamard basis, where $\ket{0}$ can lead to both "+" and "-" outcomes, and $\ket{+}$ always leads to "+". Then if we measured "-", we can with certainty conclude that the state was $\ket{0}$. + +This leads to the following scenarios (shown are the conditional probabilities + of the resulting answers in each of the above scenarios). + +
| State | +Basis | +P(0) | +P(1) | +P(-1) | +
|---|---|---|---|---|
| $\ket{0}$ | +Computational | +$0$ | +$0$ | +$1$ | +
| $\ket{+}$ | +Computational | +$0$ | +$\frac12$ | +$\frac12$ | +
| $\ket{0}$ | +Hadamard | +$\frac12$ | +$0$ | +$\frac12$ | +
| $\ket{+}$ | +Hadamard | +$0$ | +$0$ | +$1$ | +