Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

/get targets refactor #788

Merged
merged 57 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
d8f092d
initial commit
bharat-thotakura Sep 23, 2023
88c69f6
filtering + dataclass for /get_targets
bharat-thotakura Sep 23, 2023
d3dc507
missing types + flake8 fix
bharat-thotakura Sep 29, 2023
5b1efbd
store get_targets output
bharat-thotakura Oct 5, 2023
c9e2ae9
update with BaseModel + code clean-up
bharat-thotakura Oct 5, 2023
89cb821
updated tests
bharat-thotakura Oct 5, 2023
f6d5c46
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 5, 2023
8a26063
update in supermarq
bharat-thotakura Oct 5, 2023
29c7519
extract target name
bharat-thotakura Oct 5, 2023
79fe2bd
add pydantic req
bharat-thotakura Oct 5, 2023
4dddff8
remove print statement
bharat-thotakura Oct 5, 2023
b0f3d26
remove __repr__ and __eq__ test
bharat-thotakura Oct 5, 2023
e761737
create testing.py
bharat-thotakura Oct 5, 2023
9c146b6
extension rename
bharat-thotakura Oct 5, 2023
42534d5
update req version
bharat-thotakura Oct 5, 2023
bdde587
apply requirements
bharat-thotakura Oct 5, 2023
85e0687
kwargs -> explicit args
bharat-thotakura Oct 6, 2023
20e617a
move filtering
bharat-thotakura Oct 6, 2023
5f4c1b5
remove filtering tests
bharat-thotakura Oct 6, 2023
a89d63c
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 6, 2023
c78510f
Use kwargs in `gss` `get_targets()`
bharat-thotakura Oct 10, 2023
635ecd1
`locals()` -> `kwargs` in `gss`
bharat-thotakura Oct 10, 2023
f22ea53
move to gss.typing
bharat-thotakura Oct 10, 2023
59a5c2f
comment out notebook get_targets()
bharat-thotakura Oct 10, 2023
0624b6c
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 10, 2023
b9f80c9
conftest typing + docstring fix
bharat-thotakura Oct 10, 2023
2ade622
remove get_targets
bharat-thotakura Oct 11, 2023
b3e8b21
move clients get_targets to gss.service
bharat-thotakura Oct 11, 2023
281c6d9
relocate get_targets test
bharat-thotakura Oct 11, 2023
bfe4a77
use post_request + kwargs + docstring nits
bharat-thotakura Oct 11, 2023
bc03723
change to post request in tests
bharat-thotakura Oct 11, 2023
945e01e
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 24, 2023
f175de3
remove merge conflict test
bharat-thotakura Oct 24, 2023
fbd0b1c
docstring update (1)
bharat-thotakura Oct 26, 2023
707f4f9
docstring update (2)
bharat-thotakura Oct 26, 2023
7161384
docstring update (3)
bharat-thotakura Oct 26, 2023
082b070
update requirements
bharat-thotakura Oct 26, 2023
b6d1470
use gss.TargetInfo
bharat-thotakura Oct 26, 2023
47cabd5
remove testing import
bharat-thotakura Oct 26, 2023
5f4460a
remove submission restriction
bharat-thotakura Oct 26, 2023
6015453
update test from allowed backends update
bharat-thotakura Oct 26, 2023
0a82700
update testing import in qss
bharat-thotakura Oct 26, 2023
31b8d82
gss.testing import fix in qss
bharat-thotakura Oct 26, 2023
25a812a
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 26, 2023
4dfaf89
clearer TargetInfo in integration test
bharat-thotakura Oct 26, 2023
73d74d9
Merge branch 'main' into get_targets_refactor
bharat-thotakura Oct 31, 2023
d4f6f96
Revert "MIS notebook update"
bharat-thotakura Nov 3, 2023
cf43299
Merge branch 'main' into get_targets_refactor
bharat-thotakura Nov 9, 2023
9aeba90
renaming TargetInfo -> Target
bharat-thotakura Nov 9, 2023
38bdb83
Revert "comment out notebook get_targets()"
bharat-thotakura Nov 10, 2023
421b636
filter to backend()
bharat-thotakura Nov 10, 2023
8972ef8
add filters to backends()
bharat-thotakura Nov 10, 2023
8926de3
docstring typo fix
bharat-thotakura Nov 10, 2023
c4182ea
rerun supermarq notebook
bharat-thotakura Nov 10, 2023
f85fc62
re-run get_started notebooks + nits
bharat-thotakura Nov 10, 2023
ceae28e
wording: backends -> targets
bharat-thotakura Nov 10, 2023
5b11ed7
Revert "Revert "MIS notebook update""
bharat-thotakura Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions cirq-superstaq/cirq_superstaq/daily_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,24 @@ def test_get_resource_estimate(service: css.Service) -> None:

