Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/pasqal-io/Pulser into bu…
Browse files Browse the repository at this point in the history
…mp_to_v0.16dev3
  • Loading branch information
a-corni committed Nov 20, 2023
2 parents 6c9fec1 + 9f9a17a commit 3135b67
Show file tree
Hide file tree
Showing 16 changed files with 78 additions and 50 deletions.
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
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
8 changes: 4 additions & 4 deletions pulser-core/pulser/json/abstract_repr/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import inspect
import json
from collections.abc import Iterable
from collections.abc import Collection
from itertools import chain
from typing import TYPE_CHECKING, Any, Union, cast

Expand Down Expand Up @@ -156,16 +156,16 @@ def serialize_abstract_sequence(
res["variables"][var.name]["value"] = [var.dtype()] * var.size

def unfold_targets(
target_ids: QubitId | Iterable[QubitId],
target_ids: QubitId | Collection[QubitId],
) -> QubitId | list[QubitId]:
if isinstance(target_ids, (int, str)):
return target_ids

targets = list(cast(Iterable, target_ids))
targets = list(cast(Collection, target_ids))
return targets if len(targets) > 1 else targets[0]

def convert_targets(
target_ids: Union[QubitId, Iterable[QubitId]],
target_ids: Union[QubitId, Collection[QubitId]],
force_list_out: bool = False,
) -> Union[int, list[int]]:
target_array = np.array(unfold_targets(target_ids))
Expand Down
11 changes: 6 additions & 5 deletions pulser-core/pulser/register/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,6 @@ def max_connectivity(
f" ({device.max_atom_num})."
)

if not device.min_atom_distance > 0.0:
raise NotImplementedError(
"Maximum connectivity layouts are not well defined for a "
f"device with 'min_atom_distance={device.min_atom_distance}'."
)
# Default spacing or check minimal distance
if spacing is None:
spacing = device.min_atom_distance
Expand All @@ -268,6 +263,12 @@ def max_connectivity(
" distance supported by this device"
f" ({device.min_atom_distance})."
)
if spacing <= 0.0:
# spacing is None or 0.0, device.min_atom_distance is 0.0
raise NotImplementedError(
"Maximum connectivity layouts are not well defined for a "
"device with 'min_atom_distance=0.0'."
)

coords = patterns.triangular_hex(n_qubits) * spacing

Expand Down
29 changes: 17 additions & 12 deletions pulser-core/pulser/sequence/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import json
import os
import warnings
from collections.abc import Iterable, Mapping
from collections.abc import Collection, Mapping
from typing import (
Any,
Generic,
Expand Down Expand Up @@ -529,7 +529,7 @@ def _set_slm_mask_dmm(self, dmm_id: str, targets: set[QubitId]) -> None:

@seq_decorators.store
def config_slm_mask(
self, qubits: Iterable[QubitId], dmm_id: str = "dmm_0"
self, qubits: Collection[QubitId], dmm_id: str = "dmm_0"
) -> None:
"""Setup an SLM mask by specifying the qubits it targets.
Expand All @@ -545,7 +545,7 @@ def config_slm_mask(
pulse added after this operation.
Args:
qubits: Iterable of qubit ID's to mask during the first global
qubits: Collection of qubit ID's to mask during the first global
pulse of the sequence.
dmm_id: Id of the DMM channel to use in the device.
"""
Expand Down Expand Up @@ -846,7 +846,7 @@ def declare_channel(
self,
name: str,
channel_id: str,
initial_target: Optional[Union[QubitId, Iterable[QubitId]]] = None,
initial_target: Optional[Union[QubitId, Collection[QubitId]]] = None,
) -> None:
"""Declares a new channel to the Sequence.
Expand Down Expand Up @@ -901,7 +901,7 @@ def declare_channel(
try:
cond = any(
isinstance(t, Parametrized)
for t in cast(Iterable, initial_target)
for t in cast(Collection, initial_target)
)
except TypeError:
cond = isinstance(initial_target, Parametrized)
Expand Down Expand Up @@ -930,7 +930,7 @@ def declare_channel(
else:
# "_target" call is not saved
self._target(
cast(Union[Iterable, QubitId], initial_target), name
cast(Union[Collection, QubitId], initial_target), name
)

# Manually store the channel declaration as a regular call
Expand Down Expand Up @@ -1319,14 +1319,14 @@ def add_dmm_detuning(
@seq_decorators.store
def target(
self,
qubits: Union[QubitId, Iterable[QubitId]],
qubits: Union[QubitId, Collection[QubitId]],
channel: str,
) -> None:
"""Changes the target qubit of a 'Local' channel.
Args:
qubits: The new target for this channel. Must correspond to a
qubit ID in device or an iterable of qubit IDs, when
qubit ID in device or a collection of qubit IDs, when
multi-qubit addressing is possible.
channel: The channel's name provided when declared. Must be
a channel with 'Local' addressing.
Expand All @@ -1336,14 +1336,14 @@ def target(
@seq_decorators.store
def target_index(
self,
qubits: Union[int, Iterable[int], Parametrized],
qubits: Union[int, Collection[int], Parametrized],
channel: str,
) -> None:
"""Changes the target qubit of a 'Local' channel.
Args:
qubits: The new target for this channel. Must correspond to a
qubit index or an iterable of qubit indices, when multi-qubit
qubit index or an collection of qubit indices, when multi-qubit
addressing is possible.
A qubit index is a number between 0 and the number of qubits.
It is then converted to a Qubit ID using the order in which
Expand Down Expand Up @@ -1993,21 +1993,26 @@ def _add(
@seq_decorators.block_if_measured
def _target(
self,
qubits: Union[Iterable[QubitId], QubitId, Parametrized],
qubits: Union[Collection[QubitId], QubitId, Parametrized],
channel: str,
_index: bool = False,
) -> None:
self._validate_channel(channel, block_eom_mode=True)
channel_obj = self._schedule[channel].channel_obj
try:
qubits_set = (
set(cast(Iterable, qubits))
set(cast(Collection, qubits))
if not isinstance(qubits, str)
else {qubits}
)
except TypeError:
qubits_set = {qubits}

if not qubits_set:
raise ValueError(
"Need at least one qubit to target but none were given."
)

if channel_obj.addressing != "Local":
raise ValueError("Can only choose target of 'Local' channels.")
elif (
Expand Down
12 changes: 6 additions & 6 deletions pulser-core/pulser/waveforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,12 +805,12 @@ def duration(self) -> int:
@cached_property
def _samples(self) -> np.ndarray:
"""The value at each time step that describes the waveform."""
return cast(
np.ndarray,
np.round(
self._interp_func(np.arange(self._duration)), decimals=9
), # Rounds to the order of Hz
)
samples = self._interp_func(np.arange(self._duration))
value_range = np.max(np.abs(samples))
decimals = int(
min(np.finfo(samples.dtype).precision - np.log10(value_range), 9)
) # Reduces decimal values below 9 for large ranges
return cast(np.ndarray, np.round(samples, decimals=decimals))

@property
def interp_function(
Expand Down
7 changes: 6 additions & 1 deletion pulser-pasqal/pulser_pasqal/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(
)

def run(
self, job_params: list[JobParams] | None = None
self, job_params: list[JobParams] | None = None, wait: bool = False
) -> RemoteResults | tuple[RemoteResults, ...]:
"""Executes on the emulator backend through the Pasqal Cloud.
Expand All @@ -68,6 +68,10 @@ def run(
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 @@ -87,6 +91,7 @@ def run(
job_params=job_params,
emulator=self.emulator,
config=self._config,
wait=wait,
)

def _validate_config(self, config: EmulatorConfig) -> None:
Expand Down
6 changes: 4 additions & 2 deletions pulser-pasqal/pulser_pasqal/pasqal_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ def __init__(
**kwargs,
)

def submit(self, sequence: Sequence, **kwargs: Any) -> RemoteResults:
def submit(
self, sequence: Sequence, wait: bool = False, **kwargs: Any
) -> RemoteResults:
"""Submits the sequence for execution on a remote Pasqal backend."""
if not sequence.is_measured():
bases = sequence.get_addressed_bases()
Expand Down Expand Up @@ -141,7 +143,7 @@ def submit(self, sequence: Sequence, **kwargs: Any) -> RemoteResults:
jobs=job_params or [], # type: ignore[arg-type]
emulator=emulator,
configuration=configuration,
wait=False,
wait=wait,
)

return RemoteResults(batch.id, self)
Expand Down
2 changes: 1 addition & 1 deletion pulser-pasqal/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pasqal-cloud ~= 0.3.5
pasqal-cloud ~= 0.4.0
backoff ~= 2.2
2 changes: 1 addition & 1 deletion pulser-simulation/pulser_simulation/simconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def __post_init__(self) -> None:
raise TypeError(
f"'temperature' must be a float, not {type(self.temperature)}."
)
self._change_attribute("temperature", self.temperature * 1e-6)
self._change_attribute("temperature", self.temperature / 1e6)

# Kept to show error messages with the right parameter names
self._check_spam_dict()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class _MockConnection(RemoteConnection):
def __init__(self):
self._status_calls = 0

def submit(self, sequence, **kwargs) -> RemoteResults:
def submit(self, sequence, wait: bool = False, **kwargsn) -> RemoteResults:
return RemoteResults("abcd", self)

def _fetch_result(self, submission_id: str) -> typing.Sequence[Result]:
Expand Down
6 changes: 2 additions & 4 deletions tests/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,8 @@ def test_max_connectivity():
# Check spacing
reg = Register.max_connectivity(max_atom_num, device, spacing=spacing)
with pytest.raises(ValueError, match="Spacing "):
reg = Register.max_connectivity(
max_atom_num, device, spacing=spacing - 1.0
)

Register.max_connectivity(max_atom_num, device, spacing=spacing - 1.0)
reg = Register.max_connectivity(max_atom_num, MockDevice, spacing=spacing)
with pytest.raises(
NotImplementedError,
match="Maximum connectivity layouts are not well defined for a "
Expand Down
2 changes: 2 additions & 0 deletions tests/test_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,8 @@ def test_target(reg, device):
seq.target("q3", "ch1")
with pytest.raises(ValueError, match="can target at most 1 qubits"):
seq.target(["q1", "q5"], "ch0")
with pytest.raises(ValueError, match="Need at least one qubit to target"):
seq.target([], "ch0")

assert seq._schedule["ch0"][-1] == _TimeSlot("target", -1, 0, {"q1"})
seq.target("q4", "ch0")
Expand Down
3 changes: 3 additions & 0 deletions tests/test_simconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def test_init():
temperature=1000.0,
runs=100,
)
assert config.temperature == 1e-3 # in K
str_config = config.__str__(True)
assert "SPAM, doppler, dephasing, amplitude" in str_config
assert (
Expand All @@ -49,6 +50,8 @@ def test_init():
and "Solver Options" in str_config
)
config = SimConfig(noise="depolarizing")
assert config.temperature == 5e-5
assert config.to_noise_model().temperature == 50
str_config = config.__str__(True)
assert "depolarizing" in str_config
config = SimConfig(
Expand Down
Loading

0 comments on commit 3135b67

Please sign in to comment.