Skip to content
Pasqal Documentation

Send a job to Fresnel

Fresnel is Pasqal’s state of the art neutral-atom QPU available on the cloud.

You can use the cloud SDK to send a job to Fresnel just like you did for an emulator in the previous section.

However there are a few hardware constraints to keep in mind before sending a job for execution on a real QPU. Some additional validation rules are checked on your sequence before it is scheduled:

  • the channels defined in your sequence must match with channels available on the device
  • your register must derive from a register layout
  • the register must verify the constraints of the hardware: minimum spacing, atom count, etc.

Get the Fresnel specs

The specifications of our QPUs are accessible via the SDK which will let you build a pulser Device object that you can use to design your pulser Sequence.

from pulser.json.abstract_repr.deserializer import deserialize_device
from pasqal_cloud import SDK
project_id = "your_project_id"# Replace this value by your project_id on the Pasqal platform.
username = "your_username"# Replace this value by your username or email on the Pasqal platform.
# Initialize the cloud client
sdk = SDK(username=username, project_id=project_id)
specs = sdk.get_device_specs_dict()
# Build a device object to describe the specs of our "Fresnel" QPU
device = deserialize_device(specs["FRESNEL"])

Inspect the newly build device object, this describes all the constraints of the hardware and will help you design your sequence. Refer to the pulser documentation on devices (external) for more details.

Build your register

You need to define both a register and a layout before proceeding. The register specify where atoms will be arranged, while the layout specifies the positioning of traps necessary to capture and structure these atoms within the register.

For details on layouts, see the Pulser documentation (external) .

Pre-calibrated layouts

The device defines a list of pre-calibrated layouts (external) . You can build your register out of one of these layouts.

This is the recommended option because it will improve the performance of the QPU

  • Option 1: Define your register using pre-calibrated layouts:

    Inspect the layouts available on Fresnel and define your register from this layout. Check the pulser documentation for more information on how to do that.

    Example:

    # let's say we are interested in the first layout available on the device
    layout = device.pre_calibrated_layouts[0]
    # Select traps 1, 3 and 5 of the layout to define the register
    traps = [1,3,5]
    register = layout.define_register(*traps)
    # You can draw the resulting register to verify it matches your expectations
    register.draw()

Arbitrary layouts

If pre-calibrated layouts do not satisfy the requirements of your experiment, you can create a custom layout.

For any given arbitrary register, a neutral-atom QPU will place traps according to the layout, which must then undergo calibration. Since each calibration requires time, it is generally advisable to reuse an existing calibrated layout whenever possible

  • Option 2: Automatically derive a layout from your defined register

    This option allows for the automatic generation of a layout based on a specified register. However, for large registers, this process may yield sub-optimal solutions due to limitations in the algorithm used to create the layout.

    qubits = {
    "q0": (0, 0),
    "q1": (0, 10),
    "q2": (8, 2),
    "q3": (1, 15),
    "q4": (-10, -3),
    "q5": (-8, 5),
    }
    register = Register(qubits).with_automatic_layout(device)
  • Option 3: Define your register using a manually defined layout

    • Create an arbitrary layout with 20 traps randomly positioned in a 2D plane
    import numpy as np
    # Generating random coordinates
    np.random.seed(301122) # Keeps results consistent between runs
    traps = np.random.randint(0, 30, size=(20, 2))
    traps = traps - np.mean(traps, axis=0)
    # Creating the layout
    layout = RegisterLayout(traps, slug="random_20")
    • Define your register with specific trap IDs
    trap_ids = [4, 8, 19, 0]
    reg1 = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"])
    reg1.draw()

Design your sequence

Once you have defined your register you can now create a Sequence object using the Fresnel device and register. Refer to the Pulser documentation (external) to design your sequence.

# Assuming `device` is the pulser Device object
# And `register` is the register defined from one of the layout of the device
# Build a pulser sequence
seq = Sequence(register, device)
# Now design your sequence by defining variables, adding pulses, ...

Send the job for execution

Once you have designed your sequence, you can serialize it and send it for execution.

Before sending a job to Fresnel you can try validating your sequence by targeting an emulator backend first. You can do this by targeting our device EMU_FRESNEL within the SDK, your sequence will be run on an emulator but is validated server-side using the same criteria as a job scheduled for Fresnel.

from pasqal_cloud import SDK, EmulatorType
sdk = SDK(
...
)
jobs = [{"runs": 100}]
batch = sdk.create_batch(
seq.to_abstract_repr(), jobs, emulator=EmulatorType.EMU_FRESNEL
)

If your batch is successful after this, it should be ready for execution on Fresnel.

# Create a batch of 1 job with 100 runs
jobs = [{"runs": 100}]
# Simply omit the `emulator` arguments to target Fresnel
fresnel_batch = sdk.create_batch(seq.to_abstract_repr(), jobs)