Skip to content

Commit

Permalink
Superdense coding kata finish (#1479)
Browse files Browse the repository at this point in the history
Part of #1185.

Changes Done:
- Revamped Task 1: Using `qAlice` and `qBob` for better clarity.
Replaced `\rangle` with `\ket`
- Added tasks 2-4.
- Using a tuple of boolean variables instead of `ProtocolMessage` to
improve learner and developer productivity.

---------

Co-authored-by: Mariia Mykhailova <[email protected]>
  • Loading branch information
Manvi-Agrawal and tcNickolas authored May 8, 2024
1 parent aeea75f commit 3897cea
Show file tree
Hide file tree
Showing 23 changed files with 352 additions and 16 deletions.
1 change: 1 addition & 0 deletions katas/content/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"single_qubit_measurements",
"multi_qubit_measurements",
"random_numbers",
"superdense_coding",
"oracles",
"deutsch_algo",
"deutsch_jozsa",
Expand Down
75 changes: 75 additions & 0 deletions katas/content/superdense_coding/Common.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace Kata.Verification {
operation CreateEntangledPairWrapper_Reference(qs : Qubit[]) : Unit is Adj + Ctl {
let (qAlice, qBob) = (qs[0], qs[1]);
H(qAlice);
CNOT(qAlice, qBob);
}

operation EncodeMessageInQubit_Reference(qAlice : Qubit, message : (Bool, Bool)) : Unit {
let (bit1, bit2) = message;

if bit1 {
Z(qAlice);
}

if bit2 {
X(qAlice);
}
}

operation DecodeMessageFromQubits_Reference(qAlice : Qubit, qBob : Qubit) : (Bool, Bool) {
CNOT(qAlice, qBob);
H(qAlice);
return (MResetZ(qAlice) == One, MResetZ(qBob) == One);
}


// ------------------------------------------------------
// Helper operation that runs superdense coding protocol using two building blocks
// specified as first two parameters.
operation ComposeProtocol(
encodeOp : ((Qubit, (Bool, Bool)) => Unit),
decodeOp : ((Qubit, Qubit) => (Bool, Bool)),
message : (Bool, Bool)
) : (Bool, Bool) {

use (qAlice, qBob) = (Qubit(), Qubit());

CreateEntangledPairWrapper_Reference([qAlice, qBob]);

encodeOp(qAlice, message);

let (bit1, bit2) = decodeOp(qAlice, qBob);

ResetAll([qAlice, qBob]);

return (bit1, bit2);
}

// ------------------------------------------------------
// Helper operation that runs superdense coding protocol (specified by protocolOp)
// on all possible input values and verifies that decoding result matches the inputs
operation CheckProtocolWithFeedback(protocolOp : ((Bool, Bool) => (Bool, Bool))) : Bool {

// Loop over the 4 possible combinations of two bits
for n in 0..3 {
let data = (1 == n / 2, 1 == n % 2);
let (dataBit1, dataBit2) = data;

for iter in 1..100 {
let (bit1, bit2) = protocolOp(data);

// Now test if the bits were transfered correctly.
if not (bit1 == dataBit1 and bit2 == dataBit2) {
Message("Incorrect.");
Message($"({dataBit1}, {dataBit2}) was transfered incorrectly as ({bit1}, {bit2})");
return false;
}
}
}

Message("Correct!");
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Kata {
operation EncodeMessageInQubit(qAlice : Qubit, message : (Bool, Bool)) : Unit {
// Get the bits from the message
let (bit1, bit2) = message;

// Implement your solution here...

}
}
13 changes: 13 additions & 0 deletions katas/content/superdense_coding/alice_sends_message/Solution.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Kata {
operation EncodeMessageInQubit(qAlice : Qubit, message : (Bool, Bool)) : Unit {
let (bit1, bit2) = message;

if bit2 {
X(qAlice);
}

if bit1 {
Z(qAlice);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata.Verification {
@EntryPoint()
operation CheckSolution() : Bool {
return CheckProtocolWithFeedback(ComposeProtocol(Kata.EncodeMessageInQubit, DecodeMessageFromQubits_Reference, _));
}

}
16 changes: 16 additions & 0 deletions katas/content/superdense_coding/alice_sends_message/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**Inputs**:

1. `qAlice` : Alice's part of the entangled pair of qubits.
2. `message`: Two classical bits represented by a tuple of of two `Bool` variables to represent `bit1` and `bit2` respectively.

**Goal**:
Encode the message (two classical bits) by manipulating Alice's qubit.

Superdense coding protocol changes the joint state of the two qubits to one of the following four states based on the value of message:

- `(0, 0)`: $\ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11})$
- `(0, 1)`: $\ket{\Psi^{+}} = \frac{1}{\sqrt{2}} (\ket{01} + \ket{10})$
- `(1, 0)`: $\ket{\Phi^{-}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11})$
- `(1, 1)`: $\ket{\Psi^{-}} = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10})$

Note that your solution is tested as part of an end-to-end implementation of the protocol; the goal is to get the message transmitted correctly.
35 changes: 35 additions & 0 deletions katas/content/superdense_coding/alice_sends_message/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Recall that we learnt how to prepare all Bell states in "Preparing Quantum States" kata. This is slightly advanced version of that task demonstrating how operations applied to one qubit of a Bell state allow us to transform it into any other Bell state.

Superdense coding protocol uses the below Bell state we prepared in the previous task:

$$\frac{1}{\sqrt{2}} \big(\ket{00} + \ket{11} \big)$$

We can transform it into every other Bell state according to the value of `message`:

- `(0, 0)`: $\ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11})$
- `(0, 1)`: $\ket{\Psi^{+}} = \frac{1}{\sqrt{2}} (\ket{01} + \ket{10})$
- `(1, 0)`: $\ket{\Phi^{-}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11})$
- `(1, 1)`: $\ket{\Psi^{-}} = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10})$

