From 5bd455fc55e3c1cef44668998b3cdae2535b8bff Mon Sep 17 00:00:00 2001 From: rich Date: Mon, 27 Oct 2025 11:33:01 -0500 Subject: [PATCH 1/7] new target model --- general-superstaq/general_superstaq/models.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 50d266860..6b4dbe20b 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -338,6 +338,43 @@ class UpdateUserDetails(DefaultPydanticModel): """New user balance.""" +class TargetStatus(str, Enum): + """The current status of a Superstaq Target.""" + AVAILABLE = "available" + """Target is currently accepting new jobs.""" + DEV_ONLY = "dev_only" + """Target is in maintenance mode.""" + OFFLINE = "offline" + """Target is temporarily unavailable for job submission.""" + RETIRED = "retired" + """Target has been permanently retired.""" + UNSUPPORTED = "unsupported" + """Target does not support job submission through Superstaq.""" + + +class TargetInputType(str, Enum): + """The input type supported by a Superstaq target.""" + CIRCUIT = "circuit" + QUBO = "qubo" + + +class TargetModel(DefaultPydanticModel): + """Model for the details of a target.""" + + target: str + """The target name.""" + status: TargetStatus + """The status of this target.""" + input_type: TargetInputType + """The input type expected by this target.""" + accessible: bool + """Whether this target is accessible to the current user.""" + + @property + def simulator(self) -> bool: + return self.target.endswith("_simulator") + + class TargetModel(DefaultPydanticModel): """Model for the details of a target.""" From f894fe601251202483da5cc7de4d5324d2c603f5 Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 29 Oct 2025 10:22:11 -0500 Subject: [PATCH 2/7] new target model --- general-superstaq/general_superstaq/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 6b4dbe20b..34ba29259 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -366,7 +366,7 @@ class TargetModel(DefaultPydanticModel): status: TargetStatus """The status of this target.""" input_type: TargetInputType - """The input type expected by this target.""" + """The underlying input type expected by this target (e.g. "circuit" or "qubo").""" accessible: bool """Whether this target is accessible to the current user.""" @@ -375,7 +375,7 @@ def simulator(self) -> bool: return self.target.endswith("_simulator") -class TargetModel(DefaultPydanticModel): +class TargetModel_v0(DefaultPydanticModel): """Model for the details of a target.""" target_name: str From 1295921716049edaca50ef54a2ab9c2a06bb1f2f Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 19 Nov 2025 15:30:01 -0600 Subject: [PATCH 3/7] supported_inputs instead of input_type --- general-superstaq/general_superstaq/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 34ba29259..093eb7492 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -365,8 +365,8 @@ class TargetModel(DefaultPydanticModel): """The target name.""" status: TargetStatus """The status of this target.""" - input_type: TargetInputType - """The underlying input type expected by this target (e.g. "circuit" or "qubo").""" + supported_inputs: list[TargetInputType] + """The input types supported by this target for job submission (e.g. "circuit", "qubo").""" accessible: bool """Whether this target is accessible to the current user.""" From 8d8d5c8ae0fde03245efb1081144ea6dba7b42a3 Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 19 Nov 2025 15:44:20 -0600 Subject: [PATCH 4/7] add test --- general-superstaq/general_superstaq/models.py | 9 ++--- .../general_superstaq/models_test.py | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 4927c25e4..20b22a87c 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -368,11 +368,12 @@ class TargetStatus(str, Enum): class TargetInputType(str, Enum): """The input type supported by a Superstaq target.""" + CIRCUIT = "circuit" QUBO = "qubo" -class TargetModel(DefaultPydanticModel): +class TargetDescription(DefaultPydanticModel): """Model for the details of a target.""" target: TargetStr @@ -381,7 +382,7 @@ class TargetModel(DefaultPydanticModel): """The status of this target.""" supported_inputs: list[TargetInputType] """The input types supported by this target for job submission (e.g. "circuit", "qubo").""" - accessible: bool + accessible: bool = False """Whether this target is accessible to the current user.""" @property @@ -389,8 +390,8 @@ def simulator(self) -> bool: return self.target.endswith("_simulator") -class TargetModelV0(DefaultPydanticModel): - """Model for the details of a target.""" +class TargetModel(DefaultPydanticModel): + """Legacy model for the details of a target.""" target_name: TargetStr """The target name.""" diff --git a/general-superstaq/general_superstaq/models_test.py b/general-superstaq/general_superstaq/models_test.py index 0a03e6601..2abd5ba37 100644 --- a/general-superstaq/general_superstaq/models_test.py +++ b/general-superstaq/general_superstaq/models_test.py @@ -11,6 +11,41 @@ """ +def test_target_description() -> None: + target_status = gss.models.TargetDescription( + target="sqale_example_qpu", + status=gss.models.TargetStatus.OFFLINE, + supported_inputs=[gss.models.TargetInputType.CIRCUIT], + ) + assert not target_status.accessible + assert not target_status.simulator + + target_status = gss.models.TargetDescription( + target="ss_unconstrained_simulator", + status="available", + supported_inputs=("qubo", "circuit"), + accessible=True, + ) + assert target_status.accessible + assert target_status.simulator + + with pytest.raises(pydantic.ValidationError, match=r"valid target device type"): + _ = gss.models.TargetDescription( + target="sqale_example_system", + status="available", + supported_inputs=["circuit"], + accessible=True, + ) + + with pytest.raises(pydantic.ValidationError, match=r"valid string format"): + _ = gss.models.TargetDescription( + target="bad_target", + status="available", + supported_inputs=["circuit"], + accessible=True, + ) + + def test_user_token_response() -> None: gss.models.UserTokenResponse( email="valid.email@infleqtion.com", From 2fc0d2df40e98af7af707abd889a87cf24ace088 Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 19 Nov 2025 15:48:03 -0600 Subject: [PATCH 5/7] update docstring --- general-superstaq/general_superstaq/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 20b22a87c..a9dea1024 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -381,7 +381,7 @@ class TargetDescription(DefaultPydanticModel): status: TargetStatus """The status of this target.""" supported_inputs: list[TargetInputType] - """The input types supported by this target for job submission (e.g. "circuit", "qubo").""" + """The input types supported by this target (e.g. "circuit", "qubo").""" accessible: bool = False """Whether this target is accessible to the current user.""" From c4f9aadbbfd496e182c02964862a8714919217ec Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 19 Nov 2025 16:03:06 -0600 Subject: [PATCH 6/7] add conversion --- general-superstaq/general_superstaq/models.py | 15 +++++++- .../general_superstaq/models_test.py | 38 +++++++++++++++---- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index a9dea1024..4216e9b13 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -8,7 +8,7 @@ import uuid from collections.abc import Mapping, Sequence from enum import Enum -from typing import Annotated, Any +from typing import Annotated, Any, Self import pydantic.functional_validators @@ -410,6 +410,19 @@ class TargetModel(DefaultPydanticModel): accessible: bool """Target is accessible to user.""" + @classmethod + def from_target_description(cls, target_description: TargetDescription) -> Self: + return cls( + target_name=target_description.target, + supports_submit=(target_description.status != TargetStatus.UNSUPPORTED), + supports_submit_qubo=(TargetInputType.QUBO in target_description.supported_inputs), + supports_compile=(TargetInputType.CIRCUIT in target_description.supported_inputs), + available=(target_description.status == TargetStatus.AVAILABLE), + retired=(target_description.status == TargetStatus.RETIRED), + simulator=target_description.simulator, + accessible=target_description.accessible, + ) + class GetTargetsFilterModel(DefaultPydanticModel): """Model for /get_target requests.""" diff --git a/general-superstaq/general_superstaq/models_test.py b/general-superstaq/general_superstaq/models_test.py index 2abd5ba37..ecad4d21b 100644 --- a/general-superstaq/general_superstaq/models_test.py +++ b/general-superstaq/general_superstaq/models_test.py @@ -12,22 +12,46 @@ def test_target_description() -> None: - target_status = gss.models.TargetDescription( + target_description = gss.models.TargetDescription( target="sqale_example_qpu", - status=gss.models.TargetStatus.OFFLINE, + status=gss.models.TargetStatus.RETIRED, supported_inputs=[gss.models.TargetInputType.CIRCUIT], ) - assert not target_status.accessible - assert not target_status.simulator + assert not target_description.accessible + assert not target_description.simulator + + target_model = gss.models.TargetModel.from_target_description(target_description) + assert target_model == gss.models.TargetModel( + target_name="sqale_example_qpu", + supports_submit=True, + supports_submit_qubo=False, + supports_compile=True, + available=False, + retired=True, + simulator=False, + accessible=False, + ) - target_status = gss.models.TargetDescription( + target_description = gss.models.TargetDescription( target="ss_unconstrained_simulator", status="available", supported_inputs=("qubo", "circuit"), accessible=True, ) - assert target_status.accessible - assert target_status.simulator + assert target_description.accessible + assert target_description.simulator + + target_model = gss.models.TargetModel.from_target_description(target_description) + assert target_model == gss.models.TargetModel( + target_name="ss_unconstrained_simulator", + supports_submit=True, + supports_submit_qubo=True, + supports_compile=True, + available=True, + retired=False, + simulator=True, + accessible=True, + ) with pytest.raises(pydantic.ValidationError, match=r"valid target device type"): _ = gss.models.TargetDescription( From 095a8af7e8151e61600f124d7e53c3350cf3e221 Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 19 Nov 2025 16:09:52 -0600 Subject: [PATCH 7/7] fix for python 3.9 --- general-superstaq/general_superstaq/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/general-superstaq/general_superstaq/models.py b/general-superstaq/general_superstaq/models.py index 4216e9b13..0a44ae275 100644 --- a/general-superstaq/general_superstaq/models.py +++ b/general-superstaq/general_superstaq/models.py @@ -8,7 +8,7 @@ import uuid from collections.abc import Mapping, Sequence from enum import Enum -from typing import Annotated, Any, Self +from typing import Annotated, Any import pydantic.functional_validators @@ -411,7 +411,7 @@ class TargetModel(DefaultPydanticModel): """Target is accessible to user.""" @classmethod - def from_target_description(cls, target_description: TargetDescription) -> Self: + def from_target_description(cls, target_description: TargetDescription) -> TargetModel: return cls( target_name=target_description.target, supports_submit=(target_description.status != TargetStatus.UNSUPPORTED),