Rydberg Layout addon
The Rydberg layout addon creates atomic layouts and analog schedules for neutral atom quantum computers.
Device Specification
To work with quantum hardware and simulators, we need to define the specifications of our device.
These specifications include information about atom placement, the maximum number of atoms,
the interaction coefficient, and the allowed detuning range.
In this example, we will define an AtomicDeviceSpecs.
import math
from parityos_addons.rydberg_layout.base.device import AtomicDeviceSpecs, CircularDevice
device_specs = AtomicDeviceSpecs(
device_geometry=CircularDevice(50),
min_atom_distance=4,
max_atom_count=100,
interaction_coefficient=5.0 * 1e6,
detuning_range=(-60, 60),
)
where device_geometry specifies the geometry of the device (circular, radius = maximum atomic
distance from center, in µm), min_atom_distance is the minimum allowed separation between atoms
(in µm), max_atom_count is the maximum number of atoms, and interaction_coefficient
corresponds to \(C_6/\hbar\) (in rad/µs x µm^6),
with detuning_range representing
the allowed range of detuning (in rad/µs).
Atoms
Let’s arrange atoms in the triangle gadget proposed in [Lanthaler23].
To do this, we need to define the Qubit instances, their coordinates,
and their Detunings.
from parityos import Qubit
from parityos_addons.rydberg_layout.base.atom import Atom
from parityos_addons.rydberg_layout.base.atom_coordinate import AtomCoordinate
from parityos_addons.rydberg_layout.base.detuning import Detuning
qubit_count = 6
qubits = [Qubit(index) for index in range(qubit_count)]
rydberg_distance = 5.0
list_of_coordinates = [
AtomCoordinate(0.0, 0.0, 0.0),
AtomCoordinate(2 * rydberg_distance, 0.0, 0.0),
AtomCoordinate(rydberg_distance, math.sqrt(3) * rydberg_distance, 0.0),
AtomCoordinate(0.5 * rydberg_distance, math.sqrt(3) / 2 * rydberg_distance, 0.0),
AtomCoordinate(rydberg_distance, 0.0, 0.0),
AtomCoordinate(1.5 * rydberg_distance, math.sqrt(3) / 2 * rydberg_distance, 0.0),
]
alpha_detuning = 15 # rad/µs
list_of_detunings = [
Detuning(J=alpha_detuning, alpha=alpha_detuning),
Detuning(J=alpha_detuning, alpha=alpha_detuning),
Detuning(J=alpha_detuning, alpha=alpha_detuning),
Detuning(J=0.0, alpha=2 * alpha_detuning),
Detuning(J=0.0, alpha=2 * alpha_detuning),
Detuning(J=0.0, alpha=2 * alpha_detuning),
]
atoms = [
Atom(coordinates, qubit, detunings)
for coordinates, qubit, detunings in zip(
list_of_coordinates, qubits, list_of_detunings
)
]
Here, we have chosen J values for the detunings such that all the atoms will have the same Detuning.value, which will allow us to run simulations later.
RydbergAtoms
For creating a RydbergAtoms instance one needs to supply the list of Atomss and the given
device specification. An exception is raised if the Atomss do not fit to the device, e.g.
because minimal distances are violated.
from parityos_addons.rydberg_layout.base.rydberg_atoms import RydbergAtoms
rydberg_atoms = RydbergAtoms(atoms, device_specs)
Analog Computation with Rydberg Atoms
The Ising Hamiltonian for Rydberg Atoms [Henriet2020] can be written as follows:
where \(n_j = (1 + Z_j) / 2\), \(X_j\) and \(Z_j\) are the Pauli matrices of the spin
\(j\) and \(C_6\) is the van-der-Waals interaction coefficient.
\(\Omega(t)\) denotes the time-dependent Rabi-frequency and \(\delta(t)\) denotes the time-dependent detuning.
Analog schedules for neutral atom can be defined by providing the corresponding \(\Omega(t)\),
\(\delta(t)\) functions. Here is an example how one can create a RydbergSchedule by
providing those inputs:
import sympy
from parityos_addons.rydberg_layout.schedule.rydberg_schedule import RydbergSchedule
# Parameters in rad/µs and ns
Omega_max = 9
final_detuning = 1
initial_detuning = -0.5
t_rise = 1000
t_sweep = 3000
t_fall = 1000
t_total = t_rise + t_sweep + t_fall
t = sympy.Symbol("t")
# the times when the pulse is changing
t_1 = t_rise
t_2 = t_rise + t_sweep
rabi_coefficient = sympy.Piecewise(
((t / t_1) * Omega_max, (t >= 0) & (t < t_1)),
(Omega_max, (t >= t_1) & (t < t_2)),
((1 - (t - t_2) / (t_total - t_2)) * Omega_max, (t >= t_2) & (t <= t_total)),
)
detuning_coefficient = sympy.Piecewise(
(initial_detuning, (t >= 0) & (t < t_1)),
(
(final_detuning * (t - t_1) + initial_detuning * (t_2 - t)) / (t_2 - t_1),
(t >= t_1) & (t < t_2),
),
(final_detuning, (t >= t_2) & (t <= t_total)),
)
rydberg_schedule = RydbergSchedule(
rydberg_atoms=rydberg_atoms,
detuning_coefficient=detuning_coefficient,
rabi_coefficient=rabi_coefficient,
time_parameter=t,
duration=t_total,
)
Visualization tools for atoms and schedules
We can use visualization tools to check if the atoms are in the expected positions:
from parityos_addons.rydberg_layout.visualization.plot_rydberg_atoms import (
plot_rydberg_atoms,
)
plot_rydberg_atoms(rydberg_atoms, show=True)
Also, one can visualize the RydbergSchedules:
from parityos_addons.rydberg_layout.visualization.plot_rydberg_schedule import (
plot_rydberg_schedule,
)
plot_rydberg_schedule(rydberg_schedule, show=True)
Pulser Exporter
For simulations, we can use pulser_exporter. For that, one needs to add [pulser]
during the installation:
pip install parityos["rydberg_layout", "pulser"]
Here is how we can run the simulation for the example created above:
import pulser
from parityos_addons.interfaces.pulser_exporter import (
rydberg_schedule_to_pulse, atoms_to_pulser_register,
)
register = atoms_to_pulser_register(atoms=atoms)
pulse = rydberg_schedule_to_pulse(schedule=rydberg_schedule)
sequence = pulser.Sequence(register, pulser.devices.MockDevice)
sequence.declare_channel("rydberg_global", "rydberg_global")
sequence.add(pulse, "rydberg_global")
backend = pulser.backends.QutipBackend(sequence)
result = backend.run()
counts = result.sample_final_state(1000)
solution = max(counts, key=counts.get)
print("counts: ", counts)
print("solution:", solution)
For this simulation, it is expected that in the solution only the atoms at the corners of the triangle gadget will be in the Rydberg state. This can be checked by visualizing the obtained atomic state:
from parityos_addons.rydberg_layout.utils.rydberg_atom_state import RydbergAtomState
from parityos_addons.rydberg_layout.visualization.plot_rydberg_state import plot_rydberg_state
atom_state = RydbergAtomState.from_bit_string(atoms=atoms, bit_string=solution)
plot_rydberg_state(atom_state, show=True)
References
Lanthaler et al, Rydberg-blockade-based parity quantum optimization, Phys. Rev. Lett. 130, 220601 (2023).
Henriet et al, “Quantum computing with neutral atoms”, Quantum 4, 327 (2020).
Silvério et al, “Pulser: An open-source package for the design of pulse sequences in programmable neutral-atom arrays”, Quantum 6, 629 (2022).