Devices and Virtual Devices
[1]:
import numpy as np
from dataclasses import replace
from pulser.devices import Device, VirtualDevice, AnalogDevice, MockDevice
from pulser.channels import Rydberg, Raman, Microwave
from pulser import Pulse, Sequence, Register
Physical devices
pulser.devices
.Device
class. They are constrained by physical considerations and all their parameters are defined.As an example, we present below the specifications of the physical device AnalogDevice
, which can be accessed via the Device.print_specs()
method.
[2]:
# Display AnalogDevice's specifications
AnalogDevice.print_specs()
---------------------------
AnalogDevice Specifications
---------------------------
Register parameters:
- Dimensions: 2D
- Rydberg level: 60
- Maximum number of atoms: 25
- Maximum distance from origin: 35 μm
- Minimum distance between neighbouring atoms: 5 μm
- Maximum layout filling fraction: 0.5
- SLM Mask: No
- Maximum sequence duration: 4000 ns
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, 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)
Virtual Devices
Converting a Device into a Virtual Device
However, we sometimes want to perform the computations on a more permissive device, a device that would have more dimensions or more atoms for instance, or more types of channels. This can be done on an emulator behaving like a device. The VirtualDevice
class is useful to define such an emulator, a virtual device.
Let’s start by configuring a virtual device having the same parameters as AnalogDevice
. To do this, we use the Device.to_virtual()
method that creates a virtual device from a physical one.
[3]:
# Converting the Device object in a VirtualDevice object
VirtualAnalog = AnalogDevice.to_virtual()
print(VirtualAnalog)
VirtualDevice(name='AnalogDevice', dimensions=2, rydberg_level=60, min_atom_distance=5, max_atom_num=25, max_radial_distance=35, 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=4000, max_runs=2000, requires_layout=True, reusable_channels=False, channel_ids=('rydberg_global',), channel_objects=(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, 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),), dmm_objects=(), default_noise_model=None)
Changing parameters of a virtual device with dataclasses.replace()
dataclasses.replace()
can be used to create a new VirtualDevice
having some parameters changed.AnalogDevice
but allowing working in 3 dimensions.[4]:
# Adding a dimension to the emulator
VirtualAnalog3D = replace(VirtualAnalog, dimensions=3)
print(VirtualAnalog3D)
VirtualDevice(name='AnalogDevice', dimensions=3, rydberg_level=60, min_atom_distance=5, max_atom_num=25, max_radial_distance=35, 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=4000, max_runs=2000, requires_layout=True, reusable_channels=False, channel_ids=('rydberg_global',), channel_objects=(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, 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),), dmm_objects=(), default_noise_model=None)
Unrealistic parameters of a virtual device
VirtualDevice
, some are called unrealistic as they do not refer to something physically implementable.[5]:
# Changing the Rydberg level
VirtualAnalog3D.change_rydberg_level(61)
print(VirtualAnalog3D)
VirtualDevice(name='AnalogDevice', dimensions=3, rydberg_level=61, min_atom_distance=5, max_atom_num=25, max_radial_distance=35, 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=4000, max_runs=2000, requires_layout=True, reusable_channels=False, channel_ids=('rydberg_global',), channel_objects=(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, 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),), dmm_objects=(), default_noise_model=None)
[6]:
# Enable the multiple declaration of a channel in a sequence
VirtualAnalog3D = replace(VirtualAnalog3D, reusable_channels=True)
# Creating a square register
reg = Register.square(4, spacing=5) # 4x4 array with atoms 5 um apart
# Building a sequence with the register and the virtual device
seq = Sequence(reg, VirtualAnalog3D)
# Declare twice the channel "rydberg_global"
seq.declare_channel("ch0", "rydberg_global")
seq.declare_channel("ch1", "rydberg_global")
# Show the declared channels
print(seq.declared_channels)
{'ch0': 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, 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), 'ch1': 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, 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)}
Building your own virtual device
More generally, the VirtualDevice
class is more permissive than the Device
class. In fact, some parameters can be left undefined and simpler devices can be built with a VirtualDevice
instance. A virtual device only needs a name
, a dimension
and a rydberg_level
to be initialized.
[7]:
BasicVirtualDevice = VirtualDevice(
name="BasicMockDevice",
dimensions=2,
rydberg_level=61,
)
print(BasicVirtualDevice)
VirtualDevice(name='BasicMockDevice', dimensions=2, rydberg_level=61, min_atom_distance=0, max_atom_num=None, max_radial_distance=None, interaction_coeff_xy=None, supports_slm_mask=True, 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=False, reusable_channels=True, channel_ids=(), channel_objects=(), dmm_objects=(DMM(addressing='Global', max_abs_detuning=None, max_amp=0, 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, eom_config=None, propagation_dir=None, bottom_detuning=None, total_bottom_detuning=None),), default_noise_model=None)
Defining the channels of your device
channel_objects
. Their IDs can be defined in channel_ids
, but if no IDs are provided, they will be automatically generated as {channeltype}_adressing
.Rydberg.Global
channel is automatically named rydberg_global
in the specifications of AnalogDevice
.[8]:
# This basic device can be used for digital quantum computing
DigitalQCVirtualDevice = replace(
BasicVirtualDevice,
channel_ids=(
"ryd_loc",
"ram_loc",
),
channel_objects=(
Rydberg.Local(None, None, max_duration=None),
Raman.Local(None, None, max_duration=None),
),
)
print(DigitalQCVirtualDevice)
VirtualDevice(name='BasicMockDevice', dimensions=2, rydberg_level=61, min_atom_distance=0, max_atom_num=None, max_radial_distance=None, interaction_coeff_xy=None, supports_slm_mask=True, 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=False, reusable_channels=True, channel_ids=('ryd_loc', 'ram_loc'), channel_objects=(Rydberg(addressing='Local', max_abs_detuning=None, max_amp=None, min_retarget_interval=0, fixed_retarget_t=0, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None), Raman(addressing='Local', max_abs_detuning=None, max_amp=None, min_retarget_interval=0, fixed_retarget_t=0, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None)), dmm_objects=(DMM(addressing='Global', max_abs_detuning=None, max_amp=0, 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, eom_config=None, propagation_dir=None, bottom_detuning=None, total_bottom_detuning=None),), default_noise_model=None)
A built-in example of a virtual device: the MockDevice
Actually, there exists a virtual device having all the channels already implemented, with no constraints on the number of atoms, the distance between them. This virtual device is the MockDevice
.
[9]:
MyMockDevice = replace(MockDevice, name="MyMockDevice")
print(MyMockDevice)
VirtualDevice(name='MyMockDevice', dimensions=3, rydberg_level=70, min_atom_distance=0.0, max_atom_num=None, max_radial_distance=None, interaction_coeff_xy=3700.0, supports_slm_mask=True, 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=False, reusable_channels=True, channel_ids=('rydberg_global', 'rydberg_local', 'raman_global', 'raman_local', 'mw_global'), channel_objects=(Rydberg(addressing='Global', max_abs_detuning=None, max_amp=None, min_retarget_interval=None, fixed_retarget_t=None, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None), Rydberg(addressing='Local', max_abs_detuning=None, max_amp=None, min_retarget_interval=0, fixed_retarget_t=0, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None), Raman(addressing='Global', max_abs_detuning=None, max_amp=None, min_retarget_interval=None, fixed_retarget_t=None, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None), Raman(addressing='Local', max_abs_detuning=None, max_amp=None, min_retarget_interval=0, fixed_retarget_t=0, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None), Microwave(addressing='Global', max_abs_detuning=None, max_amp=None, min_retarget_interval=None, fixed_retarget_t=None, max_targets=None, clock_period=1, min_duration=1, max_duration=None, min_avg_amp=0, mod_bandwidth=None, eom_config=None, propagation_dir=None)), dmm_objects=(DMM(addressing='Global', max_abs_detuning=None, max_amp=0, 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, eom_config=None, propagation_dir=None, bottom_detuning=None, total_bottom_detuning=None),), default_noise_model=None)