Here is how we can perform this transformation:

- If `bits == (0, 0)`, we do nothing - the prepared state is already $\ket{\Phi^{+}}$.

- If `bits == (0, 1)`, we need to change the second qubit in both $\ket{00}$ and $\ket{11}$ terms. Observe that applying an $X$ gate to Alice's qubit does exactly that:
$$(X \otimes I) \ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{10} + \ket{01})$$

- If `bits == (1, 0)`, we need to add a relative phase of $-1$ to the $\ket{11}$ term. Observe that applying $Z$ gate to Alice's qubit does exactly that:
$$(Z \otimes I) \ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11})$$

- If `bits = (1, 1)`, we use the same logic to realize that we need to apply both the $Z$ and $X$ corrections to get $\ket{\Psi^{-}}$ state.
$$ (Z \otimes I) \cdot (X \otimes I) \ket{\Psi^{+}} = (Z \otimes I) \frac{1}{\sqrt{2}} (\ket{10} + \ket{01}) = \frac{1}{\sqrt{2}} (-\ket{10} + \ket{01}) = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10}) $$

The final sequence of steps is as follows:

1. Apply the $X$ gate to Alice's qubit if `bit2 == 1`.
2. Apply the $Z$ gate to Alice's qubit if `bit1 == 1`.

@[solution]({
"id": "superdense_coding__alice_sends_message_solution",
"codePath": "./Solution.qs"
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata {
operation DecodeMessageFromQubits(qAlice : Qubit, qBob : Qubit) : (Bool, Bool) {
// Implement your solution here...

return (false, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata {
operation DecodeMessageFromQubits(qAlice : Qubit, qBob : Qubit) : (Bool, Bool) {
CNOT(qAlice, qBob);
H(qAlice);
return (MResetZ(qAlice) == One, MResetZ(qBob) == One);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata.Verification {
@EntryPoint()
operation CheckSolution() : Bool {
return CheckProtocolWithFeedback(ComposeProtocol(EncodeMessageInQubit_Reference, Kata.DecodeMessageFromQubits, _));
}
}

8 changes: 8 additions & 0 deletions katas/content/superdense_coding/bob_decodes_message/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**Inputs:**

1. `qAlice` : Qubit received from Alice.
2. `qBob` : Bob's part of the entangled pair.

**Goal** : Decode the message using the qubit received from Alice. For this, retrieve two bits of classic data from the qubits and return a tuple of the form `(Bool, Bool)` to represent `message`. The state of the qubits in the end of the operation does not matter.

Note that your solution is tested as part of an end-to-end implementation of the protocol; the goal is to get the message transmitted correctly.
22 changes: 22 additions & 0 deletions katas/content/superdense_coding/bob_decodes_message/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Recall that Alice encoded qubits as follows:

- `(0, 0)`: $\ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11})$
- `(0, 1)`: $\ket{\Psi^{+}} = \frac{1}{\sqrt{2}} (\ket{01} + \ket{10})$
- `(1, 0)`: $\ket{\Phi^{-}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11})$
- `(1, 1)`: $\ket{\Psi^{-}} = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10})$

To read out the encoded message, Bob needs to figure out which of the four Bell states he has. We can map the Bell states to basis states by applying a $CNOT$ gate with the first qubit as control and the second qubit as target, followed by an $H$ gate on the first qubit. (Notice that this is exactly what [`Adjoint`](https://learn.microsoft.com/azure/quantum/user-guide/language/expressions/functorapplication#adjoint-functor) of the state preparation operation in the first task does.)

What is the outcome of this transformation, assuming each of the possible quantum states after the encoding step?

- $\ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11}) \rightarrow \ket{00}$
- $\ket{\Psi^{+}} = \frac{1}{\sqrt{2}} (\ket{01} + \ket{10}) \rightarrow \ket{01}$
- $\ket{\Phi^{-}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11}) \rightarrow \ket{10}$
- $\ket{\Psi^{-}} = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10}) \rightarrow \ket{11}$

Hence, we can retrieve the encoded bits just by measuring the bits.

