Skip to content

Commit

Permalink
Implement PulserPasqal module (#410)
Browse files Browse the repository at this point in the history
* Add PasqalCloud class and UT

* Add documentation's API reference

* Fix missing formatting

* Ignore cloud PasqalSdk types

* Check virtual devices are not sent to a real QPU

* Update PulserPasqal to 0.1.6

* Fix return statements

* Apply remarks

* Add missing lines

* Add JobParameters class

* Use abstract representation for emulators

* Apply new comments and create JobVariables

* Apply new comment

* Also fix Sequence's type on variables
  • Loading branch information
CdeTerra authored Nov 17, 2022
1 parent 354d07a commit bc9d9f0
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 2 deletions.
9 changes: 9 additions & 0 deletions docs/source/apidoc/cloud.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
************************
Pasqal Cloud connection
************************

PasqalCloud
----------------------

.. autoclass:: pulser_pasqal.PasqalCloud
:members:
1 change: 1 addition & 0 deletions docs/source/apidoc/pulser.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ API Reference

core
simulation
cloud
7 changes: 6 additions & 1 deletion pulser-core/pulser/sequence/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ def qubit_info(self) -> dict[QubitId, np.ndarray]:
)
return cast(BaseRegister, self._register).qubits

@property
def device(self) -> BaseDevice:
"""Device that the sequence is using."""
return self._device

@property
def register(self) -> BaseRegister:
"""Register with the qubit's IDs and positions."""
Expand Down Expand Up @@ -793,7 +798,7 @@ def build(
self,
*,
qubits: Optional[Mapping[QubitId, int]] = None,
**vars: Union[ArrayLike, float, int, str],
**vars: Union[ArrayLike, float, int],
) -> Sequence:
"""Builds a sequence from the programmed instructions.
Expand Down
4 changes: 4 additions & 0 deletions pulser-pasqal/pulser_pasqal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
# limitations under the License.
"""Classes for interfacing with Pasqal backends."""

from sdk import Configuration, DeviceType, Endpoints

from pulser_pasqal._version import __version__
from pulser_pasqal.job_parameters import JobParameters, JobVariables
from pulser_pasqal.pasqal_cloud import PasqalCloud
65 changes: 65 additions & 0 deletions pulser-pasqal/pulser_pasqal/job_parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2022 Pulser Development Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Parameters to build a sequence sent to the cloud."""
from __future__ import annotations

import dataclasses
from typing import Dict, Mapping, Optional, Union

from numpy.typing import ArrayLike

from pulser.register import QubitId

JobVariablesDict = Dict[str, Union[ArrayLike, Optional[Mapping[QubitId, int]]]]


class JobVariables:
"""Variables to build the sequence."""

def __init__(
self,
qubits: Optional[Mapping[QubitId, int]] = None,
**vars: Union[ArrayLike, float, int],
):
"""Initializes the JobVariables class.
Args:
qubits: A mapping between qubit IDs and trap IDs used to define
the register. Must only be provided when the sequence is
initialized with a MappableRegister.
vars: The values for all the variables declared in this Sequence
instance, indexed by the name given upon declaration. Check
``Sequence.declared_variables`` to see all the variables.
"""
self._qubits = qubits
self._vars = vars

def get_dict(self) -> JobVariablesDict:
"""Creates a dictionary used by the Sequence building and the cloud."""
return {"qubits": self._qubits, **self._vars}


@dataclasses.dataclass
class JobParameters:
"""Parameters representing a job to build the sequence."""

runs: int
variables: JobVariables

def get_dict(self) -> dict[str, Union[int, JobVariablesDict]]:
"""Creates a dictionary to send to the cloud."""
return dict(
runs=self.runs,
variables=self.variables.get_dict(),
)
119 changes: 119 additions & 0 deletions pulser-pasqal/pulser_pasqal/pasqal_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2022 Pulser Development Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Allows to connect to the cloud powered by Pasqal to run sequences."""
from __future__ import annotations

from typing import Any, Optional

import sdk

from pulser import Sequence
from pulser.devices import Device
from pulser_pasqal.job_parameters import JobParameters


class PasqalCloud:
"""Manager of the connection to the cloud powered by Pasqal.
The cloud connection enables to run sequences on simulators or on real
QPUs.
Args:
client_id: client_id of the API key you are holding for Pasqal
cloud.
client_secret: client_secret of the API key you are holding for
Pasqal cloud.
kwargs: Additional arguments to provide to SDK
"""

def __init__(
self,
client_id: str,
client_secret: str,
**kwargs: Any,
):
"""Initializes a connection to the cloud."""
self._sdk_connection = sdk.SDK(
client_id=client_id,
client_secret=client_secret,
**kwargs,
)

def create_batch(
self,
seq: Sequence,
jobs: list[JobParameters],
device_type: sdk.DeviceType = sdk.DeviceType.QPU,
configuration: Optional[sdk.Configuration] = None,
wait: bool = False,
) -> sdk.Batch:
"""Create a new batch and send it to the API.
For Iroise MVP, the batch must contain at least one job and will be
declared as complete immediately.
Args:
seq: Pulser sequence.
jobs: List of jobs to be added to the batch at creation.
device_type: The type of device to use, either an emulator or a QPU
If set to QPU, the device_type will be set to the one
stored in the serialized sequence.
configuration: A dictionary with extra configuration for the
emulators that accept it.
wait: Whether to wait for results to be sent back.
Returns:
Batch: The new batch that has been created in the database.
"""
if device_type == sdk.DeviceType.QPU and not isinstance(
seq.device, Device
):
raise TypeError(
"To be sent to a real QPU, the device of the sequence "
"must be a real device, instance of 'Device'."
)

for params in jobs:
seq.build(**params.variables.get_dict()) # type: ignore

return self._sdk_connection.create_batch(
serialized_sequence=self._serialize_seq(
seq=seq, device_type=device_type
),
jobs=[j.get_dict() for j in jobs],
device_type=device_type,
configuration=configuration,
wait=wait,
)

def _serialize_seq(
self, seq: Sequence, device_type: sdk.DeviceType
) -> str:
if device_type == sdk.DeviceType.QPU:
return seq.serialize()
return seq.to_abstract_repr()

def get_batch(self, id: int, fetch_results: bool = False) -> sdk.Batch:
"""Retrieve a batch's data and all its jobs.
Args:
id: Id of the batch.
fetch_results: Whether to load job results.
Returns:
Batch: The batch stored in the database.
"""
return self._sdk_connection.get_batch(
id=id, fetch_results=fetch_results
)
2 changes: 1 addition & 1 deletion pulser-pasqal/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pasqal-sdk
pasqal-sdk == 0.1.6
Loading

0 comments on commit bc9d9f0

Please sign in to comment.