def test_get_targets(service: css.Service) -> None:
result = service.get_targets()
assert "ibmq_qasm_simulator" in result["compile-and-run"]
assert "aqt_keysight_qpu" in result["compile-only"]
ibmq_properties = {
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
"supports_submit": True,
"supports_submit_qubo": False,
"supports_compile": True,
"available": True,
"retired": False,
}
aqt_properties = {
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
"supports_submit": False,
"supports_submit_qubo": False,
"supports_compile": True,
"available": True,
"retired": False,
}
assert (
gss.superstaq_client.TargetInfo(target="ibmq_qasm_simulator", **ibmq_properties) in result
)
assert gss.superstaq_client.TargetInfo(target="aqt_keysight_qpu", **aqt_properties) in result


def test_qscout_compile(service: css.Service) -> None:
Expand Down
22 changes: 18 additions & 4 deletions cirq-superstaq/cirq_superstaq/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,13 +404,27 @@ def get_balance(self, pretty_output: bool = True) -> Union[str, float]:
return f"${balance:,.2f}"
return balance

def get_targets(self) -> Dict[str, List[str]]:
"""Gets a list of available, unavailable, and retired targets.
def get_targets(
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
self, simulator: Optional[bool] = None, **kwargs: Any
) -> List[gss.superstaq_client.TargetInfo]:
"""Gets a list of Superstaq targets along with their status information.

