Skip to content

Commit

Permalink
Merge pull request #621 from pasqal-io/release/v0.16.0
Browse files Browse the repository at this point in the history
Release v0.16.0
  • Loading branch information
a-corni authored Dec 4, 2023
2 parents e1dcf47 + 53eab97 commit 394567d
Show file tree
Hide file tree
Showing 61 changed files with 5,829 additions and 5,329 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[flake8]
docstring-convention = google
exclude = ./build, ./docs
exclude = ./build, ./docs, ./__venv__
extend-ignore =
# D105 Missing docstring in magic method
D105,
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Upload Release Package to PyPI
name: Release to PyPI

on:
release:
Expand All @@ -7,6 +7,10 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
environment: release
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- name: Check out Pulser
uses: actions/checkout@v3
Expand All @@ -19,15 +23,14 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
pip install setuptools wheel
- name: Build packages
shell: bash
run: ./.github/scripts/package.sh
- name: Publish to TestPyPI
env:
TWINE_USERNAME: ${{ secrets.TESTPYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TESTPYPI_PASSWORD }}
run: twine upload --repository testpypi dist/*
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
- name: Install from TestPyPI
timeout-minutes: 5
shell: bash
Expand All @@ -44,10 +47,7 @@ jobs:
grep -e pytest dev_requirements.txt | sed 's/ //g' | xargs pip install
pytest
- name: Publish to PyPI
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: twine upload dist/*
uses: pypa/gh-action-pypi-publish@release/v1
- name: Confirm deployment
timeout-minutes: 5
shell: bash
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.vscode
.python-version
.pytest_cache/
.mypy_cache/
.idea/
.coverage
.spyproject/
Expand All @@ -15,3 +16,4 @@ docs/build/
dist/
env*
*.egg-info/
__venv__/
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 22.1.0
rev: 23.10.1
hooks:
- id: black-jupyter

Expand All @@ -10,7 +10,7 @@ repos:
- id: flake8

- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
name: isort (python)
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.15.3
0.16.0
5 changes: 3 additions & 2 deletions docs/source/apidoc/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ Physical Devices
Each `Device`` instance holds the characteristics of a physical device,
which when associated with a :class:`pulser.Sequence` condition its development.

.. autodata:: pulser.devices.Chadoq2

.. autodata:: pulser.devices.AnalogDevice

.. autodata:: pulser.devices.DigitalAnalogDevice



Channels
---------------------
Expand Down
6 changes: 3 additions & 3 deletions docs/source/intro_rydberg_blockade.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"outputs": [],
"source": [
"from pulser import Register\n",
"from pulser.devices import Chadoq2\n",
"from pulser.devices import DigitalAnalogDevice\n",
"\n",
"layers = 3\n",
"reg = Register.hexagon(layers)\n",
Expand Down Expand Up @@ -105,7 +105,7 @@
"\n",
"pi_pulse = Pulse.ConstantDetuning(BlackmanWaveform(duration, np.pi), 0.0, 0.0)\n",
"\n",
"seq = Sequence(reg, Chadoq2)\n",
"seq = Sequence(reg, DigitalAnalogDevice)\n",
"\n",
"seq.declare_channel(\"ryd\", \"rydberg_local\", \"atom0\")\n",
"\n",
Expand Down Expand Up @@ -169,7 +169,7 @@
" reg = Register.rectangle(1, 2, spacing=R, prefix=\"atom\")\n",
"\n",
" # Pulse Sequence\n",
" seq = Sequence(reg, Chadoq2)\n",
" seq = Sequence(reg, DigitalAnalogDevice)\n",
" seq.declare_channel(\"ryd\", \"rydberg_local\", \"atom0\")\n",
" seq.add(pi_pulse, \"ryd\")\n",
" seq.target(\"atom1\", \"ryd\")\n",
Expand Down
10 changes: 8 additions & 2 deletions pulser-core/pulser/backend/qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
class QPUBackend(RemoteBackend):
"""Backend for sequence execution on a QPU."""

def run(self, job_params: list[JobParams] | None = None) -> RemoteResults:
def run(
self, job_params: list[JobParams] | None = None, wait: bool = False
) -> RemoteResults:
"""Runs the sequence on the remote QPU and returns the result.
Args:
Expand All @@ -34,6 +36,10 @@ def run(self, job_params: list[JobParams] | None = None) -> RemoteResults:
is parametrized, the values for all the variables necessary
to build the sequence must be given in it's own mapping, for
each job, under the 'variables' field.
wait: Whether to wait until the results of the jobs become
available. If set to False, the call is non-blocking and the
obtained results' status can be checked using their `status`
property.
Returns:
The results, which can be accessed once all sequences have been
Expand All @@ -55,7 +61,7 @@ def run(self, job_params: list[JobParams] | None = None) -> RemoteResults:
f"device ({max_runs})" + suffix
)
results = self._connection.submit(
self._sequence, job_params=job_params
self._sequence, job_params=job_params, wait=wait
)
return cast(RemoteResults, results)

Expand Down
2 changes: 1 addition & 1 deletion pulser-core/pulser/backend/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class RemoteConnection(ABC):

@abstractmethod
def submit(
self, sequence: Sequence, **kwargs: Any
self, sequence: Sequence, wait: bool = False, **kwargs: Any
) -> RemoteResults | tuple[RemoteResults, ...]:
"""Submit a job for execution."""
pass
Expand Down
91 changes: 78 additions & 13 deletions pulser-core/pulser/channels/dmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
"""Defines the detuning map modulator."""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Literal, Optional
import warnings
from dataclasses import dataclass, field, fields
from typing import Any, Literal, Optional

import numpy as np

from pulser.channels.base_channel import Channel
from pulser.json.utils import get_dataclass_defaults
from pulser.pulse import Pulse
from pulser.register.weight_maps import DetuningMap

OPTIONAL_ABSTR_DMM_FIELDS = ["total_bottom_detuning"]


@dataclass(init=True, repr=False, frozen=True)
Expand All @@ -31,16 +36,20 @@ class DMM(Channel):
(of zero amplitude and phase). These Pulses are locally modulated by the
weights of a `DetuningMap`, thus providing a local control over the
detuning. The detuning of the pulses added to a DMM has to be negative,
between 0 and `bottom_detuning`. Channel targeting the transition between
the ground and rydberg states, thus encoding the 'ground-rydberg' basis.
between 0 and `bottom_detuning`, and the sum of the weights multiplied by
that detuning has to be below `total_bottom_detuning`. Channel targeting
the transition between the ground and rydberg states, thus encoding the
'ground-rydberg' basis.
Note:
The protocol to add pulses to the DMM Channel is by default
"no-delay".
Args:
bottom_detuning: Minimum possible detuning (in rad/µs), must be below
zero.
bottom_detuning: Minimum possible detuning per atom (in rad/µs),
must be below zero.
total_bottom_detuning: Minimum possible detuning distributed on all
atoms (in rad/µs), must be below zero.
clock_period: 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.
Expand All @@ -51,7 +60,8 @@ class DMM(Channel):
MHz.
"""

bottom_detuning: Optional[float] = field(default=None, init=True)
bottom_detuning: float | None = None
total_bottom_detuning: float | None = None
addressing: Literal["Global"] = field(default="Global", init=False)
max_abs_detuning: Optional[float] = field(default=None, init=False)
max_amp: float = field(default=0, init=False)
Expand All @@ -63,6 +73,17 @@ def __post_init__(self) -> None:
super().__post_init__()
if self.bottom_detuning and self.bottom_detuning > 0:
raise ValueError("bottom_detuning must be negative.")
if self.total_bottom_detuning:
if self.total_bottom_detuning > 0:
raise ValueError("total_bottom_detuning must be negative.")
if (
self.bottom_detuning
and self.bottom_detuning < self.total_bottom_detuning
):
raise ValueError(
"total_bottom_detuning must be lower than"
" bottom_detuning."
)

@property
def basis(self) -> Literal["ground-rydberg"]:
Expand All @@ -73,26 +94,70 @@ def _undefined_fields(self) -> list[str]:
optional = [
"bottom_detuning",
"max_duration",
# TODO: "total_bottom_detuning"
]
return [field for field in optional if getattr(self, field) is None]

def validate_pulse(self, pulse: Pulse) -> None:
"""Checks if a pulse can be executed in this DMM.
def is_virtual(self) -> bool:
"""Whether the channel is virtual (i.e. partially defined)."""
virtual_dmm = bool(self._undefined_fields())
if not virtual_dmm and self.total_bottom_detuning is None:
warnings.warn(
"From v0.17 and onwards, `total_bottom_detuning` must be"
" defined to define a physical DMM.",
DeprecationWarning,
)
return virtual_dmm

def validate_pulse(
self,
pulse: Pulse,
detuning_map: DetuningMap = DetuningMap(
trap_coordinates=[(0, 0)], weights=[1.0]
),
) -> None:
"""Checks if a pulse can be executed via this DMM on a DetuningMap.
Args:
pulse: The pulse to validate.
detuning_map: The detuning map on which the pulse is applied
(defaults to a detuning map with weight 1.0).
"""
super().validate_pulse(pulse)
round_detuning = np.round(pulse.detuning.samples, decimals=6)
# Check that detuning is negative
if np.any(round_detuning > 0):
raise ValueError("The detuning in a DMM must not be positive.")
if self.bottom_detuning is not None and np.any(
round_detuning < self.bottom_detuning
# Check that detuning on each atom is above bottom_detuning
min_round_detuning = np.min(round_detuning)
if (
self.bottom_detuning is not None
and np.max(detuning_map.weights) * min_round_detuning
< self.bottom_detuning
):
raise ValueError(
"The detuning goes below the bottom detuning "
f"of the DMM ({self.bottom_detuning} rad/µs)."
"The detunings on some atoms go below the local bottom "
f"detuning of the DMM ({self.bottom_detuning} rad/µs)."
)
# Check that distributed detuning is above total_bottom_detuning
if (
self.total_bottom_detuning is not None
and np.sum(detuning_map.weights) * min_round_detuning
< self.total_bottom_detuning
):
raise ValueError(
"The applied detuning goes below the total bottom detuning "
f"of the DMM ({self.total_bottom_detuning} rad/µs)."
)

def _to_abstract_repr(self, id: str) -> dict[str, Any]:
all_fields = fields(self)
defaults = get_dataclass_defaults(all_fields)
params = super()._to_abstract_repr(id)
for p in OPTIONAL_ABSTR_DMM_FIELDS:
if params[p] == defaults[p]:
params.pop(p, None)
return params


def _dmm_id_from_name(dmm_name: str) -> str:
Expand Down
8 changes: 7 additions & 1 deletion pulser-core/pulser/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
from typing import TYPE_CHECKING

from pulser.devices._device_datacls import Device, VirtualDevice
from pulser.devices._devices import AnalogDevice, Chadoq2, IroiseMVP
from pulser.devices._devices import (
AnalogDevice,
Chadoq2,
DigitalAnalogDevice,
IroiseMVP,
)
from pulser.devices._mock_device import MockDevice

# Registers which devices can be used to avoid definition of custom devices
Expand All @@ -27,4 +32,5 @@
Chadoq2,
IroiseMVP,
AnalogDevice,
DigitalAnalogDevice,
)
Loading

0 comments on commit 394567d

Please sign in to comment.