Skip to content
Pasqal Documentation

Hardware specifications

What you will learn:

  • what is a Device and why is a Device needed;

  • what are the available devices and where to find them;

  • what do a Device and a Channel enforce;

  • tips on how to pick a Device;

As presented in the introduction to programming a neutral-atom QPU, the first step to writing a Pulser program is the selection of a Device.

The Device is an object that stores all the physical constraints a quantum program written with Pulser should verify. The Device enforces that each operation added to the quantum program (i.e. the pulser.Sequence) respects its constraints.

Decision Diagram to select a Device for the computation

To choose a Device for your Sequence, the first question you should ask yourself is:

“How close do I want the physical constraints I program with to be to the QPU’s?”

If you want to program with the physical constraints of a QPU: Each QPU has an associated Device, which you can get from the cloud provider you use to access this QPU.

There are several reasons for which you might want to feel less constrained by the features currently supported by real QPUs. For instance, you might want to design an algorithm for a QPU having better performances (supporting more qubits, longer sequences, …) or hardware components that have not been installed.

Pulser enables you to define your own devices, but a Device object takes as input lots of parameters that have all to be defined. Therefore, for user convenience, Pulser provides:

  • Examples of typical physical devices in pulser.devices. Notably, pulser.AnalogDevice is an example of a QPU implementing an Ising Hamiltonian.

  • The possibility to define a device without some physical constraints using the VirtualDevice class. An example of such a virtual device is the MockDevice provided in the pulser.devices, which gives full liberty to write a quantum program. VirtualDevice is detailed in an advanced tutorial.

Reading through the Device’s specifications

Section titled “Reading through the Device’s specifications”

The second question you should ask yourself to choose your Device is:

“Do its constraints allow me to program my Sequence ?”

The device specifications are here to guide your choice. Here are all the parameters in a Device:

class pulser.devices._device_datacls.Device(name, dimensions, rydberg_level, min_atom_distance, max_atom_num, max_radial_distance, interaction_coeff_xy=None, supports_slm_mask=False, max_layout_filling=0.5, optimal_layout_filling=None, min_layout_traps=1, max_layout_traps=None, max_sequence_duration=None, max_runs=None, requires_layout=True, channel_ids=None, channel_objects=<factory>, dmm_objects=<factory>, default_noise_model=None, short_description='', pre_calibrated_layouts=<factory>, accepts_new_layouts=True)

Specifications of a neutral-atom device.

Each Device instance holds the characteristics of a physical device, which when associated with a pulser.Sequence condition its development.

Note

