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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ __pycache__/
/build
/venv
/dist

.env
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Running notebooks locally requires additional dependencies located in [notebooks
| QPE | [Quantum_Phase_Estimation_Algorithm.ipynb](notebooks/textbook/Quantum_Phase_Estimation_Algorithm.ipynb) | [Kitaev1995](https://arxiv.org/abs/quant-ph/9511026) |
| Quantum Counting | [Quantum_Counting_Algorithm.ipynb](notebooks/textbook/Quantum_Counting_Algorithm.ipynb) | [Brassard1998](https://arxiv.org/abs/quant-ph/9805082) |
| Quantum Walk | [Quantum_Walk.ipynb](notebooks/textbook/Quantum_Walk.ipynb) | [Childs2002](https://arxiv.org/abs/quant-ph/0209131) |
|Rabi Oscillations| [Rabi_Oscillations.ipynb](notebooks/textbook/Rabi_Oscillations.ipynb) | [Rabi1937](https://journals.aps.org/pr/abstract/10.1103/PhysRev.51.652) |
|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
848 changes: 848 additions & 0 deletions notebooks/textbook/Rabi_Oscillations.ipynb

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .rabi_oscillations import (
excited_state_probability,
rabi_circuit,
rabi_probability,
rabi_simulated_dynamics,
)

__all__ = [
"excited_state_probability",
"rabi_circuit",
"rabi_probability",
"rabi_simulated_dynamics",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Rabi Oscillations

Rabi oscillations describe coherent population transfer in a driven two-level system.
In the circuit model, sweeping the rotation angle in an `Rx(θ)` gate produces an
excited-state probability of `sin²(θ/2)`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import math
from math import sin

from braket.circuits import Circuit, ResultType
from braket.devices import LocalSimulator


def rabi_probability(theta: float) -> float:
"""Return excited-state probability for a single-qubit Rx rotation.

Args:
theta (float): Rotation angle.

Returns:
float: Probability of measuring |1>.
"""
return sin(theta / 2) ** 2


def rabi_circuit(theta: float) -> Circuit:
"""Generate a single-qubit Rabi oscillation circuit.

Args:
theta (float): Rotation angle.

Returns:
Circuit: Circuit implementing Rx(theta) on qubit 0.
"""
return Circuit().rx(0, theta)


def rabi_simulated_dynamics(
theta: float,
*,
gamma_t1: float = 0.0,
gamma_t2: float = 0.0,
delta: float = 0.0,
dtheta: float | None = None,
) -> Circuit:
"""Generate a single-qubit circuit for noisy or detuned Rabi dynamics.

Args:
theta (float): Total resonant drive rotation angle.
gamma_t1 (float): Amplitude damping strength per unit rotation angle.
gamma_t2 (float): Phase damping strength per unit rotation angle.
delta (float): Detuning strength relative to the resonant drive.
dtheta (float | None): Step size for stepwise evolution. If None,
noise and detuning are applied once after the full rotation.

Returns:
Circuit: Circuit implementing the requested Rabi dynamics.
"""
circ = Circuit()

if dtheta is None:
circ.rx(0, theta)

if delta != 0.0:
circ.rz(0, delta * theta)
if gamma_t1 != 0.0:
circ.amplitude_damping(0, gamma_t1)
if gamma_t2 != 0.0:
circ.phase_damping(0, gamma_t2)

else:
n_steps = max(1, math.ceil(theta / dtheta))
dtheta_eff = theta / n_steps
dphi = delta * dtheta_eff
gamma_t1_step = gamma_t1 * dtheta_eff
gamma_t2_step = gamma_t2 * dtheta_eff

for _ in range(n_steps):
circ.rx(0, dtheta_eff)

if delta != 0.0:
circ.rz(0, dphi)
if gamma_t1 != 0.0:
circ.amplitude_damping(0, gamma_t1_step)
if gamma_t2 != 0.0:
circ.phase_damping(0, gamma_t2_step)

circ.add_result_type(ResultType.Probability(target=[0]))
return circ


def excited_state_probability(circ: Circuit, device: LocalSimulator) -> float:
"""Run a probability-result circuit and return the excited-state probability.

Args:
circ (Circuit): Circuit with a probability result type.
device (LocalSimulator): Simulator used to run the circuit.

Returns:
float: Probability of measuring |1>.
"""
task = device.run(circ, shots=0)
probs = task.result().result_types[0].value
return float(probs[1])
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import math

from braket.devices import LocalSimulator
from braket.experimental.algorithms.rabi_oscillations import (
excited_state_probability,
rabi_circuit,
rabi_probability,
rabi_simulated_dynamics,
)


def test_rabi_probability_edge_cases():
assert rabi_probability(0.0) == 0.0
assert math.isclose(rabi_probability(math.pi), 1.0, rel_tol=0.0, abs_tol=1e-12)
assert math.isclose(rabi_probability(2 * math.pi), 0.0, rel_tol=0.0, abs_tol=1e-12)


def test_rabi_circuit_has_single_rx_instruction():
circ = rabi_circuit(0.123)
assert circ.qubit_count == 1
assert len(circ.instructions) == 1

instr = circ.instructions[0]
assert instr.operator.name == "Rx"
assert list(instr.target) == [0]


def test_rabi_simulated_dynamics_defaults_to_single_rx_with_probability_result():
circ = rabi_simulated_dynamics(0.123)

assert circ.qubit_count == 1
assert len(circ.instructions) == 1
assert circ.instructions[0].operator.name == "Rx"
assert len(circ.result_types) == 1


def test_rabi_simulated_dynamics_adds_single_channel_noise():
circ = rabi_simulated_dynamics(0.123, gamma_t1=0.1, gamma_t2=0.2, delta=0.3)

operator_names = [instruction.operator.name for instruction in circ.instructions]

assert operator_names == ["Rx", "Rz", "AmplitudeDamping", "PhaseDamping"]
assert len(circ.result_types) == 1


def test_rabi_simulated_dynamics_adds_stepwise_noise_and_detuning():
circ = rabi_simulated_dynamics(
math.pi,
gamma_t1=0.1,
gamma_t2=0.2,
delta=0.3,
dtheta=0.5,
)

n_steps = math.ceil(math.pi / 0.5)
operator_names = [instruction.operator.name for instruction in circ.instructions]

assert len(circ.instructions) == 4 * n_steps
assert operator_names[:4] == ["Rx", "Rz", "AmplitudeDamping", "PhaseDamping"]
assert len(circ.result_types) == 1


def test_excited_state_probability_returns_probability_of_one():
device = LocalSimulator()
circ = rabi_simulated_dynamics(math.pi)

assert math.isclose(
excited_state_probability(circ, device),
1.0,
rel_tol=0.0,
abs_tol=1e-12,
)

def test_rabi_simulated_dynamics_stepwise_only_detuning():
circ = rabi_simulated_dynamics(
math.pi,
delta=0.3,
dtheta=0.5,
)

operator_names = [instruction.operator.name for instruction in circ.instructions]

assert "Rz" in operator_names
assert "AmplitudeDamping" not in operator_names
assert "PhaseDamping" not in operator_names


def test_rabi_simulated_dynamics_stepwise_only_t1():
circ = rabi_simulated_dynamics(
math.pi,
gamma_t1=0.1,
dtheta=0.5,
)

operator_names = [instruction.operator.name for instruction in circ.instructions]

assert "AmplitudeDamping" in operator_names
assert "Rz" not in operator_names
assert "PhaseDamping" not in operator_names


def test_rabi_simulated_dynamics_stepwise_only_t2():
circ = rabi_simulated_dynamics(
math.pi,
gamma_t2=0.1,
dtheta=0.5,
)

operator_names = [instruction.operator.name for instruction in circ.instructions]

assert "PhaseDamping" in operator_names
assert "Rz" not in operator_names
assert "AmplitudeDamping" not in operator_names