Skip to content

Commit

Permalink
[IMPROVEMENT] Replace DeviceType with EmulatorType (#72)
Browse files Browse the repository at this point in the history
* ♻️ remove qpu

* ♻️ export device stuff to device for convenience

* 📝 bump major

* ⚗️ push again
  • Loading branch information
Augustinio authored Apr 18, 2023
1 parent 03962dc commit da2da8e
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 54 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@

All notable changes to this project will be documented in this file.

## [0.1.16] 2023-04-06

### Changed

- `device_type` argument replace by `emulator` in sdk create_batch
- `DeviceType` replaced with `EmulatorType`

### Deleted

- QPU device type and related logic

## [0.1.15] 2023-03-27

### Added

- Added tests to check the login behavior.
- Added tests to check the override Endpoints behavior.

### Changed

- The authentication now directly connects to the Auth0 platform instead of connecting through PasqalCloud.
- Small refactor of files, with the authentication modules in the `authentication.py` file, instead of `client.py`.

### Deleted

- Account endpoint, we now use Auth0.

## [0.1.14]
Expand Down
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ sdk = SDK(..., endpoints=endpoints, auth0=auth0)

This enables you to target backend services running locally on your machine, or other environment like preprod or dev.


## Basic usage

The package main component is a python object called `SDK` which can be used to create a `Batch` and send it automatically
Expand Down Expand Up @@ -141,10 +140,10 @@ job2 = {"runs": 50, "variables": {"omega_max": 10.5} }
# Send the batch of jobs to the QPU and wait for the results
batch = sdk.create_batch(serialized_sequence, [job1,job2], wait=True)

# You can also choose to run your batch on an emulator using the optional argument 'device_type'
# For using a basic single-threaded QPU emulator that can go up to 10 qubits, you can specify the "EMU_FREE" device type.
from sdk import DeviceType
batch = sdk.create_batch(serialized_sequence, [job1,job2], device_type=DeviceType.EMU_FREE)
# You can also choose to run your batch on an emulator using the optional argument 'emulator'
# For using a basic single-threaded QPU emulator that can go up to 10 qubits, you can specify the "EMU_FREE" emulator.
from sdk.device import EmulatorType
batch = sdk.create_batch(serialized_sequence, [job1,job2], emulator=EmulatorType.EMU_FREE)

# Once the QPU has returned the results, you can access them with the following:
for job in batch.jobs.values():
Expand All @@ -162,23 +161,21 @@ For EMU_TN you may add the integrator timestep in nanoseconds, the numerical acc
```python
# replace the corresponding section in the above code example with this to
# add further configuration
from sdk.device.device_types import DeviceType
from sdk.device.configuration import EmuTNConfig
from sdk.device import EmulatorType, EmuTNConfig

configuration = EmuTNConfig(dt = 10.0, precision = "normal", max_bond_dim = 100)
batch = sdk.create_batch(serialized_sequence, [job1,job2], device_type=DeviceType.EMU_TN, configuration=configuration)
batch = sdk.create_batch(serialized_sequence, [job1,job2], emulator=EmulatorType.EMU_TN, configuration=configuration)
```

For EMU_FREE you may add some default SPAM noise. Beware this makes your job take much longer.

```python
# replace the corresponding section in the above code example with this to
# add further configuration
from sdk.device.device_types import DeviceType
from sdk.device.configuration import EmuFreeConfig
from sdk.device import EmulatorType, EmuFreeConfig

configuration = EmuFreeConfig(with_noise=True)
batch = sdk.create_batch(serialized_sequence, [job1,job2], device_type=DeviceType.EMU_FREE, configuration=configuration)
batch = sdk.create_batch(serialized_sequence, [job1,job2], emulator=EmulatorType.EMU_FREE, configuration=configuration)
```

### List of supported device specifications
Expand Down
16 changes: 8 additions & 8 deletions sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
from sdk.authentication import TokenProvider
from sdk.batch import Batch, RESULT_POLLING_INTERVAL
from sdk.client import Client
from sdk.endpoints import Endpoints, Auth0Conf
from sdk.device.configuration import BaseConfig
from sdk.device.device_types import DeviceType
from sdk.device import BaseConfig
from sdk.device import EmulatorType
from sdk.endpoints import Auth0Conf, Endpoints
from sdk.job import Job


Expand Down Expand Up @@ -58,7 +58,7 @@ def create_batch(
self,
serialized_sequence: str,
jobs: List[Dict[str, Any]],
device_type: DeviceType = DeviceType.QPU,
emulator: Optional[EmulatorType] = None,
configuration: Optional[BaseConfig] = None,
wait: bool = False,
fetch_results: bool = False,
Expand All @@ -71,8 +71,8 @@ def create_batch(
serialized_sequence: Serialized pulser sequence.
jobs: List of jobs to be added to the batch at creation.
(#TODO: Make optional after Iroise MVP)
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
emulator: The type of emulator to use,
If set to None, 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.
Expand All @@ -93,8 +93,8 @@ def create_batch(

# the emulator field is only added in the case
# an emulator job is requested otherwise it's left empty
if device_type != DeviceType.QPU:
req.update({"emulator": device_type})
if emulator:
req.update({"emulator": emulator})

# The configuration field is only added in the case
# it's requested
Expand Down
2 changes: 1 addition & 1 deletion sdk/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "0.1.15"
__version__ = "0.2.0"
9 changes: 5 additions & 4 deletions sdk/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sdk.client import Client
from sdk.job import Job
from sdk.device.configuration import BaseConfig, EmuTNConfig, EmuFreeConfig
from sdk.device.device_types import DeviceType
from sdk.device import EmulatorType

RESULT_POLLING_INTERVAL = 2 # seconds

Expand Down Expand Up @@ -65,9 +65,9 @@ def __post_init__(self) -> None:
if not isinstance(self.configuration, dict):
return
conf_class: Type[BaseConfig] = BaseConfig
if self.device_type == DeviceType.EMU_TN.value:
if self.device_type == EmulatorType.EMU_TN.value:
conf_class = EmuTNConfig
elif self.device_type == DeviceType.EMU_FREE.value:
elif self.device_type == EmulatorType.EMU_FREE.value:
conf_class = EmuFreeConfig

self.configuration = conf_class.from_dict(self.configuration)
Expand Down Expand Up @@ -108,7 +108,8 @@ def declare_complete(
Args:
wait: Whether to wait for the batch to be done
fetch_results: Whether to download the results. Implies waiting for the batch.
fetch_results: Whether to download the results. Implies
waiting for the batch.
A batch that is complete awaits no extra jobs. All jobs previously added
will be executed before the batch is terminated. When all its jobs are done,
Expand Down
2 changes: 1 addition & 1 deletion sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from getpass import getpass
from typing import Any, Dict, List, Optional, Tuple

from requests.auth import AuthBase
import requests
from requests.auth import AuthBase

from sdk.authentication import (
TokenProvider,
Expand Down
13 changes: 13 additions & 0 deletions sdk/device/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from sdk.device.configuration import (
BaseConfig,
EmuFreeConfig,
EmuTNConfig,
)
from sdk.device.emulator_types import EmulatorType

__all__ = [
"BaseConfig",
"EmuFreeConfig",
"EmuTNConfig",
"EmulatorType",
]
11 changes: 0 additions & 11 deletions sdk/device/device_types.py

This file was deleted.

6 changes: 6 additions & 0 deletions sdk/device/emulator_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from sdk.utils.strenum import StrEnum


class EmulatorType(StrEnum):
EMU_FREE = "EMU_FREE"
EMU_TN = "EMU_TN"
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json
import os

import pytest
import requests_mock
from sdk.device.device_types import DeviceType

from sdk.endpoints import Endpoints

Expand All @@ -26,7 +26,7 @@ def mock_core_response(request):
if data.get("emulator"):
result["data"]["device_type"] = data["emulator"]
else:
result["data"]["device_type"] = DeviceType.QPU
result["data"]["device_type"] = "FRESNEL"
result["data"]["configuration"] = None
return result

Expand Down
27 changes: 14 additions & 13 deletions tests/test_batch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from uuid import uuid4
from unittest.mock import patch
import pytest
from uuid import uuid4

from sdk import SDK, DeviceType
from sdk.device.configuration import BaseConfig, EmuFreeConfig, EmuTNConfig
import pytest
from tests.test_doubles.authentication import FakeAuth0AuthenticationSuccess

from sdk import SDK
from sdk.device import BaseConfig, EmuFreeConfig, EmulatorType, EmuTNConfig


class TestBatch:
@pytest.fixture(autouse=True)
Expand All @@ -21,13 +22,13 @@ def init_sdk(self, start_mock_request):
self.job_id = "00000000-0000-0000-0000-000000022010"
self.job_variables = {"Omega_max": 14.4, "last_target": "q1", "ts": [200, 500]}

@pytest.mark.parametrize("device_type", [d.value for d in DeviceType])
def test_create_batch(self, device_type):
@pytest.mark.parametrize("emulator", [None] + [e.value for e in EmulatorType])
def test_create_batch(self, emulator):
job = {"runs": self.n_job_runs, "variables": self.job_variables}
batch = self.sdk.create_batch(
serialized_sequence=self.pulser_sequence,
jobs=[job],
device_type=device_type,
emulator=emulator,
)
assert batch.id == self.batch_id
assert batch.sequence_builder == self.pulser_sequence
Expand Down Expand Up @@ -128,12 +129,12 @@ def test_batch_declare_complete_and_wait_for_results(self, request_mock):
assert batch.jobs[self.job_id].result == self.job_result

@pytest.mark.parametrize(
"device_type, configuration, expected",
"emulator, configuration, expected",
[
(DeviceType.EMU_TN, EmuTNConfig(), EmuTNConfig()),
(DeviceType.QPU, None, None),
(EmulatorType.EMU_TN, EmuTNConfig(), EmuTNConfig()),
(None, None, None),
(
DeviceType.EMU_FREE,
EmulatorType.EMU_FREE,
EmuFreeConfig(),
EmuFreeConfig(extra_config={"dt": 10.0, "precision": "normal"}),
),
Expand All @@ -144,12 +145,12 @@ def test_batch_declare_complete_and_wait_for_results(self, request_mock):
),
],
)
def test_create_batch_configuration(self, device_type, configuration, expected):
def test_create_batch_configuration(self, emulator, configuration, expected):
job = {"runs": self.n_job_runs, "variables": self.job_variables}
batch = self.sdk.create_batch(
serialized_sequence=self.pulser_sequence,
jobs=[job],
device_type=device_type,
emulator=emulator,
configuration=configuration,
)
assert batch.configuration == expected
6 changes: 3 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import pytest

from sdk.device import EmuFreeConfig
from sdk.device.configuration.base_config import (
BaseConfig,
InvalidConfiguration,
INVALID_KEY_ERROR_MSG,
InvalidConfiguration,
)
from sdk.device.configuration import EmuFreeConfig
from sdk.device.configuration.emu_tn import (
EmuTNConfig,
DT_VALUE_NOT_VALID,
EmuTNConfig,
PRECISION_NOT_VALID,
)

Expand Down

0 comments on commit da2da8e

Please sign in to comment.