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..6124064a39 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/Solution.qs @@ -0,0 +1,17 @@ +namespace Kata { + operation CreateEntangledTriple (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 + + // 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 + 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..c25ae633b4 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/Verification.qs @@ -0,0 +1,40 @@ +namespace Kata.Verification { + open Microsoft.Quantum.Diagnostics; + + 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 + + // 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 + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } + + @EntryPoint() + operation CheckSolution() : Bool { + use qs = Qubit[3]; + // apply operation that needs to be tested + Kata.CreateEntangledTriple(qs); + + // apply adjoint reference operation and check that the result is |0ᴺ⟩ + Adjoint CreateEntangledTriple_Reference(qs); + + // check that all qubits end up in |0⟩ state + let result = CheckAllZero(qs); + ResetAll(qs); + if result { + Message("Correct!"); + } + else { + Message("Entangled triple is not implemented correctly"); + } + return result; + } +} 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..9a62c521e8 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_create_entangled_triple/index.md @@ -0,0 +1,9 @@ +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 $|000\rangle$ 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 GHZ state $\frac{1}{\sqrt{2}} (\big(\ket{000} + \ket{111} \big)$ up to local unitary operation. 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..ea196a2b7b --- /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://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.canon.controlledonbitstring) 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_game/Placeholder.qs b/katas/content/nonlocal_games/ghz_quantum_game/Placeholder.qs new file mode 100644 index 0000000000..3ee6e1f331 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_game/Placeholder.qs @@ -0,0 +1,7 @@ +namespace Kata { + operation PlayQuantumGHZ (strategies : ((Bool, Qubit) => Bool)[], inputs : Bool[]) : Bool[] { + // Implement your solution here... + + return []; + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_game/Solution.qs b/katas/content/nonlocal_games/ghz_quantum_game/Solution.qs new file mode 100644 index 0000000000..5d05cb9e60 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_game/Solution.qs @@ -0,0 +1,32 @@ +namespace Kata { + operation CreateEntangledTriple (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 + + // 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 + ApplyControlledOnBitString([false, true], X, [qs[0], qs[1]], qs[2]); + ApplyControlledOnBitString([true, false], X, [qs[0], qs[1]], qs[2]); + } + + operation PlayQuantumGHZ (strategies : ((Bool, Qubit) => Bool)[], inputs : Bool[]) : Bool[] { + use qs = Qubit[3]; + CreateEntangledTriple(qs); + + let r = inputs[0]; + let s = inputs[1]; + let t = inputs[2]; + let a = strategies[0](r, qs[0]); + let b = strategies[1](s, qs[1]); + let c = strategies[2](t, qs[2]); + + ResetAll(qs); + return [a, b, c]; + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_game/Verification.qs b/katas/content/nonlocal_games/ghz_quantum_game/Verification.qs new file mode 100644 index 0000000000..b9da0bc6f3 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_game/Verification.qs @@ -0,0 +1,93 @@ +namespace Kata.Verification { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Logical; + + // All possible starting bits (r, s and t) that the referee can give + // to Alice, Bob and Charlie. + function RefereeBits () : Bool[][] { + return [[false, false, false], + [true, true, false], + [false, true, true], + [true, false, true]]; + } + + function WinCondition_Reference (rst : Bool[], abc : Bool[]) : Bool { + return (rst[0] or rst[1] or rst[2]) == Xor(Xor(abc[0], abc[1]), abc[2]); + } + + operation AliceQuantum_Reference (bit : Bool, qubit : Qubit) : Bool { + if bit { + H(qubit); + } + return M(qubit) == One; + } + + operation BobQuantum_Reference (bit : Bool, qubit : Qubit) : Bool { + if bit { + H(qubit); + } + return M(qubit) == One; + } + + operation CharlieQuantum_Reference (bit : Bool, qubit : Qubit) : Bool { + if bit { + H(qubit); + } + return M(qubit) == One; + } + + 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 + + // 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 + 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[]) : Bool[] { + use qs = Qubit[3]; + CreateEntangledTriple_Reference(qs); + let r = inputs[0]; + let s = inputs[1]; + let t = inputs[2]; + let a = strategies[0](r, qs[0]); + let b = strategies[1](s, qs[1]); + let c = strategies[2](t, qs[2]); + + ResetAll(qs); + return [a, b, c]; + } + + @EntryPoint() + operation CheckSolution() : Bool { + let inputs = RefereeBits(); + let strategies = [AliceQuantum_Reference, BobQuantum_Reference, CharlieQuantum_Reference]; + for rst in inputs { + let actualBits = Kata.PlayQuantumGHZ(strategies, rst); + if Length(actualBits) != 3 { + Message($"Expected 3 bits from PlayQuantumGHZ, got {Length(actualBits)}"); + return false; + } + let expectedBits = PlayQuantumGHZ_Reference(strategies, rst); + let actualWin = WinCondition_Reference(rst, actualBits); + let expectedWin = WinCondition_Reference(rst, expectedBits); + if actualWin != expectedWin { + Message($"Expected win={expectedWin} {expectedBits}, got {actualBits} for {rst}"); + return false; + } + else { + Message($"Cool {expectedBits}, got {actualBits} for {rst}"); + } + } + true + } +} diff --git a/katas/content/nonlocal_games/ghz_quantum_game/index.md b/katas/content/nonlocal_games/ghz_quantum_game/index.md new file mode 100644 index 0000000000..449fadb22c --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_game/index.md @@ -0,0 +1,8 @@ +Let's play the GHZ game using the quantum strategy. + +**Inputs:** +1. An array of three operations which implement the quantum strategies of the players, +2. An array of 3 input bits that should be passed to the players. + +**Goal:** +An array of three bits that will be produced if each player uses their given strategy. diff --git a/katas/content/nonlocal_games/ghz_quantum_game/solution.md b/katas/content/nonlocal_games/ghz_quantum_game/solution.md new file mode 100644 index 0000000000..f74e5fbea9 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_game/solution.md @@ -0,0 +1,11 @@ +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. Return their measurement results. + +@[solution]({ + "id": "nonlocal_games__ghz_quantum_game_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..3db48f34f0 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/Solution.qs @@ -0,0 +1,27 @@ +namespace Kata { + operation AliceQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + let res = MResetX(qubit); + return res == One; + } + let res = MResetZ(qubit); + return res == One; + } + + operation BobQuantum (bit : Bool, qubit : Qubit) : Bool { + if bit { + let res = MResetX(qubit); + return res == One; + } + let res = MResetZ(qubit); + return res == 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..93b7be9102 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/Verification.qs @@ -0,0 +1,44 @@ +namespace Kata.Verification { + + @EntryPoint() + operation CheckSolution() : Bool { + use q = Qubit(); + for _ in 1 .. 4 { + // repeat 4 times since we are testing a measurement and wrong basis still might get + // the correct answer, reduces probability of false positives + if (Kata.AliceQuantum(false, q) != false) { + Message("|0⟩ not measured as false"); + Reset(q); + return false; + } + + // apply the Pauli X gate + X(q); + if (Kata.AliceQuantum(false, q) != true) { + Message("|1⟩ not measured as true"); + Reset(q); + return false; + } + + // apply the Hadamard gate + H(q); + if (Kata.AliceQuantum(true, q) != false) { + Message("|+⟩ not measured as false"); + Reset(q); + return false; + } + + // apply the Pauli X and then the Hadamard gate + X(q); + H(q); + if (Kata.AliceQuantum(true, q) != true) { + Message("|-⟩ not measured as true"); + Reset(q); + return false; + } + Reset(q); + } + 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..a3e03f5fa1 --- /dev/null +++ b/katas/content/nonlocal_games/ghz_quantum_strategy/index.md @@ -0,0 +1,8 @@ +**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..7910d9c5f8 --- /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://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.measure) +or convenient shorthands for measure-and-reset-to-$\ket{0}$ sequence of operations +[MResetZ](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.measurement.mresetz) and +[MResetX](https://docs.microsoft.com/qsharp/api/qsharp/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..6f03c8c6e8 100644 --- a/katas/content/nonlocal_games/index.md +++ b/katas/content/nonlocal_games/index.md @@ -226,6 +226,24 @@ Then, let's proceed with quantum strategy and game implementation. "path": "./ghz_classical_game/" }) +@[exercise]({ + "id": "nonlocal_games__ghz_create_ghz_state", + "title": "Create Entangled Triple", + "path": "./ghz_create_entangled_triple/" +}) + +@[exercise]({ + "id": "nonlocal_games__ghz_quantum_strategy", + "title": "Quantum Strategy", + "path": "./ghz_quantum_strategy/" +}) + +@[exercise]({ + "id": "nonlocal_games__ghz_quantum_game", + "title": "Quantum Game", + "path": "./ghz_quantum_game/" +}) + @[section]({ "id": "nonlocal_games__conclusion", "title": "Conclusion"