Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Running notebooks locally requires additional dependencies located in [notebooks
| Quantum Circuit Born Machine | [Quantum_Circuit_Born_Machine.ipynb](notebooks/textbook/Quantum_Circuit_Born_Machine.ipynb) | [Benedetti2019](https://www.nature.com/articles/s41534-019-0157-8), [Liu2018](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.98.062324) |
| QFT | [Quantum_Fourier_Transform.ipynb](notebooks/textbook/Quantum_Fourier_Transform.ipynb) | [Coppersmith2002](https://arxiv.org/abs/quant-ph/0201067) |
| QPE | [Quantum_Phase_Estimation_Algorithm.ipynb](notebooks/textbook/Quantum_Phase_Estimation_Algorithm.ipynb) | [Kitaev1995](https://arxiv.org/abs/quant-ph/9511026) |
| Quantum Teleportation | [Quantum_Teleportation.ipynb](notebooks/textbook/Quantum_Teleportation.ipynb) | [Bennett1993](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.70.1895) |
| Quantum Walk | [Quantum_Walk.ipynb](notebooks/textbook/Quantum_Walk.ipynb) | [Childs2002](https://arxiv.org/abs/quant-ph/0209131) |
|Shor's| [Shors_Algorithm.ipynb](notebooks/textbook/Shors_Algorithm.ipynb) | [Shor1998](https://arxiv.org/abs/quant-ph/9508027) |
| Simon's | [Simons_Algorithm.ipynb](notebooks/textbook/Simons_Algorithm.ipynb) | [Simon1997](https://epubs.siam.org/doi/10.1137/S0097539796298637) |
Expand Down
148 changes: 148 additions & 0 deletions notebooks/textbook/Quantum_Teleportation.ipynb
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.

Copy link
Copy Markdown
Contributor

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

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)


<!--
[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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state_prep_circ must be a circuit for a single qubit. Please add this assumption more explicitly to the docstring, and add a validation here that throws an error if it's not a single-qubit circuit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of bypasses quantum_teleportation_circuit circuit by adding more gates. I understand that you add it for easier assertion in the test. Please add comment on the reason why you are doing it.

)

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)