diff --git a/katas/content/nonlocal_games/examples/GHZGameDemo.qs b/katas/content/nonlocal_games/examples/GHZGameDemo.qs new file mode 100644 index 0000000000..3858ee2460 --- /dev/null +++ b/katas/content/nonlocal_games/examples/GHZGameDemo.qs @@ -0,0 +1,82 @@ +namespace Quantum.Kata.GHZGame { + open Microsoft.Quantum.Random; + open Microsoft.Quantum.Convert; + + function WinCondition (rst : Bool[], abc : Bool[]) : Bool { + return (rst[0] or rst[1] or rst[2]) == (abc[0] != abc[1] != abc[2]); + } + + function AliceClassical (r : Bool) : Bool { + return true; + } + + function BobClassical (s : Bool) : Bool { + return true; + } + + function CharlieClassical (t : Bool) : Bool { + return true; + } + + operation CreateEntangledTriple (qs : Qubit[]) : Unit is Adj { + X(qs[0]); + X(qs[1]); + H(qs[0]); + H(qs[1]); + Controlled Z([qs[0]], qs[1]); + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } + + operation AliceQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + return MResetX(qubit) == One; + } + return MResetZ(qubit) == One; + } + + operation BobQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + return MResetX(qubit) == One; + } + return MResetZ(qubit) == One; + } + + operation CharlieQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + return MResetX(qubit) == One; + } + return MResetZ(qubit) == One; + } + + operation getRandomRefereeBits () : Bool[] { + let bits = [[false, false, false], + [true, true, false], + [false, true, true], + [true, false, true]]; + return bits[DrawRandomInt(0, 3)]; + } + + @EntryPoint() + operation GHZ_GameDemo () : Unit { + use (aliceQubit, bobQubit, charlieQubit) = (Qubit(), Qubit(), Qubit()); + mutable classicalWins = 0; + mutable quantumWins = 0; + let iterations = 1000; + for _ in 1 .. iterations { + CreateEntangledTriple([aliceQubit, bobQubit, charlieQubit]); + let inputs = getRandomRefereeBits(); + let coutputs = [AliceClassical(inputs[0]), BobClassical(inputs[1]), CharlieClassical(inputs[2])]; + if WinCondition(inputs, coutputs) { + set classicalWins += 1; + } + let qoutputs = [AliceQuantum(inputs[0], aliceQubit), BobQuantum(inputs[1], bobQubit), CharlieQuantum(inputs[2], charlieQubit)]; + if WinCondition(inputs, qoutputs) { + set quantumWins += 1; + } + ResetAll([aliceQubit, bobQubit, charlieQubit]); + } + Message($"Percentage of classical wins is {100.0*IntAsDouble(classicalWins)/IntAsDouble(iterations)}%"); + Message($"Percentage of quantum wins is {100.0*IntAsDouble(quantumWins)/IntAsDouble(iterations)}%"); + } +} diff --git a/katas/content/nonlocal_games/ghz_create_entangled_triple/Placeholder.qs b/katas/content/nonlocal_games/ghz_create_entangled_triple/Placeholder.qs new file mode 100644 index 0000000000..fc35a3eba4 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/Placeholder.qs @@ -0,0 +1,5 @@ +namespace Kata { + operation CreateEntangledTriple (qs : Qubit[]) : Unit { + // Implement your solution here... + } +} diff --git a/katas/content/nonlocal_games/ghz_create_entangled_triple/Solution.qs b/katas/content/nonlocal_games/ghz_create_entangled_triple/Solution.qs new file mode 100644 index 0000000000..77314a6679 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/Solution.qs @@ -0,0 +1,11 @@ +namespace Kata { + operation CreateEntangledTriple (qs : Qubit[]) : Unit is Adj { + X(qs[0]); + X(qs[1]); + H(qs[0]); + H(qs[1]); + Controlled Z([qs[0]], qs[1]); + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } +} diff --git a/katas/content/nonlocal_games/ghz_create_entangled_triple/Verification.qs b/katas/content/nonlocal_games/ghz_create_entangled_triple/Verification.qs new file mode 100644 index 0000000000..039384cd56 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/Verification.qs @@ -0,0 +1,19 @@ +namespace Kata.Verification { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Katas; + + operation CreateEntangledTriple_Reference (qs : Qubit[]) : Unit is Adj { + X(qs[0]); + X(qs[1]); + H(qs[0]); + H(qs[1]); + Controlled Z([qs[0]], qs[1]); + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } + + @EntryPoint() + operation CheckSolution() : Bool { + CheckOperationsEquivalenceOnZeroStateWithFeedback(Kata.CreateEntangledTriple, CreateEntangledTriple_Reference, 3) + } +} diff --git a/katas/content/nonlocal_games/ghz_create_entangled_triple/index.md b/katas/content/nonlocal_games/ghz_create_entangled_triple/index.md new file mode 100644 index 0000000000..4566d06dcb --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/index.md @@ -0,0 +1,12 @@ +In the quantum version of the game, the players still can not communicate during the game, but they are allowed to share +qubits from an entangled triple before the start of the game. + +**Input:** +An array of three qubits in the $\ket{000}$ state. + +**Goal:** +Create the entangled state $\ket{\Phi} = \frac{1}{2} \big(\ket{000} - \ket{011} - \ket{101} - \ket{110} \big)$ on these qubits. + +>This state is equivalent to the three-qubit [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) +>$$\frac{1}{\sqrt{2}} \big(\ket{000} + \ket{111} \big)$$ +>up to local unitary operations. Please refer to the follow-up GHZ Quantum Strategy discussion for details. diff --git a/katas/content/nonlocal_games/ghz_create_entangled_triple/solution.md b/katas/content/nonlocal_games/ghz_create_entangled_triple/solution.md new file mode 100644 index 0000000000..674cac56f7 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/solution.md @@ -0,0 +1,13 @@ +1. Apply an X gate to the first and the second qubits to get the $\ket{110}$ state. +2. Appy an H gate to the first and the second qubits to get the following state: +$$\frac12 \big( \ket{000} - \ket{010} - \ket{100} + \ket{110} \big)$$ +3. Flip the sign of the last term using a controlled Z gate with the first qubit as control and the second qubit as target (or vice versa): +$$\frac12 \big( \ket{000} - \ket{010} - \ket{100} -{\color{blue}\ket{110}} \big)$$ +4. Now we have the right signs for each term, and the first and the last terms match those of the state we're preparing, so we just need to adjust the two middle terms. +To do this, we can use [ControlledOnBitString](https://learn.microsoft.com/qsharp/api/qsharp-lang/microsoft.quantum.canon/applycontrolledonbitstring) operation to flip the state of the last qubit if the first two qubits are in $\ket{01}$ or in $\ket{10}$ states, which gives us: +$$\frac{1}{2} \big(\ket{000} - {\color{blue}\ket{011}} - {\color{blue}\ket{101}} - \ket{110} \big)$$ + +@[solution]({ + "id": "nonlocal_games__ghz_create_entangled_triple_solution", + "codePath": "Solution.qs" +}) diff --git a/katas/content/nonlocal_games/ghz_quantum_strategy/Placeholder.qs b/katas/content/nonlocal_games/ghz_quantum_strategy/Placeholder.qs new file mode 100644 index 0000000000..051a08d564 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/Placeholder.qs @@ -0,0 +1,19 @@ +namespace Kata { + operation AliceQuantum (bit : Bool, qubit : Qubit) : Bool { + // Implement your solution here... + + return false; + } + + operation BobQuantum (bit : Bool, qubit : Qubit) : Bool { + // Implement your solution here... + + return false; + } + + operation CharlieQuantum (bit : Bool, qubit : Qubit) : Bool { + // Implement your solution here... + + return false; + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_strategy/Solution.qs b/katas/content/nonlocal_games/ghz_quantum_strategy/Solution.qs new file mode 100644 index 0000000000..6904f2ad8e --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/Solution.qs @@ -0,0 +1,23 @@ +namespace Kata { + operation AliceQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + return MResetX(qubit) == One; + } + return MResetZ(qubit) == One; + } + + operation BobQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + return MResetX(qubit) == One; + } + return MResetZ(qubit) == One; + } + + // alternative implementation + operation CharlieQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + H(qubit); + } + return M(qubit) == One; + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_strategy/Verification.qs b/katas/content/nonlocal_games/ghz_quantum_strategy/Verification.qs new file mode 100644 index 0000000000..3b7c2d8023 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/Verification.qs @@ -0,0 +1,59 @@ +namespace Kata.Verification { + + function WinCondition_Reference (rst : Bool[], abc : Bool[]) : Bool { + return (rst[0] or rst[1] or rst[2]) == (abc[0] != abc[1] != abc[2]); + } + + function RefereeBits () : Bool[][] { + return [[false, false, false], + [true, true, false], + [false, true, true], + [true, false, true]]; + } + + operation CreateEntangledTriple_Reference (qs : Qubit[]) : Unit is Adj { + X(qs[0]); + X(qs[1]); + H(qs[0]); + H(qs[1]); + Controlled Z([qs[0]], qs[1]); + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } + + operation PlayQuantumGHZ_Reference (strategies : ((Bool, Qubit) => Bool)[], inputs : Bool[], qubits : Qubit[]) : Bool[] { + let r = inputs[0]; + let s = inputs[1]; + let t = inputs[2]; + let a = strategies[0](r, qubits[0]); + let b = strategies[1](s, qubits[1]); + let c = strategies[2](t, qubits[2]); + return [a, b, c]; + } + + @EntryPoint() + operation CheckSolution () : Bool { + use qs = Qubit[3]; + let inputs = RefereeBits(); + let strategies = [Kata.AliceQuantum, Kata.BobQuantum, Kata.CharlieQuantum]; + + let iterations = 1000; + mutable wins = 0; + for _ in 1 .. iterations { + for bits in inputs { + CreateEntangledTriple_Reference(qs); + let abc = PlayQuantumGHZ_Reference(strategies, bits, qs); + if WinCondition_Reference(bits, abc) { + set wins = wins + 1; + } + ResetAll(qs); + } + } + if wins < iterations*Length(inputs) { + Message($"Players' quantum strategies get {wins} wins out of {iterations*Length(inputs)} runs, which is not optimal"); + return false; + } + Message("Correct!"); + true + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_strategy/index.md b/katas/content/nonlocal_games/ghz_quantum_strategy/index.md new file mode 100644 index 0000000000..c604c7906f --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/index.md @@ -0,0 +1,11 @@ +In this task, you should implement three functions, one for each player's quantum strategy. +Note that they are covered by one test, so you must implement all of them to pass the test. + +**Inputs:** + +1. The input bit for one of each of the players (R, S and T respectively), +2. That player's qubit of the entangled triple shared between the players. + +**Goal:** +Measure the qubit in the Z basis if the bit is 0 (FALSE), or the X basis if the bit is 1 (TRUE), and return the result. +The state of the qubit after the operation does not matter. diff --git a/katas/content/nonlocal_games/ghz_quantum_strategy/solution.md b/katas/content/nonlocal_games/ghz_quantum_strategy/solution.md new file mode 100644 index 0000000000..861d9f78e3 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/solution.md @@ -0,0 +1,12 @@ +In Q#, you can perform measurements in a specific basis using either the +[Measure operation](https://learn.microsoft.com/qsharp/api/qsharp-lang/microsoft.quantum.intrinsic/measure) +or convenient shorthands for measure-and-reset-to-$\ket{0}$ sequence of operations +[MResetZ](https://learn.microsoft.com/qsharp/api/qsharp-lang/microsoft.quantum.measurement/mresetz) and +[MResetX](https://learn.microsoft.com/qsharp/api/qsharp-lang/microsoft.quantum.measurement/mresetx). + +Alternatively, you can recall that measuring the qubit in the X basis is equivalent to applying an H gate to it and measuring it in the Z basis. + +@[solution]({ + "id": "nonlocal_games__ghz_quantum_strategy_solution", + "codePath": "Solution.qs" +}) diff --git a/katas/content/nonlocal_games/index.md b/katas/content/nonlocal_games/index.md index 2eb25e8aad..1d683bb1d1 100644 --- a/katas/content/nonlocal_games/index.md +++ b/katas/content/nonlocal_games/index.md @@ -64,7 +64,7 @@ Then, let's proceed with quantum strategies for Alice and Bob. }) @[section]({ - "id": "nonlocal_games__discussion", + "id": "nonlocal_games__chsh_discussion", "title": "Discussion: Probability of Victory for Quantum Strategy" }) @@ -226,6 +226,146 @@ Then, let's proceed with quantum strategy and game implementation. "path": "./ghz_classical_game/" }) +@[exercise]({ + "id": "nonlocal_games__ghz_create_entangled_state", + "title": "Create Entangled Triple", + "path": "./ghz_create_entangled_triple/" +}) + +@[exercise]({ + "id": "nonlocal_games__ghz_quantum_strategy", + "title": "Quantum Strategy", + "path": "./ghz_quantum_strategy/" +}) + +@[section]({ + "id": "nonlocal_games__ghz_discussion", + "title": "Discussion: Why the GHZ Quantum Strategy has a 100% Win Rate" +}) +--------------------------------------------------------------- +Recall the formula for the win condition: +1. The sum of the answer bits must be even if the question bits are (0,0,0) +2. The sum of the answer bits must be odd if the question bits are (1,1,0), (1,0,1) or (0,1,1). + +> As a reminder, the probability "wavefunction" for three qubits is given by the following vector of length 8: +> $$\begin{bmatrix} +\psi_{000}\\ +\psi_{001}\\ +\psi_{010}\\ +\psi_{011}\\ +\psi_{100}\\ +\psi_{101}\\ +\psi_{110}\\ +\psi_{111} +\end{bmatrix}$$ +> $|\psi_{ijk}|^2$ gives the probability of observing the corresponding basis state $\ket{ijk}$ upon measuring the qubit trio. + +Now, the entangled state $\ket{\Phi}$ that Alice, Bob and Charlie have agreed to use is represented as + +$$\begin{bmatrix} ++1/2\\ + 0\\ + 0\\ +-1/2\\ + 0\\ +-1/2\\ +-1/2\\ + 0 +\end{bmatrix}$$ + +Let's first consider the case in which **all three players got the 0 bit**. + +When the players make their measurements, they will collectively get one of the basis states of the original state - 000, 011, 101 or 110. +This means they'll report back zero \"1\" bits between them (with $25\%$ probability) or two \"1\" bits between them (with $75\%$ probability), +either way satisfying the win condition for the team. + +Now, suppose **Alice gets a 0 bit and the others get 1**. + +Alice, looking at the 0, takes a Z basis measurement as before, while Bob and Charlie each take X basis measurements. +(An X basis measurement is also equivalent to performing a Hadamard transform followed by a standard Z basis measurement, +as the X basis is the $\ket{+}$ / $\ket{-}$, and a Hadamard transform rotates the $\ket{0}$ / $\ket{1}$ basis to $\ket{+}$ / $\ket{-}$.) +So Bob and Charlie apply a Hadamard transform to their qubits, which corresponds to the following transformation applied to the whole system state: + +$$I \otimes H \otimes H = \begin{bmatrix} +1/2 & 1/2 & 1/2 & 1/2 & 0 & 0 & 0 & 0\\ +1/2 & -1/2 & 1/2 & -1/2 & 0 & 0 & 0 & 0\\ +1/2 & 1/2 & -1/2 & -1/2 & 0 & 0 & 0 & 0\\ +1/2 & -1/2 & -1/2 & 1/2 & 0 & 0 & 0 & 0\\ +0 & 0 & 0 & 0 & 1/2 & 1/2 & 1/2 & 1/2\\ +0 & 0 & 0 & 0 & 1/2 & -1/2 & 1/2 & -1/2\\ +0 & 0 & 0 & 0 & 1/2 & 1/2 & -1/2 & -1/2\\ +0 & 0 & 0 & 0 & 1/2 & -1/2 & -1/2 & 1/2 +\end{bmatrix}$$ + +When applied to the original entangled state, all the amplitude shifts to the states corresponding to $\ket{001}$, $\ket{010}$, $\ket{100}$, and $\ket{111}$. +The precise configuration of the new entangled state is + +$$\begin{bmatrix} + 0\\ + 1/2\\ + 1/2\\ + 0\\ +-1/2\\ + 0\\ + 0\\ + 1/2 +\end{bmatrix}$$ + +Now the players perform their measurements, and an odd number of them will see \"1\" (thanks to the new entangled state), again satisfying the win condition. +Similarly, if **Alice and Charlie get \"1\" bits and Bob a \"0\"**, Alice and Charlie will apply Hadamard transforms to their qubits to give the tensor product + +$$ H \otimes I \otimes H = \begin{bmatrix} +1/2 & 1/2 & 0 & 0 & 1/2 & 1/2 & 0 & 0\\ +1/2 & -1/2 & 0 & 0 & 1/2 & -1/2 & 0 & 0\\ +0 & 0 & 1/2 & 1/2 & 0 & 0 & 1/2 & 1/2\\ +0 & 0 & 1/2 & -1/2 & 0 & 0 & 1/2 & -1/2\\ +1/2 & 1/2 & 0 & 0 & -1/2 & -1/2 & 0 & 0\\ +1/2 & -1/2 & 0 & 0 & -1/2 & 1/2 & 0 & 0\\ +0 & 0 & 1/2 & 1/2 & 0 & 0 & -1/2 & -1/2\\ +0 & 0 & 1/2 & -1/2 & 0 & 0 & -1/2 & 1/2 +\end{bmatrix}$$ + +The resulting state vector before the measurement will be the same as in the previous case, except that the $\ket{010}$ state +ends up with the negative amplitude instead of $\ket{100}$. Again the players report back an odd number of true bits between them and the team wins. + +Finally if Charlie got the \"0\" bit and Alice and Bob both got \"1\", the latter two will apply Hadamard transform for the tensor product + +$$ H \otimes H \otimes I = \begin{bmatrix} +1/2 & 0 & 1/2 & 0 & 1/2 & 0 & 1/2 & 0\\ +0 & 1/2 & 0 & 1/2 & 0 & 1/2 & 0 & 1/2\\ +1/2 & 0 & -1/2 & 0 & 1/2 & 0 & -1/2 & 0\\ +0 & 1/2 & 0 & -1/2 & 0 & 1/2 & 0 & -1/2\\ +1/2 & 0 & 1/2 & 0 & -1/2 & 0 & -1/2 & 0\\ +0 & 1/2 & 0 & 1/2 & 0 & -1/2 & 0 & -1/2\\ +1/2 & 0 & -1/2 & 0 & -1/2 & 0 & 1/2 & 0\\ +0 & 1/2 & 0 & -1/2 & 0 & -1/2 & 0 & 1/2 +\end{bmatrix}$$ + +Operating with this on the original entangled state yields $(\ket{100} + \ket{010} - \ket{001} + \ket{111})/2$ and +once more the team will report back an odd number of true bits between them and win. + +@[section]({ + "id": "nonlocal_games__ghz_e2e", + "title": "Running GHZ Game End to End" +}) + +Putting together the building blocks we've implemented into a strategy is very simple: + +1. Allocate three qubits and prepare our entangled state on them (using `CreateEntangledTriple`). +2. Send one of the qubits to each of the players (this step is \"virtual\", not directly reflected in Q# code, other than making sure that the strategies each act on their qubit only). +3. Have the players perform their measurements on their respective qubits using corresponding elements of the `strategies` array. +4. Reset qubits to $\ket{0}$ before they are released. +5. Return their measurement results. + +In the example below you can compare winning percentage of classical and quantum games. + +>You may play with the code and check if there is a difference in results when +>1. The referee picks non-random bits. How can the referee minimize player's win probability? +>2. Players get the [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state). +> How to change the quantum strategies to get $100\%$ win rate? + +@[example]({"id": "nonlocal_games__ghz_e2edemo", "codePath": "./examples/GHZGameDemo.qs"}) + @[section]({ "id": "nonlocal_games__conclusion", "title": "Conclusion"