-
Notifications
You must be signed in to change notification settings - Fork 61
Add Quantum Teleportation algorithm #186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": "# Quantum Teleportation\n\nThis notebook demonstrates the quantum teleportation protocol using Amazon Braket.\n\nQuantum teleportation transfers an unknown quantum state from one qubit (Alice) to another (Bob) using a shared entangled Bell pair and two bits of classical communication. The original state is destroyed in the process, consistent with the no-cloning theorem.\n\nSince Amazon Braket does not support mid-circuit measurement with classical feedforward, this implementation uses the **deferred measurement** principle: classically-controlled gates are replaced by quantum-controlled gates (CNOT and CZ), which produce equivalent results.\n\n**Note:** This implementation creates the Bell pair $|\\Phi^+\\rangle = (|00\\rangle + |11\\rangle)/\\sqrt{2}$ directly using H and CNOT gates. The algorithm library also provides a `bell_singlet` subroutine (in `bells_inequality`) that prepares the singlet state $|\\psi^-\\rangle$, which is a different Bell state used for inequality tests." | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## References\n", | ||
| "\n", | ||
| "[1] Bennett, C. H. et al. \"Teleporting an unknown quantum state via dual classical and Einstein-Podolsky-Rosen channels.\" Physical Review Letters 70, 1895 (1993). https://doi.org/10.1103/PhysRevLett.70.1895\n", | ||
| "\n", | ||
| "[2] Nielsen, M. A. and Chuang, I. L. \"Quantum Computation and Quantum Information.\" Cambridge University Press (2010)." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Run on a local simulator" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import numpy as np\n", | ||
| "from braket.circuits import Circuit\n", | ||
| "from braket.devices import LocalSimulator\n", | ||
| "from braket.tracking import Tracker\n", | ||
| "\n", | ||
| "from braket.experimental.algorithms.quantum_teleportation import (\n", | ||
| " get_quantum_teleportation_results,\n", | ||
| " quantum_teleportation_circuit,\n", | ||
| " run_quantum_teleportation,\n", | ||
| ")\n", | ||
| "\n", | ||
| "tracker = Tracker().start()\n", | ||
| "device = LocalSimulator()" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Print the circuit\n", | ||
| "\n", | ||
| "The teleportation circuit for teleporting $|1\\rangle$ (prepared with an X gate):" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "circ = quantum_teleportation_circuit(Circuit().x(0))\n", | ||
| "print(circ)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Teleport different states\n", | ||
| "\n", | ||
| "We teleport $|0\\rangle$, $|1\\rangle$, and $|+\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$, then verify Bob's qubit matches the original state." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": "test_cases = [\n (\"|0>\", None),\n (\"|1>\", Circuit().x(0)),\n (\"|+>\", Circuit().h(0)),\n (\"Ry(pi/3)|0>\", Circuit().ry(0, np.pi / 3)),\n]\n\nnames, probs_0, probs_1 = [], [], []\nfor name, prep in test_cases:\n circ = quantum_teleportation_circuit(prep)\n task = run_quantum_teleportation(circ, device, shots=1000)\n result = get_quantum_teleportation_results(task)\n names.append(name)\n probs_0.append(result[\"0\"])\n probs_1.append(result[\"1\"])\n print(f\"Teleport {name}: P(|0>) = {result['0']:.4f}, P(|1>) = {result['1']:.4f}\")" | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "source": "import matplotlib.pyplot as plt\n\nx = np.arange(len(names))\nwidth = 0.35\n\nfig, ax = plt.subplots(figsize=(8, 5))\nax.bar(x - width / 2, probs_0, width, label=r\"$P(|0\\rangle)$\", color=\"steelblue\")\nax.bar(x + width / 2, probs_1, width, label=r\"$P(|1\\rangle)$\", color=\"coral\")\nax.set_xticks(x)\nax.set_xticklabels(names)\nax.set_ylabel(\"Probability\")\nax.set_title(\"Quantum Teleportation Results (Bob's qubit)\")\nax.legend()\nax.set_ylim(0, 1.15)\nax.grid(axis=\"y\", alpha=0.3)\nplt.tight_layout()\nplt.show()", | ||
| "metadata": {}, | ||
| "execution_count": null, | ||
| "outputs": [] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Run on a QPU\n", | ||
| "\n", | ||
| "To run on a QPU, replace `LocalSimulator()` with an `AwsDevice`. The cost for 4 circuits of 1000 shots on IQM Garnet is approximately $7.00 USD." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "# # Uncomment to run on a QPU\n", | ||
| "# from braket.aws import AwsDevice\n", | ||
| "# qpu = AwsDevice(\"arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet\")\n", | ||
| "# circ = quantum_teleportation_circuit(Circuit().x(0))\n", | ||
| "# task = run_quantum_teleportation(circ, qpu, shots=1000)\n", | ||
| "# result = get_quantum_teleportation_results(task)\n", | ||
| "# print(f\"QPU result: {result}\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "print(\"Task Summary\")\n", | ||
| "print(f\"{tracker.quantum_tasks_statistics()} \\n\")\n", | ||
| "print(\n", | ||
| " f\"Estimated cost to run this example: \"\n", | ||
| " f\"{tracker.qpu_tasks_cost() + tracker.simulator_tasks_cost():.2f} USD\"\n", | ||
| ")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2)." | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3 (ipykernel)", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "name": "python", | ||
| "version": "3.11.0" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 4 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"). You | ||
| # may not use this file except in compliance with the License. A copy of | ||
| # the License is located at | ||
| # | ||
| # http://aws.amazon.com/apache2.0/ | ||
| # | ||
| # or in the "license" file accompanying this file. This file is | ||
| # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
| # ANY KIND, either express or implied. See the License for the specific | ||
| # language governing permissions and limitations under the License. | ||
|
|
||
| from braket.experimental.algorithms.quantum_teleportation.quantum_teleportation import ( # noqa: F401, E501 | ||
| get_quantum_teleportation_results, | ||
| quantum_teleportation_circuit, | ||
| run_quantum_teleportation, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| Quantum teleportation transfers an arbitrary quantum state from one qubit to another using a shared entangled Bell pair and classical communication. This implementation uses the deferred measurement principle, replacing mid-circuit measurements and classical feedforward with coherent CNOT and CZ corrections, so the circuit runs on any gate-based simulator. | ||
|
|
||
| <!-- | ||
| [metadata-name]: Quantum Teleportation | ||
| [metadata-tags]: Textbook | ||
| [metadata-url]: https://github.com/amazon-braket/amazon-braket-algorithm-library/tree/main/src/braket/experimental/algorithms/quantum_teleportation | ||
| --> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"). You | ||
| # may not use this file except in compliance with the License. A copy of | ||
| # the License is located at | ||
| # | ||
| # http://aws.amazon.com/apache2.0/ | ||
| # | ||
| # or in the "license" file accompanying this file. This file is | ||
| # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
| # ANY KIND, either express or implied. See the License for the specific | ||
| # language governing permissions and limitations under the License. | ||
| from typing import Dict, Optional | ||
|
|
||
| import numpy as np | ||
|
|
||
| from braket.circuits import Circuit | ||
| from braket.devices import Device | ||
| from braket.tasks import QuantumTask | ||
|
|
||
|
|
||
| def quantum_teleportation_circuit(state_prep_circ: Optional[Circuit] = None) -> Circuit: | ||
| """Create a quantum teleportation circuit using deferred measurement. | ||
|
|
||
| Teleports the state of qubit 0 to qubit 2 via a shared Bell pair. | ||
| Uses CNOT and CZ gates instead of classical feedforward (deferred measurement | ||
| principle), so the circuit can run on any simulator without mid-circuit measurement. | ||
|
|
||
| Qubit layout: | ||
| qubit 0: message qubit (Alice's input) | ||
| qubit 1: Alice's half of Bell pair | ||
| qubit 2: Bob's half of Bell pair (receives teleported state) | ||
|
|
||
| Args: | ||
| state_prep_circ (Optional[Circuit]): Circuit to prepare the state on qubit 0. | ||
| If None, teleports the |0> state. | ||
|
|
||
| Returns: | ||
| Circuit: Quantum teleportation circuit with probability measurement on qubit 2. | ||
| """ | ||
| circ = Circuit() | ||
|
|
||
| # Prepare the state to teleport on qubit 0 | ||
| if state_prep_circ is not None: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, add a unit test about the validation. |
||
| circ.add_circuit(state_prep_circ) | ||
|
|
||
| # Create Bell pair between qubit 1 and qubit 2 | ||
| circ.h(1) | ||
| circ.cnot(1, 2) | ||
|
|
||
| # Bell measurement on qubits 0 and 1 | ||
| circ.cnot(0, 1) | ||
| circ.h(0) | ||
|
|
||
| # Deferred corrections (equivalent to classical conditional gates) | ||
| circ.cnot(1, 2) # if qubit 1 measured |1>: apply X to qubit 2 | ||
| circ.cz(0, 2) # if qubit 0 measured |1>: apply Z to qubit 2 | ||
|
|
||
| circ.probability([2]) | ||
| return circ | ||
|
|
||
|
|
||
| def run_quantum_teleportation(circuit: Circuit, device: Device, shots: int = 1000) -> QuantumTask: | ||
| """Run a quantum teleportation circuit on a device. | ||
|
|
||
| Args: | ||
| circuit (Circuit): Quantum teleportation circuit. | ||
| device (Device): Braket device or simulator. | ||
| shots (int): Number of shots. Defaults to 1000. | ||
|
|
||
| Returns: | ||
| QuantumTask: Quantum task. | ||
| """ | ||
| return device.run(circuit, shots=shots) | ||
|
|
||
|
|
||
| def get_quantum_teleportation_results(task: QuantumTask) -> Dict[str, float]: | ||
| """Extract teleportation results from a quantum task. | ||
|
|
||
| Args: | ||
| task (QuantumTask): Completed quantum task. | ||
|
|
||
| Returns: | ||
| Dict[str, float]: Probability of Bob's qubit in {"0": P(|0>), "1": P(|1>)}. | ||
| """ | ||
| probs = task.result().result_types[0].value | ||
| probs = np.round(probs, 10) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is the rounding needed? |
||
| return {"0": probs[0], "1": probs[1]} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"). You | ||
| # may not use this file except in compliance with the License. A copy of | ||
| # the License is located at | ||
| # | ||
| # http://aws.amazon.com/apache2.0/ | ||
| # | ||
| # or in the "license" file accompanying this file. This file is | ||
| # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
| # ANY KIND, either express or implied. See the License for the specific | ||
| # language governing permissions and limitations under the License. | ||
|
|
||
| import numpy as np | ||
| import pytest | ||
|
|
||
| from braket.circuits import Circuit | ||
| from braket.devices import LocalSimulator | ||
| from braket.experimental.algorithms.quantum_teleportation import ( | ||
| get_quantum_teleportation_results, | ||
| quantum_teleportation_circuit, | ||
| run_quantum_teleportation, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def device(): | ||
| return LocalSimulator() | ||
|
|
||
|
|
||
| # teleport |0>, expect P(|0>)=1 | ||
| def test_teleport_zero(device): | ||
| circ = quantum_teleportation_circuit() | ||
| task = run_quantum_teleportation(circ, device, shots=0) | ||
| result = get_quantum_teleportation_results(task) | ||
| assert np.isclose(result["0"], 1.0) | ||
| assert np.isclose(result["1"], 0.0) | ||
|
|
||
|
|
||
| # teleport |1>, expect P(|1>)=1 | ||
| def test_teleport_one(device): | ||
| circ = quantum_teleportation_circuit(Circuit().x(0)) | ||
| task = run_quantum_teleportation(circ, device, shots=0) | ||
| result = get_quantum_teleportation_results(task) | ||
| assert np.isclose(result["0"], 0.0) | ||
| assert np.isclose(result["1"], 1.0) | ||
|
|
||
|
|
||
| # teleport |+>, expect 50/50 | ||
| def test_teleport_plus(device): | ||
| circ = quantum_teleportation_circuit(Circuit().h(0)) | ||
| task = run_quantum_teleportation(circ, device, shots=0) | ||
| result = get_quantum_teleportation_results(task) | ||
| assert np.isclose(result["0"], 0.5, atol=1e-6) | ||
| assert np.isclose(result["1"], 0.5, atol=1e-6) | ||
|
|
||
|
|
||
| # teleport Ry(pi/3)|0>, verify probabilities match | ||
| def test_teleport_arbitrary_state(device): | ||
| angle = np.pi / 3 | ||
| circ = quantum_teleportation_circuit(Circuit().ry(0, angle)) | ||
| task = run_quantum_teleportation(circ, device, shots=0) | ||
| result = get_quantum_teleportation_results(task) | ||
| # Ry(theta)|0> = cos(theta/2)|0> + sin(theta/2)|1> | ||
| assert np.isclose(result["0"], np.cos(angle / 2) ** 2, atol=1e-6) | ||
| assert np.isclose(result["1"], np.sin(angle / 2) ** 2, atol=1e-6) | ||
|
|
||
|
|
||
| # teleport |+i>, verify phase is preserved via S†H readout | ||
| def test_teleport_plus_i_phase(device): | ||
| # prep |+i> = S H |0> | ||
| teleportation_circuit = quantum_teleportation_circuit(Circuit().h(0).s(0)) | ||
|
|
||
| # S†H maps |+i> to |0>, so successful teleportation => P(|0>) ~= 1 | ||
| phase_sensitive_circuit = ( | ||
| Circuit().add(teleportation_circuit.instructions).si(2).h(2).probability([2]) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This kind of bypasses |
||
| ) | ||
|
|
||
| task = run_quantum_teleportation(phase_sensitive_circuit, device, shots=0) | ||
| result = get_quantum_teleportation_results(task) | ||
|
|
||
| assert np.isclose(result["0"], 1.0, atol=1e-6) | ||
| assert np.isclose(result["1"], 0.0, atol=1e-6) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be more complete to mention that the circuit generator is only implemented for a 3 qubit case. And mention your qubit layout