{ "cells": [ { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "# Hardware specifications" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "*What you will learn:*\n", "- what is a Device and why is a Device needed;\n", "- what are the available devices and where to find them;\n", "- what do a Device and a Channel enforce; \n", "- tips on how to pick a `Device`;" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "## The `Device`" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "As presented in the [introduction to programming a neutral-atom QPU](programming.md#writing-a-pulser-program), the first step to writing a Pulser program is [the selection of a Device](programming.md#pick-a-device). \n", "\n", "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. " ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "\n", "**Important note**:\n", "\n", "\n", "The `Device` represents the physics of a neutral-atom QPU but is not the QPU itself. A QPU must be accessed via a `Backend`, which is presented [in this section](./tutorials/backends.nblink).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "### Choosing a `Device`" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "\"Decision\n", "
\n" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "To choose a `Device` for your `Sequence`, the first question you should ask yourself is:\n", "\n", "
\"How close do I want the physical constraints I program with to be to the QPU's?\"
\n", "\n", "**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](tutorials/qpu.nblink#1.-Preparation-for-execution-on-QPUBackend).\n", "\n", "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.\n", "\n", "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:\n", "\n", "- **Examples of typical physical devices** in `pulser.devices`. Notably, `pulser.AnalogDevice` is an example of a QPU implementing an [Ising Hamiltonian](./programming.md#ising-hamiltonian).\n", "\n", "- **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](./tutorials/virtual_devices.nblink)." ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "\n", "**Note**:\n", "\n", "The selection of a device in a Pulser program does not enforce any choice on the [backend](tutorials/backends.nblink). No matter the device you used to program your `Sequence`, you can always submit it to any QPU: if the values of the `Sequence` match the constraints of the `QPU`, it will be executed. \n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "\n", "\n", "**Tip**:\n", "\n", "It is possible to change the device with which a `Sequence` was built, by using `Sequence.with_new_device`. This is especially useful to check if the values of the `Sequence` match the constraints of the `QPU` prior to submitting to the `QPU`. For instance, you could have built your `Sequence` with an example of a `Device` like `AnalogDevice`, and now want to run it on a QPU, or the specifications of your QPU might have changed between your design and submission.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "### Reading through the `Device`'s specifications" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "The second question you should ask yourself to choose your `Device` is: \n", "\n", "
\"Do its constraints allow me to program my `Sequence` ?\"
\n", "\n", "The device specifications are here to guide your choice. Here are all the parameters in a `Device`:" ] }, { "cell_type": "raw", "metadata": { "editable": true, "raw_mimetype": "text/restructuredtext", "slideshow": { "slide_type": "" }, "tags": [], "vscode": { "languageId": "raw" } }, "source": [ ".. autoclass:: pulser.devices._device_datacls.Device\n", " :noindex:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note**:\n", "\n", "The `Device` object has many useful properties and methods, that you can check in the [API documentation](./apidoc/_autosummary/pulser.devices.Device.rst#pulser.devices.Device). For instance, it is possible to display some of the specifications of the `Device` with `Device.specs`. See an example with `AnalogDevice.specs` [in the section below](#the-analogdevice).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "### Tips on `Device` selection" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "The `Device` is going to constrain the next steps of your [quantum program](./programming.md#writing-a-pulser-program):\n", "\n", "1) Some parameters are going to constrain [the creation of your Register](./programming.md#create-the-register), and therefore, the [interaction strength in the interaction Hamiltonian](programming.md#interaction-hamiltonian). Some of these parameters are:\n", " - `dimensions`\n", " - `max_atom_num`\n", " - `max_radial_distance`\n", " - `min_atom_distance`\n", "\n", "2) The `rydberg_level` determines the [Ising interaction coefficient](./programming.md#ising-hamiltonian) $C_6$ of the Ising Hamiltonian. The quantity $\\frac{C_6}{\\hbar}$ is accessible via the `interaction_coeff` attribute of the `Device`.\n", "\n", "3) The `Channels` in the `channel_objects` parameter are going to determine what [Channels are available for the computation](programming.md#pick-the-channels). 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](conventions.md#bases).\n", "\n", "4) The `max_sequence_duration` constrains the duration of the [Pulses you can add](programming.md#add-the-pulses), and therefore the Hamiltonian describing the system can at most be defined between 0 and this value.\n", "\n", "5) The `max_runs` limits the number of runs a quantum program can be executed on the QPU. See [the section on QPU execution](./tutorials/qpu.nblink) to read more about this." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note**:\n", "\n", "If the Device associated with a QPU has `requires_layout=True`, then you have to define the `Register` from a layout. This adds more constraints to the creation of your `Register`, and is [presented in an advanced tutorial](./tutorials/reg_layouts.nblink).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "## The `Channels`" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "The third step to writing a Pulser program is [the selection of Channels among the Device](programming.md#pick-a-device).\n", "\n", "As a reminder, the selection of a `Channel` defines the [interaction Hamiltonian](programming.md#interaction-hamiltonian) and [the driving Hamiltonian](programming.md#driving-hamiltonian) $H^D$.\n", "\n", "$$\n", "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|\n", "$$\n", "\n", "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\"]`. " ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "### Reading through the `Channel`'s specifications" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "The `Channel` is defined by:" ] }, { "cell_type": "raw", "metadata": { "editable": true, "raw_mimetype": "text/restructuredtext", "slideshow": { "slide_type": "" }, "tags": [], "vscode": { "languageId": "raw" } }, "source": [ ".. autoclass:: pulser.channels.base_channel.Channel\n", " :noindex:" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "### Tips on `Channel` selection" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "The `Channel` is going to determine the computational basis used in the driving Hamiltonian, and what is the Hamiltonian each atom sees:\n", "\n", "- The type of the `Channel` defines the [states](conventions.md#bases) that can be addressed by the [driving Hamiltonian](programming.md#driving-hamiltonian) if this channel is picked. All the child classes of `Channel` can be found [here](./apidoc/_autosummary/pulser.channels.rst).\n", "- 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.\n", "\n", "The `Channel` also set constraints on the next stage of your quantum program, the addition of Pulses:\n", "- 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).\n", "- the **amplitude** is limited by the maximum amplitude `max_amp` and `min_avg_amp`.\n", "- the **detuning** is limited by the maximum absolute detuning `max_abs_det`. It has to be between -`max_abs_det` and `max_abs_det`." ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "\n", "**Note**:\n", "\n", "The modulation bandwidth `mod_bandwidth` impacts the duration, the amplitude, the detuning and the phase of the Pulses. It is a more advanced feature explained [in this tutorial](./tutorials/output_mod_eom.nblink).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "## The `AnalogDevice`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "outputs": [], "source": [ "import pulser\n", "\n", "print(pulser.AnalogDevice.specs)" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "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](programming.md#ising-hamiltonian): \n", "\n", "$$\\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