A Device instance is immutable and must have all of its parameters defined. For more unconstrained usage in emulations, it can be converted to a VirtualDevice through the Device.to_virtual() method.`

Parameters:
  • name (str) – The name of the device.

  • dimensions (Literal[2, 3]) – Whether it supports 2D or 3D arrays.

  • max_atom_num (int) – Maximum number of atoms supported in an array.

  • max_radial_distance (int) – The furthest away an atom can be from the center of the array (in μm).

  • min_atom_distance (float) – The closest together two atoms can be (in μm).

  • requires_layout (bool, default: True) – Whether the register used in the sequence must be created from a register layout. Only enforced in QPU execution.

  • accepts_new_layouts (bool, default: True) – Whether registers built from register layouts that are not already calibrated are accepted. Only enforced in QPU execution.

  • max_layout_filling (float, default: 0.5) – The largest fraction of a layout that can be filled with atoms.

  • optimal_layout_filling (float | None, default: None) – An optional value for the fraction of a layout that should be filled with atoms.

  • min_layout_traps (int, default: 1) – The minimum number of traps a layout can have.

  • max_layout_traps (int | None, default: None) – An optional value for the maximum number of traps a layout can have.

  • pre_calibrated_layouts (tuple[RegisterLayout, ...], default: <factory>) – RegisterLayout instances that are already available on the Device.

  • rydberg_level (int) – The value of the principal quantum number \(n\) when the Rydberg level used is of the form \(|nS_{1/2}, m_j = +1/2\rangle\).

  • interaction_coeff_xy (float | None, default: None) – \(C_3/\hbar\) (in \(rad \cdot \mu s^{-1} \cdot \mu m^3\)), which sets the van der Waals interaction strength between atoms in different Rydberg states. Needed only if there is a Microwave channel in the device. If unsure, 3700.0 is a good default value.

  • channel_objects (tuple[Channel, ...], default: <factory>) – The Channel subclass instances specifying each channel in the device.

  • channel_ids (tuple[str, ...] | None, default: None) – Custom IDs for each channel object. When defined, an ID must be given for each channel. If not defined, the IDs are generated internally based on the channels’ names and addressing.

  • dmm_objects (tuple[DMM, ...], default: <factory>) – The DMM subclass instances specifying each channel in the device. They are referenced by their order in the list, with the ID “dmm_[index in dmm_objects]”.

  • supports_slm_mask (bool, default: False) – Whether the device supports the SLM mask feature.

  • max_sequence_duration (int | None, default: None) – The maximum allowed duration for a sequence (in ns).

  • max_runs (int | None, default: None) – The maximum number of runs allowed on the device. Only used for backend execution.

  • default_noise_model (NoiseModel | None, default: None) – An optional noise model characterizing the default noise of the device. Can be used by emulator backends that support noise.

The Device is going to constrain the next steps of your quantum program:

  1. Some parameters are going to constrain the creation of your Register, and therefore, the interaction strength in the interaction Hamiltonian. Some of these parameters are:

    • dimensions

    • max_atom_num

    • max_radial_distance

    • min_atom_distance

  2. The rydberg_level determines the Ising interaction coefficient \(C_6\) of the Ising Hamiltonian. The quantity \(\frac{C_6}{\hbar}\) is accessible via the interaction_coeff attribute of the Device.

  3. The Channels in the channel_objects parameter are going to determine what Channels are available for the computation. Knowing what states you want to use in your computation, you can first check that they are among the Device.supported_states, then find the bases and their associated channel that enable to use these states using the conventions page.

  4. The max_sequence_duration constrains the duration of the Pulses you can add, and therefore the Hamiltonian describing the system can at most be defined between 0 and this value.

  5. The max_runs limits the number of runs a quantum program can be executed on the QPU. See the section on Backends to read more about this.

The third step to writing a Pulser program is the selection of Channels among the Device.

As a reminder, the selection of a Channel defines the interaction Hamiltonian and the driving Hamiltonian \(H^D\).

\[H^D(t) / \hbar = \frac{\Omega(t)}{2} e^{-i\phi} |a\rangle\langle b| + \frac{\Omega(t)}{2} e^{i\phi} |b\rangle\langle a| - \delta(t) |b\rangle\langle b|\]

The Channels available for selection are stored in the channels property of the Device, a dictionnary associating a channel_id to each Channel in channel_objects. For instance, AnalogDevice only contains one channel, the rydberg_global channel, which can be accessed with AnalogDevice.channels["rydberg_global"].

Reading through the Channel’s specifications

Section titled “Reading through the Channel’s specifications”

The Channel is defined by:

class pulser.channels.base_channel.Channel(addressing, max_abs_detuning, max_amp, min_retarget_interval=None, fixed_retarget_t=None, max_targets=None, clock_period=1, min_duration=1, max_duration=100000000, min_avg_amp=0, mod_bandwidth=None, custom_phase_jump_time=None, propagation_dir=None)

Base class of a hardware channel.

Not to be initialized itself, but rather through a child class and the Local or Global classmethods.

Parameters:
  • addressing (Literal['Global', 'Local']) – “Local” or “Global”.

  • max_abs_detuning (float | None) – Maximum possible detuning (in rad/µs), in absolute value.

  • max_amp (float | None) – Maximum pulse amplitude (in rad/µs).

  • min_retarget_interval (int | None, default: None) – Minimum time required between the ends of two target instructions (in ns).

  • fixed_retarget_t (int | None, default: None) – Time taken to change the target (in ns).

  • max_targets (int | None, default: None) – How many qubits can be addressed at once by the same beam.

  • clock_period (int, default: 1) – The duration of a clock cycle (in ns). The duration of a pulse or delay instruction is enforced to be a multiple of the clock cycle.

  • min_duration (int, default: 1) – The shortest duration an instruction can take.

  • max_duration (int | None, default: 100000000) – The longest duration an instruction can take.

  • min_avg_amp (float, default: 0) – The minimum average amplitude of a pulse (when not zero).

  • mod_bandwidth (float | None, default: None) – The modulation bandwidth at -3dB (50% reduction), in MHz.

  • custom_phase_jump_time (int | None, default: None) – An optional custom value for the phase jump time that overrides the default value estimated from the modulation bandwidth. It is not enforced in EOM mode.

  • propagation_dir (tuple[float, float, float] | None, default: None) – The propagation direction of the beam associated with the channel, given as a vector in 3D space.

Example

To create a channel targeting the ‘ground-rydberg’ transition globally, call Rydberg.Global(...).

The Channel is going to determine the computational basis used in the driving Hamiltonian, and what is the Hamiltonian each atom sees:

  • The type of the Channel defines the states that can be addressed by the driving Hamiltonian if this channel is picked. All the child classes of Channel can be found here.

  • The addressing of the Channel determines what atoms experience the driving Hamiltonian. In general, physical Channels have a Global addressability, which means that a Pulse added to this channel will implement the same driving Hamiltonian on all the atoms.

The Channel also set constraints on the next stage of your quantum program, the addition of Pulses:

  • the duration of the pulse is constrained by min_duration and max_duration, as well as clock_period (it has to be a multiple of the clock period).

  • the amplitude is limited by the maximum amplitude max_amp and min_avg_amp.

  • the detuning is limited by the maximum absolute detuning max_abs_det. It has to be between -max_abs_det and max_abs_det.

import pulser
print(pulser.AnalogDevice.specs)
A realistic device for analog sequence execution.

Register parameters:
 - Dimensions: 2D
 - Maximum number of atoms: 80
 - Maximum distance from origin: 38 µm
 - Minimum distance between neighbouring atoms: 5 μm

Layout parameters:
 - Requires layout: Yes
 - Accepts new layout: Yes
 - Minimal number of traps: 1
 - Maximum layout filling fraction: 0.5

Device parameters:
 - Rydberg level: 60
 - Ising interaction coefficient: 865723.02
 - Channels can be reused: No
 - Supported bases: ground-rydberg
 - Supported states: r, g
 - SLM Mask: No
 - Maximum sequence duration: 6000 ns
 - Maximum number of runs: 2000

Channels:
 - 'rydberg_global': Rydberg(addressing='Global', max_abs_detuning=125.66370614359172, max_amp=12.566370614359172, min_retarget_interval=None, fixed_retarget_t=None, max_targets=None, clock_period=4, min_duration=16, max_duration=100000000, min_avg_amp=0, mod_bandwidth=8, custom_phase_jump_time=None, eom_config=RydbergEOM(limiting_beam=<RydbergBeam.RED: 2>, max_limiting_amp=188.49555921538757, intermediate_detuning=2827.4333882308138, controlled_beams=(<RydbergBeam.BLUE: 1>,), mod_bandwidth=40, custom_buffer_time=240, multiple_beam_control=True, blue_shift_coeff=1.0, red_shift_coeff=1.0), propagation_dir=None)

The pulser.AnalogDevice only supports the \(\left|r\right>\) and \(\left|g\right>\) states, because it only supports one channel of type “Rydberg” (that can be declared using its name “rydberg_global”). It implements the Ising Hamiltonian:

\[\frac{H}{\hbar}(t) = \sum_{k=1}^N \left (\frac{\Omega(t)}{2} e^{-i\phi(t)} |g\rangle\langle r|_k + \frac{\Omega(t)}{2} e^{i\phi(t)} |r\rangle\langle g|_k - \delta(t) |r\rangle\langle r|_k(t) + \sum_{j<k}\frac{C_6}{\hbar R_{kj}^6} \hat{n}_k \hat{n}_j \right)\]

There:

  • The number of atoms \(N\) is constrained by the Maximum number of atoms.

  • The distance between the atoms \(R_{ij}\) is at least the Minimum distance between neighbouring atoms and at most twice the Maximum distance from origin. The distances are defined by placing the atoms in a 2D-plane.

  • \(C_6\) is the Ising interaction coefficient. It is defined from the Rydberg level (i.e. changing the Rydberg level would change the Ising interaction coefficient).

  • The "rydberg_global" being a "Global" channel, each pulse added to this channel is applied on all the atoms (the quantities \(\Omega\), \(\delta\), \(\phi\) are the same for each atom).

  • The Hamiltonian (and the pulses with their time-dependent quantities \(\Omega\), \(\delta\), \(\phi\)) can be defined between 0 and the minimum of the Maximum sequence duration and the maximum_duration of the channel.

  • The value of \(\Omega\) can go between 0 and max_amp and the value of \(\delta\) can go between -max_abs_detuning and max_abs_detuning.