Args:
simulator: Optional flag to restrict the list of targets to simulators.
kwargs: Optional desired target filters.
- supports_submit: Boolean flag to only return targets that (don't) allow circuit
submissions.
- supports_submit_qubo: Boolean flag to only return targets that (don't) allow qubo
submissions.
- supports_compile: Boolean flag to return targets that (don't) support circuit
compilation.
- available: Boolean flag to only return targets that are (not) available to use.
- retired: Boolean flag to only return targets that are or are not retired.

Returns:
A list of Superstaq targets.
A list of Superstaq targets (or filterd targets based on `kwargs`).
"""
return self._client.get_targets()["superstaq_targets"]
return self._client.get_targets(simulator, **kwargs)

def resource_estimate(
self, circuits: Union[cirq.Circuit, Sequence[cirq.Circuit]], target: Optional[str] = None
Expand Down
39 changes: 6 additions & 33 deletions cirq-superstaq/cirq_superstaq/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,40 +249,13 @@ def test_service_get_balance() -> None:
assert service.get_balance(pretty_output=False) == 12345.6789


def test_service_get_targets() -> None:
@mock.patch(
"general_superstaq.superstaq_client._SuperstaqClient.get_request",
return_value={"superstaq_targets": gss.typing.TARGET_LIST},
)
def test_service_get_targets(mock_get_request: mock.MagicMock) -> None:
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
service = css.Service(api_key="key", remote_host="http://example.com")
mock_client = mock.MagicMock()
targets = {
"superstaq_targets": {
"compile-and-run": [
"ibmq_qasm_simulator",
"ibmq_armonk_qpu",
"ibmq_santiago_qpu",
"ibmq_bogota_qpu",
"ibmq_lima_qpu",
"ibmq_belem_qpu",
"ibmq_quito_qpu",
"ibmq_statevector_simulator",
"ibmq_mps_simulator",
"ibmq_extended-stabilizer_simulator",
"ibmq_stabilizer_simulator",
"ibmq_manila_qpu",
"aws_dm1_simulator",
"aws_sv1_simulator",
"d-wave_advantage-system4.1_qpu",
"d-wave_dw-2000q-6_qpu",
"aws_tn1_simulator",
"rigetti_aspen-9_qpu",
"d-wave_advantage-system1.1_qpu",
"ionq_ion_qpu",
],
"compile-only": ["aqt_keysight_qpu", "aqt_zurich_qpu", "sandia_qscout_qpu"],
}
}
mock_client.get_targets.return_value = targets
service._client = mock_client

assert service.get_targets() == targets["superstaq_targets"]
assert service.get_targets() == gss.typing.RETURNED_TARGETS


@mock.patch(
Expand Down
83 changes: 79 additions & 4 deletions general-superstaq/general_superstaq/superstaq_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Client for making requests to Superstaq's API."""
from __future__ import annotations

import json
import os
import pathlib
Expand All @@ -21,6 +23,7 @@
import warnings
from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Union

import pydantic
import qubovert as qv
import requests

Expand Down Expand Up @@ -236,15 +239,51 @@ def _accept_terms_of_use(self, user_input: str) -> str:
"""
return self.post_request("/accept_terms_of_use", {"user_input": user_input})

def get_targets(self) -> Dict[str, Dict[str, List[str]]]:
def get_targets(self, simulator: Optional[bool] = None, **kwargs: Any) -> List[TargetInfo]:
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
"""Makes a GET request to retrieve targets from the Superstaq API.

Gets a list of available, unavailable, and retired targets.
Args:
simulator: Optional flag to restrict the list of targets to simulators.
kwargs: Optional desired target filters.

Raises:
ValueError: If `kwargs` is not a valid keyword inquiry on the target.

Returns:
A dictionary listing the targets.
A list of Superstaq targets (or filterd targets based on `kwargs`).
"""
return self.get_request("/targets")
superstaq_targets = self.get_request("/targets")["superstaq_targets"]
target_list = [
TargetInfo(target=target_name, **properties)
for target_name, properties in superstaq_targets.items()
]

if simulator is not None:
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
target_list = [
target_info
for target_info in target_list
if (target_info.target.endswith("simulator")) == simulator
]

if kwargs:
filtered_targets = []
for target in target_list:
for key in kwargs.keys():
if key not in target.__fields__:
raise ValueError(
f"{key} is not a valid keyword inquiry on the target. Alternatively, "
"please use the /target_info endpoint to retrieve more information on "
"the target."
)
if all(
getattr(target, filter) == filter_value
for filter, filter_value in kwargs.items()
):
filtered_targets.append(target)
if filtered_targets == []:
print("No targets matched the specified criteria.")
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
return filtered_targets
return target_list

def add_new_user(self, json_dict: Dict[str, str]) -> str:
"""Makes a POST request to Superstaq API to add a new user.
Expand Down Expand Up @@ -774,3 +813,39 @@ def find_api_key() -> str:
"Try passing an 'api_key' variable, or setting your API key in the command line "
"with SUPERSTAQ_API_KEY=..."
)


class TargetInfo(pydantic.BaseModel):
bharat-thotakura marked this conversation as resolved.
Show resolved Hide resolved
"""A class to store data returned from a `/get_targets` request."""

target: str
supports_submit: bool = False
supports_submit_qubo: bool = False
supports_compile: bool = False
available: bool = False
retired: bool = False

def __repr__(self) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth confirming, but i think pydantic.BaseModel automatically defines this and __eq__ so we don't need to

Copy link
Contributor Author

@bharat-thotakura bharat-thotakura Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it does define __repr__ but its formatting looks like this (i.e., without the newlines from the dedicated one which I guess takes up more space):

[TargetInfo(target='aws_dm1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False),
 TargetInfo(target='aws_sv1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False),
 TargetInfo(target='aws_tn1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False),
...

It does also handle the __eq__, I just had an assertion error in one of my earlier tests that was fixed by adding an __eq__ but that's not an issue anymore, so it's removed now.

return textwrap.dedent(
f"""\
TargetInfo(
target={self.target!r},
supports_submit={self.supports_submit!r},
supports_submit_qubo={self.supports_submit_qubo!r},
supports_compile={self.supports_compile!r},
available={self.available!r},
retired={self.retired!r},
)"""
)

def __eq__(self, other: object) -> bool:
if isinstance(other, TargetInfo):
return (
self.target == other.target
and self.supports_submit == other.supports_submit
and self.supports_submit_qubo == other.supports_submit_qubo
and self.supports_compile == other.supports_compile
and self.available == other.available
and self.retired == other.retired
)
return False
83 changes: 54 additions & 29 deletions general-superstaq/general_superstaq/superstaq_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,46 +490,51 @@ def test_superstaq_client_resource_estimate(mock_post: mock.MagicMock) -> None:
@mock.patch("requests.get")
def test_superstaq_client_get_targets(mock_get: mock.MagicMock) -> None:
mock_get.return_value.ok = True
targets = {
"superstaq_targets:": {
"compile-and-run": [
"ibmq_qasm_simulator",
"ibmq_armonk_qpu",
"ibmq_santiago_qpu",
"ibmq_bogota_qpu",
"ibmq_lima_qpu",
"ibmq_belem_qpu",
"ibmq_quito_qpu",
"ibmq_statevector_simulator",
"ibmq_mps_simulator",
"ibmq_extended-stabilizer_simulator",
"ibmq_stabilizer_simulator",
"ibmq_manila_qpu",
"aws_dm1_simulator",
"aws_sv1_simulator",
"d-wave_advantage-system4.1_qpu",
"d-wave_dw-2000q-6_qpu",
"aws_tn1_simulator",
"rigetti_aspen-9_qpu",
"d-wave_advantage-system1.1_qpu",
"ionq_ion_qpu",
],
"compile-only": ["aqt_keysight_qpu", "sandia_qscout_qpu"],
}
}
mock_get.return_value.json.return_value = targets
mock_get.return_value.json.return_value = {"superstaq_targets": gss.typing.TARGET_LIST}
client = gss.superstaq_client._SuperstaqClient(
client_name="general-superstaq",
remote_host="http://example.com",
api_key="to_my_heart",
)
response = client.get_targets()
assert response == targets
assert response == gss.typing.RETURNED_TARGETS

mock_get.assert_called_with(
f"http://example.com/{API_VERSION}/targets", headers=EXPECTED_HEADERS, verify=False
)

# test simulator only return
response = client.get_targets(simulator=True)
simulator_targets = [
target_info
for target_info in gss.typing.RETURNED_TARGETS
if target_info.target.endswith("simulator")
]
assert response == simulator_targets

# test kwargs return
response = client.get_targets(simulator=True, supports_submit_qubo=True, available=True)
assert response == [
gss.superstaq_client.TargetInfo(
target="toshiba_bifurcation_simulator",
**{
"supports_submit": False,
"supports_submit_qubo": True,
"supports_compile": False,
"available": True,
"retired": False,
},
)
]

# test empty return
response = client.get_targets(simulator=False, supports_submit_qubo=True, available=True)
assert response == []

# invalid kwarg test
with pytest.raises(ValueError, match="a valid keyword inquiry on the target"):
_ = client.get_targets(invalid_key=True)


@mock.patch("requests.post")
def test_superstaq_client_get_job_unauthorized(mock_post: mock.MagicMock) -> None:
Expand Down Expand Up @@ -848,3 +853,23 @@ def test_find_api_key() -> None:
with mock.patch.dict(os.environ, SUPERSTAQ_API_KEY=""):
with mock.patch("pathlib.Path.is_file", return_value=False):
gss.superstaq_client.find_api_key()


def test_target_info_repr_and_eq() -> None:
sample_target_info = gss.superstaq_client.TargetInfo(
target="example_target",
**{
"supports_submit": False,
"supports_submit_qubo": True,
"supports_compile": False,
"available": True,
"retired": False,
},
)
assert (
repr(sample_target_info)
== "TargetInfo(\n target='example_target',\n supports_submit=False,\n "
"supports_submit_qubo=True,\n supports_compile=False,\n "
"available=True,\n retired=False,\n)"
)
assert sample_target_info != {"superstaq_target": "example_target"}
Loading