-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[QI2-1076] Backend construction (#11)
- Loading branch information
Showing
11 changed files
with
423 additions
and
23 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from typing import Any, Awaitable, Callable, Generic, List, Optional, TypeVar, cast | ||
|
||
from pydantic import BaseModel, Field | ||
from typing_extensions import Annotated | ||
|
||
PageType = TypeVar("PageType") | ||
ItemType = TypeVar("ItemType") | ||
|
||
|
||
class PageInterface(BaseModel, Generic[ItemType]): | ||
"""The page models in the generated API client don't inherit from a common base class, so we have to trick the | ||
typing system a bit with this fake base class.""" | ||
|
||
items: List[ItemType] | ||
total: Optional[Annotated[int, Field(strict=True, ge=0)]] | ||
page: Optional[Annotated[int, Field(strict=True, ge=1)]] | ||
size: Optional[Annotated[int, Field(strict=True, ge=1)]] | ||
pages: Optional[Annotated[int, Field(strict=True, ge=0)]] = None | ||
|
||
|
||
class PageReader(Generic[PageType, ItemType]): | ||
"""Helper class for reading fastapi-pagination style pages returned by the compute_api_client.""" | ||
|
||
async def get_all(self, api_call: Callable[..., Awaitable[PageType]], **kwargs: Any) -> List[ItemType]: | ||
"""Get all items from an API call that supports paging.""" | ||
items: List[ItemType] = [] | ||
page = 1 | ||
|
||
while True: | ||
response = cast(PageInterface[ItemType], await api_call(page=page, **kwargs)) | ||
|
||
items.extend(response.items) | ||
page += 1 | ||
if response.pages is None or page > response.pages: | ||
break | ||
return items |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from qiskit.transpiler import CouplingMap | ||
|
||
|
||
def is_coupling_map_complete(coupling_map: CouplingMap) -> bool: | ||
"""A complete digraph is a digraph in which there is a directed edge from every vertex to every other vertex.""" | ||
distance_matrix = coupling_map.distance_matrix | ||
|
||
assert distance_matrix is not None | ||
|
||
is_semicomplete = all(distance in [1, 0] for distance in distance_matrix.flatten()) | ||
|
||
return is_semicomplete and coupling_map.is_symmetric |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from unittest.mock import AsyncMock | ||
|
||
import pytest | ||
from compute_api_client import BackendType, PageBackendType | ||
|
||
from qiskit_quantuminspire.api.pagination import PageReader | ||
from tests.helpers import create_backend_type | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_pagination_get_all() -> None: | ||
# Arrange | ||
def returned_pages(page: int) -> PageBackendType: | ||
pages = [ | ||
PageBackendType( | ||
items=[ | ||
create_backend_type(name="qi_backend_1"), | ||
create_backend_type(name="spin"), | ||
], | ||
total=5, | ||
page=1, | ||
size=2, | ||
pages=2, | ||
), | ||
PageBackendType( | ||
items=[ | ||
create_backend_type(name="qi_backend2"), | ||
create_backend_type(name="spin6"), | ||
create_backend_type(name="spin7"), | ||
], | ||
total=5, | ||
page=2, | ||
size=3, | ||
pages=2, | ||
), | ||
] | ||
return pages[page - 1] | ||
|
||
api_call = AsyncMock(side_effect=returned_pages) | ||
|
||
page_reader = PageReader[PageBackendType, BackendType]() | ||
|
||
# Act | ||
backends = await page_reader.get_all(api_call) | ||
|
||
# Assert | ||
actual_backend_names = [backend.name for backend in backends] | ||
expected_backend_names = ["qi_backend_1", "spin", "qi_backend2", "spin6", "spin7"] | ||
|
||
assert actual_backend_names == expected_backend_names |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from compute_api_client import BackendStatus, BackendType | ||
|
||
|
||
def create_backend_type( | ||
gateset: list[str] = [], | ||
topology: list[list[int]] = [], | ||
nqubits: int = 0, | ||
default_number_of_shots: int = 1024, | ||
max_number_of_shots: int = 2048, | ||
name: str = "qi_backend", | ||
) -> BackendType: | ||
"""Helper for creating a backendtype with only the fields you care about.""" | ||
return BackendType( | ||
name=name, | ||
nqubits=nqubits, | ||
gateset=gateset, | ||
topology=topology, | ||
id=1, | ||
is_hardware=True, | ||
image_id="qi_backend", | ||
features=[], | ||
default_compiler_config="", | ||
status=BackendStatus.IDLE, | ||
default_number_of_shots=default_number_of_shots, | ||
max_number_of_shots=max_number_of_shots, | ||
infrastructure="QCI", | ||
description="A Quantum Inspire backend", | ||
) |
Oops, something went wrong.