@[solution]({
"id": "superdense_coding__bob_decodes_message_solution",
"codePath": "./Solution.qs"
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Kata {
operation CreateEntangledPair(qs : Qubit[]) : Unit is Adj {
operation CreateEntangledPair(qAlice : Qubit, qBob : Qubit) : Unit is Adj {
// Implement your solution here...

}
Expand Down
6 changes: 3 additions & 3 deletions katas/content/superdense_coding/entangled_pair/Solution.qs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Kata {
operation CreateEntangledPair(qs : Qubit[]) : Unit is Adj {
H(qs[0]);
CNOT(qs[0], qs[1]);
operation CreateEntangledPair(qAlice : Qubit, qBob : Qubit) : Unit is Adj {
H(qAlice);
CNOT(qAlice, qBob);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
namespace Kata.Verification {
open Microsoft.Quantum.Katas;

operation CreateEntangledPair(qs : Qubit[]) : Unit is Adj + Ctl {
H(qs[0]);
CNOT(qs[0], qs[1]);
operation CreateEntangledPair_Wrapper(qs : Qubit[]) : Unit is Adj {
Kata.CreateEntangledPair(qs[0], qs[1]);
}

@EntryPoint()
operation CheckSolution() : Bool {

return CheckOperationsEquivalenceOnZeroStateWithFeedback(
Kata.CreateEntangledPair,
CreateEntangledPair,
CreateEntangledPair_Wrapper,
CreateEntangledPairWrapper_Reference,
2
);
}
Expand Down
7 changes: 5 additions & 2 deletions katas/content/superdense_coding/entangled_pair/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
**Input:** Two qubits in the $|00\rangle$ state (stored in an array of length 2).
**Inputs:**

**Goal:** Prepare a Bell state $|\Phi^{+}\rangle = \frac{1}{\sqrt{2}} (|00\rangle + |11\rangle)$ on these qubits.
1. `qAlice` : Alice's qubit in $\ket{0}$ state.
2. `qBob` : Bob's qubit in $\ket{0}$ state.

**Goal:** Prepare a Bell state $|\Phi^{+}\rangle = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11})$ on these qubits.
2 changes: 1 addition & 1 deletion katas/content/superdense_coding/entangled_pair/solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Recall that this state is the Bell state that we have already seen in "Multi-Qub

The solution takes two steps:

1. Apply Hadamard gate on first qubit to get the state: $\big(\frac{1}{\sqrt{2}}|0\rangle + \frac{1}{\sqrt{2}}|1\rangle\big) \otimes |0\rangle$.
1. Apply Hadamard gate on first qubit to get the state: $\big(\frac{1}{\sqrt{2}} \ket{0} + \frac{1}{\sqrt{2}} \ket{1} \big) \otimes \ket{0}$.
2. Apply a $CNOT$ gate with the first qubit as the control and the second qubit as the target.

@[solution]({
Expand Down
43 changes: 39 additions & 4 deletions katas/content/superdense_coding/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Superdense Coding Kata
# Superdense Coding

@[section]({
"id": "./superdense_coding__overview",
"id": "superdense_coding__overview",
"title": "Overview"
})

Expand All @@ -18,10 +18,45 @@ We split the superdense coding protocol into several steps:
- Finally, we compose those steps into the complete superdense coding protocol.

@[exercise]({
"id": "./superdense_coding__entangled_pair",
"id": "superdense_coding__entangled_pair",
"title": "Entangled Pair",
"path": "entangled_pair",
"qsDependencies": [
"../KatasLibrary.qs"
"../KatasLibrary.qs",
"./Common.qs"
]
})

@[exercise]({
"id": "superdense_coding__alice_sends_message",
"title": "Alice's Task: Encode Message",
"path": "alice_sends_message",
"qsDependencies": [
"./Common.qs"
]
})

@[exercise]({
"id": "superdense_coding__bob_decodes_message",
"title": "Bob's Task: Decode Message",
"path": "bob_decodes_message",
"qsDependencies": [
"./Common.qs"
]
})

@[exercise]({
"id": "superdense_coding__protocol_e2e",
"title": "Superdense Coding Protocol: End to End",
"path": "protocol_e2e",
"qsDependencies": [
"./Common.qs"
]
})

@[section]({
"id": "superdense_coding__conclusion",
"title": "Conclusion"
})

Congratulations! In this kata you learned to implement superdense coding protocol.
31 changes: 31 additions & 0 deletions katas/content/superdense_coding/protocol_e2e/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Kata {
operation SuperdenseCodingProtocol(message : (Bool, Bool)) : (Bool, Bool) {
// Implement your solution here...

return (false, false);
}

// You might find these helper operations from earlier tasks useful.
operation CreateEntangledPair(qAlice : Qubit, qBob : Qubit) : Unit is Adj {
H(qAlice);
CNOT(qAlice, qBob);
}

operation EncodeMessageInQubit(qAlice : Qubit, message : (Bool, Bool)) : Unit {
let (bit1, bit2) = message;

if bit2 {
X(qAlice);
}

if bit1 {
Z(qAlice);
}
}

operation DecodeMessageFromQubits(qAlice : Qubit, qBob : Qubit) : (Bool, Bool) {
CNOT(qAlice, qBob);
H(qAlice);
return (MResetZ(qAlice) == One, MResetZ(qBob) == One);
}
}
Loading

0 comments on commit 3897cea

Please sign in to comment.