classiq 0.32.1__py3-none-any.whl → 0.34.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +2 -1
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_internals/api_wrapper.py +34 -23
- classiq/_internals/jobs.py +41 -14
- classiq/analyzer/__init__.py +3 -1
- classiq/applications/__init__.py +3 -1
- classiq/applications/benchmarking/__init__.py +3 -1
- classiq/applications/chemistry/__init__.py +3 -1
- classiq/applications/combinatorial_optimization/__init__.py +3 -1
- classiq/applications/combinatorial_optimization/examples/__init__.py +3 -1
- classiq/applications/finance/__init__.py +3 -1
- classiq/applications/qnn/__init__.py +3 -1
- classiq/applications/qnn/datasets/__init__.py +3 -1
- classiq/applications/qsvm/qsvm.py +1 -1
- classiq/applications_model_constructors/grover_model_constructor.py +25 -8
- classiq/builtin_functions/__init__.py +3 -1
- classiq/execution/__init__.py +3 -1
- classiq/execution/jobs.py +57 -20
- classiq/executor.py +4 -11
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/backend/backend_preferences.py +17 -0
- classiq/interface/backend/pydantic_backend.py +8 -0
- classiq/interface/backend/quantum_backend_providers.py +15 -1
- classiq/interface/chemistry/ground_state_problem.py +1 -1
- classiq/interface/chemistry/operator.py +198 -0
- classiq/interface/executor/execution_request.py +5 -12
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -1
- classiq/interface/generator/complex_type.py +4 -1
- classiq/interface/generator/functions/__init__.py +3 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +39 -39
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +251 -87
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +5 -54
- classiq/interface/generator/generated_circuit.py +14 -43
- classiq/interface/generator/generated_circuit_data.py +26 -34
- classiq/interface/generator/model/preferences/preferences.py +3 -3
- classiq/interface/generator/partitioned_register.py +1 -1
- classiq/interface/generator/quantum_function_call.py +1 -1
- classiq/interface/generator/validations/validator_functions.py +4 -2
- classiq/interface/hardware.py +3 -2
- classiq/interface/ide/show.py +1 -14
- classiq/interface/model/bind_operation.py +20 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/native_function_definition.py +15 -5
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +8 -3
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +9 -4
- classiq/interface/model/quantum_expressions/quantum_expression.py +10 -5
- classiq/interface/model/quantum_function_call.py +24 -347
- classiq/interface/model/quantum_function_declaration.py +7 -11
- classiq/interface/model/quantum_statement.py +13 -7
- classiq/interface/model/validations/handle_validation_base.py +1 -2
- classiq/interface/model/validations/handles_validator.py +34 -8
- classiq/interface/model/variable_declaration_statement.py +8 -0
- classiq/interface/server/routes.py +11 -16
- classiq/model/__init__.py +3 -1
- classiq/model/function_handler.py +1 -1
- classiq/model/function_handler.pyi +88 -88
- classiq/qmod/declaration_inferrer.py +37 -18
- classiq/qmod/model_state_container.py +6 -3
- classiq/qmod/qmod_builtins.py +892 -4
- classiq/qmod/qmod_parameter.py +24 -8
- classiq/qmod/qmod_variable.py +2 -1
- classiq/qmod/quantum_expandable.py +6 -2
- classiq/qmod/quantum_function.py +11 -10
- classiq/quantum_functions/quantum_function.py +4 -1
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/METADATA +1 -1
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/RECORD +69 -72
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/apps_lib_functions.py +0 -262
- classiq/interface/model/clients/__init__.py +0 -0
- classiq/interface/model/clients/qmod/__init__.py +0 -0
- classiq/interface/model/clients/qmod/qmod_builtins.py +0 -908
- classiq/interface/model/semantics.py +0 -15
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/WHEEL +0 -0
classiq/__init__.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Classiq SDK."""
|
2
2
|
import sys
|
3
|
+
from typing import List
|
3
4
|
|
4
5
|
from classiq.interface._version import VERSION as _VERSION
|
5
6
|
from classiq.interface.generator.application_apis import * # noqa: F403
|
@@ -103,5 +104,5 @@ __all__ = (
|
|
103
104
|
)
|
104
105
|
|
105
106
|
|
106
|
-
def __dir__():
|
107
|
+
def __dir__() -> List[str]:
|
107
108
|
return __all__
|
@@ -53,7 +53,7 @@ async def _create_interactive_loop_async(
|
|
53
53
|
def _wait_for_change(widget: widgets) -> asyncio.Future:
|
54
54
|
future: asyncio.Future = asyncio.Future()
|
55
55
|
|
56
|
-
def _get_value(change) -> None:
|
56
|
+
def _get_value(change) -> None: # type: ignore[no-untyped-def]
|
57
57
|
future.set_result(change.new)
|
58
58
|
widget.unobserve(_get_value, "value")
|
59
59
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import json
|
2
|
-
from typing import
|
2
|
+
from typing import Dict, Optional, Protocol, Type, TypeVar
|
3
3
|
|
4
4
|
import pydantic
|
5
|
-
from pydantic import parse_obj_as
|
6
5
|
|
7
6
|
import classiq.interface.pyomo_extension # noqa: F401 - patches pyomo to add few features
|
8
7
|
from classiq.interface.analyzer import analysis_params, result as analysis_result
|
@@ -19,10 +18,7 @@ from classiq._internals.enum_utils import StrEnum
|
|
19
18
|
from classiq._internals.jobs import JobPoller
|
20
19
|
from classiq.exceptions import ClassiqAPIError, ClassiqValueError
|
21
20
|
|
22
|
-
_FAIL_FAST_INDICATOR = "{"
|
23
21
|
ResultType = TypeVar("ResultType", bound=pydantic.BaseModel)
|
24
|
-
OtherResultType = TypeVar("OtherResultType", bound=pydantic.BaseModel)
|
25
|
-
_Circuit = Union[generator_result.GeneratedCircuit, generator_result.ExecutionCircuit]
|
26
22
|
|
27
23
|
|
28
24
|
class HTTPMethod(StrEnum):
|
@@ -45,16 +41,6 @@ def _parse_job_response(
|
|
45
41
|
return output_type.parse_obj(description)
|
46
42
|
|
47
43
|
|
48
|
-
def _parse_job_response_multiple_outputs(
|
49
|
-
job_result: JobDescription[JSONObject],
|
50
|
-
output_type: Any, # UnionType in Python 3.10,
|
51
|
-
) -> Union[ResultType, OtherResultType]:
|
52
|
-
description = job_result.description
|
53
|
-
if job_result.status != JobStatus.COMPLETED:
|
54
|
-
raise ClassiqAPIError(description["details"])
|
55
|
-
return parse_obj_as(output_type, description)
|
56
|
-
|
57
|
-
|
58
44
|
class ApiWrapper:
|
59
45
|
@classmethod
|
60
46
|
async def _call_task_pydantic(
|
@@ -82,22 +68,45 @@ class ApiWrapper:
|
|
82
68
|
return res
|
83
69
|
|
84
70
|
@classmethod
|
85
|
-
async def call_generation_task(
|
71
|
+
async def call_generation_task(
|
72
|
+
cls, model: ModelInput
|
73
|
+
) -> generator_result.GeneratedCircuit:
|
86
74
|
poller = JobPoller(base_url=routes.TASKS_GENERATE_FULL_PATH)
|
87
75
|
result = await poller.run_pydantic(model, timeout_sec=None)
|
88
|
-
return
|
76
|
+
return _parse_job_response(result, generator_result.GeneratedCircuit)
|
89
77
|
|
90
78
|
@classmethod
|
91
79
|
async def call_execute_generated_circuit(
|
92
|
-
cls,
|
93
|
-
|
94
|
-
) -> JobID:
|
80
|
+
cls, circuit: generator_result.GeneratedCircuit
|
81
|
+
) -> execution_request.ExecutionJobDetails:
|
95
82
|
data = await cls._call_task_pydantic(
|
96
83
|
http_method=HTTPMethod.POST,
|
97
|
-
url=routes.
|
84
|
+
url=routes.EXECUTION_JOBS_FULL_PATH,
|
98
85
|
model=circuit,
|
99
86
|
)
|
100
|
-
return
|
87
|
+
return execution_request.ExecutionJobDetails.parse_obj(data)
|
88
|
+
|
89
|
+
@classmethod
|
90
|
+
async def call_get_execution_job_details(
|
91
|
+
cls,
|
92
|
+
job_id: JobID,
|
93
|
+
) -> execution_request.ExecutionJobDetails:
|
94
|
+
data = await cls._call_task(
|
95
|
+
http_method=HTTPMethod.GET,
|
96
|
+
url=f"{routes.EXECUTION_JOBS_FULL_PATH }/{job_id.job_id}",
|
97
|
+
)
|
98
|
+
return execution_request.ExecutionJobDetails.parse_obj(data)
|
99
|
+
|
100
|
+
@classmethod
|
101
|
+
async def call_get_execution_job_result(
|
102
|
+
cls,
|
103
|
+
job_id: JobID,
|
104
|
+
) -> execution_request.ExecuteGeneratedCircuitResults:
|
105
|
+
data = await cls._call_task(
|
106
|
+
http_method=HTTPMethod.GET,
|
107
|
+
url=f"{routes.EXECUTION_JOBS_FULL_PATH }/{job_id.job_id}/result",
|
108
|
+
)
|
109
|
+
return execution_request.ExecuteGeneratedCircuitResults.parse_obj(data)
|
101
110
|
|
102
111
|
@classmethod
|
103
112
|
async def call_execute_estimate(
|
@@ -209,7 +218,9 @@ class ApiWrapper:
|
|
209
218
|
async def call_generate_hamiltonian_task(
|
210
219
|
cls, problem: ground_state_problem.CHEMISTRY_PROBLEMS_TYPE
|
211
220
|
) -> operator.PauliOperator:
|
212
|
-
poller = JobPoller(
|
221
|
+
poller = JobPoller(
|
222
|
+
base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH, use_versioned_url=False
|
223
|
+
)
|
213
224
|
result = await poller.run_pydantic(problem, timeout_sec=None)
|
214
225
|
return _parse_job_response(result, operator.PauliOperator)
|
215
226
|
|
classiq/_internals/jobs.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
-
from typing import Dict, Iterable, Optional, Set
|
3
|
+
from typing import Callable, Dict, Iterable, Optional, Set, TypeVar
|
4
4
|
|
5
5
|
import httpx
|
6
6
|
import pydantic
|
@@ -15,6 +15,7 @@ from classiq.exceptions import ClassiqAPIError
|
|
15
15
|
_URL_PATH_SEP = "/"
|
16
16
|
GeneralJobDescription = JobDescription[JSONObject]
|
17
17
|
_logger = logging.getLogger(__name__)
|
18
|
+
T = TypeVar("T")
|
18
19
|
|
19
20
|
|
20
21
|
def _join_url_path(*parts: str) -> str:
|
@@ -33,6 +34,17 @@ def _join_url_path(*parts: str) -> str:
|
|
33
34
|
)
|
34
35
|
|
35
36
|
|
37
|
+
def _general_job_description_parser(
|
38
|
+
json_response: JSONObject,
|
39
|
+
) -> Optional[GeneralJobDescription]:
|
40
|
+
job_description: GeneralJobDescription = GeneralJobDescription.parse_obj(
|
41
|
+
json_response
|
42
|
+
)
|
43
|
+
if job_description.status.is_final():
|
44
|
+
return job_description
|
45
|
+
return None
|
46
|
+
|
47
|
+
|
36
48
|
class JobPoller:
|
37
49
|
INITIAL_INTERVAL_SEC = 1
|
38
50
|
INTERVAL_FACTOR = 2
|
@@ -40,11 +52,18 @@ class JobPoller:
|
|
40
52
|
DEV_INTERVAL = 0.05
|
41
53
|
|
42
54
|
def __init__(
|
43
|
-
self,
|
55
|
+
self,
|
56
|
+
base_url: str,
|
57
|
+
required_headers: Optional[Set[str]] = None,
|
58
|
+
use_versioned_url: bool = True,
|
44
59
|
) -> None:
|
45
60
|
self._required_headers = required_headers or set()
|
46
61
|
client_instance = client()
|
47
|
-
self._base_url =
|
62
|
+
self._base_url = (
|
63
|
+
client_instance.make_versioned_url(base_url)
|
64
|
+
if use_versioned_url
|
65
|
+
else base_url
|
66
|
+
)
|
48
67
|
self._async_client = client_instance.async_client()
|
49
68
|
self._mode = client_instance.config.mode
|
50
69
|
|
@@ -95,9 +114,12 @@ class JobPoller:
|
|
95
114
|
interval = min(interval * self.INTERVAL_FACTOR, self.FINAL_INTERVAL_SEC)
|
96
115
|
|
97
116
|
async def _poll(
|
98
|
-
self,
|
99
|
-
|
100
|
-
|
117
|
+
self,
|
118
|
+
poll_url: str,
|
119
|
+
timeout_sec: Optional[float],
|
120
|
+
response_parser: Callable[[JSONObject], Optional[T]] = _general_job_description_parser, # type: ignore[assignment]
|
121
|
+
) -> T:
|
122
|
+
async def poller() -> JSONObject:
|
101
123
|
nonlocal self, poll_url
|
102
124
|
raw_response = await self._request(http_method="GET", url=poll_url)
|
103
125
|
return raw_response.json()
|
@@ -105,19 +127,24 @@ class JobPoller:
|
|
105
127
|
async for json_response in poll_for(
|
106
128
|
poller, timeout_sec=timeout_sec, interval_sec=self._interval_sec()
|
107
129
|
):
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
if job_description.status.is_final():
|
112
|
-
return job_description
|
130
|
+
parsed = response_parser(json_response)
|
131
|
+
if parsed is not None:
|
132
|
+
return parsed
|
113
133
|
raise ClassiqAPIError("API request timed out")
|
114
134
|
|
115
135
|
async def poll(
|
116
|
-
self,
|
117
|
-
|
136
|
+
self,
|
137
|
+
job_id: JobID,
|
138
|
+
timeout_sec: Optional[float],
|
139
|
+
response_parser: Callable[[JSONObject], Optional[T]] = _general_job_description_parser, # type: ignore[assignment]
|
140
|
+
) -> T:
|
118
141
|
poll_url = self._make_poll_url(job_id=job_id)
|
119
142
|
async with self._async_client:
|
120
|
-
return await self._poll(
|
143
|
+
return await self._poll(
|
144
|
+
poll_url=poll_url,
|
145
|
+
response_parser=response_parser,
|
146
|
+
timeout_sec=timeout_sec,
|
147
|
+
)
|
121
148
|
|
122
149
|
async def _cancel(self, poll_url: str) -> None:
|
123
150
|
_logger.info("Cancelling job %s", poll_url, exc_info=True)
|
classiq/analyzer/__init__.py
CHANGED
classiq/applications/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
1
3
|
from classiq.applications import (
|
2
4
|
benchmarking,
|
3
5
|
chemistry,
|
@@ -18,5 +20,5 @@ __all__ = [
|
|
18
20
|
_NON_IMPORTED_PUBLIC_SUBMODULES = ["qnn"]
|
19
21
|
|
20
22
|
|
21
|
-
def __dir__():
|
23
|
+
def __dir__() -> List[str]:
|
22
24
|
return __all__ + _NON_IMPORTED_PUBLIC_SUBMODULES
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
1
3
|
from classiq.interface.combinatorial_optimization import examples
|
2
4
|
from classiq.interface.combinatorial_optimization.solver_types import QSolver
|
3
5
|
|
@@ -11,5 +13,5 @@ __all__ = [
|
|
11
13
|
]
|
12
14
|
|
13
15
|
|
14
|
-
def __dir__():
|
16
|
+
def __dir__() -> List[str]:
|
15
17
|
return __all__
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# This file will be called first whenever any file from within this directory is imported.
|
2
2
|
# Thus, we'll test dependencies only here, once.
|
3
|
+
from typing import List
|
4
|
+
|
3
5
|
try:
|
4
6
|
import torch
|
5
7
|
except ImportError as exc:
|
@@ -11,5 +13,5 @@ from ..qnn.qlayer import QLayer
|
|
11
13
|
__all__ = ["datasets", "types", "QLayer"]
|
12
14
|
|
13
15
|
|
14
|
-
def __dir__():
|
16
|
+
def __dir__() -> List[str]:
|
15
17
|
return __all__
|
@@ -4,6 +4,7 @@ from classiq.interface.generator.expressions.expression import Expression
|
|
4
4
|
from classiq.interface.generator.functions.port_declaration import (
|
5
5
|
PortDeclarationDirection,
|
6
6
|
)
|
7
|
+
from classiq.interface.model.bind_operation import BindOperation
|
7
8
|
from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
|
8
9
|
from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
|
9
10
|
from classiq.interface.model.model import Model, SerializedModel
|
@@ -16,6 +17,7 @@ from classiq.interface.model.quantum_function_call import (
|
|
16
17
|
QuantumFunctionCall,
|
17
18
|
QuantumLambdaFunction,
|
18
19
|
)
|
20
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
19
21
|
from classiq.interface.model.quantum_type import (
|
20
22
|
QuantumFixedReal,
|
21
23
|
QuantumInteger,
|
@@ -33,7 +35,7 @@ def split_registers(
|
|
33
35
|
register_names: List[str],
|
34
36
|
register_sizes: List[int],
|
35
37
|
input_wire_name: str,
|
36
|
-
) -> List[
|
38
|
+
) -> List[QuantumStatement]:
|
37
39
|
if len(register_names) == 0:
|
38
40
|
return []
|
39
41
|
wires = (
|
@@ -41,6 +43,15 @@ def split_registers(
|
|
41
43
|
+ [f"split{i}" for i in range(len(register_names) - 2)]
|
42
44
|
+ [register_names[-1]]
|
43
45
|
)
|
46
|
+
|
47
|
+
if len(register_names) == 1:
|
48
|
+
return [
|
49
|
+
BindOperation(
|
50
|
+
in_handle=HandleBinding(name=wires[0]),
|
51
|
+
out_handle=HandleBinding(name=wires[1]),
|
52
|
+
)
|
53
|
+
]
|
54
|
+
|
44
55
|
return [
|
45
56
|
QuantumFunctionCall(
|
46
57
|
function="split",
|
@@ -119,6 +130,18 @@ def grover_main_port_declarations(
|
|
119
130
|
}
|
120
131
|
|
121
132
|
|
133
|
+
def _generate_local_variable_declarations(
|
134
|
+
definitions: List[Tuple[str, RegisterUserInput]]
|
135
|
+
) -> List[LocalVariableDeclaration]:
|
136
|
+
ret = [LocalVariableDeclaration(name="gsq")]
|
137
|
+
if len(definitions) >= 2:
|
138
|
+
ret += [
|
139
|
+
LocalVariableDeclaration(name=f"split{i}")
|
140
|
+
for i in range(len(definitions) - 2)
|
141
|
+
]
|
142
|
+
return ret
|
143
|
+
|
144
|
+
|
122
145
|
def construct_grover_model(
|
123
146
|
definitions: List[Tuple[str, RegisterUserInput]],
|
124
147
|
expression: str,
|
@@ -150,13 +173,7 @@ def construct_grover_model(
|
|
150
173
|
port_declarations=grover_main_port_declarations(
|
151
174
|
definitions, PortDeclarationDirection.Output
|
152
175
|
),
|
153
|
-
local_handles=
|
154
|
-
LocalVariableDeclaration(name="gsq"),
|
155
|
-
*[
|
156
|
-
LocalVariableDeclaration(name=f"split{i}")
|
157
|
-
for i in range(len(definitions) - 2)
|
158
|
-
],
|
159
|
-
],
|
176
|
+
local_handles=_generate_local_variable_declarations(definitions),
|
160
177
|
body=[
|
161
178
|
QuantumFunctionCall(
|
162
179
|
function="grover_search",
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
1
3
|
from classiq.interface.generator.function_param_list import * # noqa: F403
|
2
4
|
from classiq.interface.generator.function_param_list_without_self_reference import * # noqa: F403
|
3
5
|
from classiq.interface.generator.oracles.oracle_function_param_list import * # noqa: F403
|
@@ -37,5 +39,5 @@ __all__ = (
|
|
37
39
|
)
|
38
40
|
|
39
41
|
|
40
|
-
def __dir__():
|
42
|
+
def __dir__() -> List[str]:
|
41
43
|
return __all__
|
classiq/execution/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
1
3
|
from ..executor import * # noqa: F403
|
2
4
|
from ..executor import __all__ as _exec_all
|
3
5
|
from ..interface.backend.backend_preferences import * # noqa: F403
|
@@ -17,5 +19,5 @@ __all__ = (
|
|
17
19
|
)
|
18
20
|
|
19
21
|
|
20
|
-
def __dir__():
|
22
|
+
def __dir__() -> List[str]:
|
21
23
|
return __all__
|
classiq/execution/jobs.py
CHANGED
@@ -1,40 +1,77 @@
|
|
1
|
-
from dataclasses import dataclass, field
|
2
1
|
from typing import Optional
|
3
2
|
|
3
|
+
from pydantic import PrivateAttr
|
4
|
+
|
4
5
|
from classiq.interface.executor.execution_request import (
|
5
|
-
|
6
|
+
ExecutionJobDetails,
|
6
7
|
ResultsCollection,
|
7
8
|
)
|
8
|
-
from classiq.interface.
|
9
|
+
from classiq.interface.jobs import JobStatus, JSONObject
|
10
|
+
from classiq.interface.server.routes import EXECUTION_JOBS_FULL_PATH
|
9
11
|
|
10
|
-
from classiq._internals.api_wrapper import
|
12
|
+
from classiq._internals.api_wrapper import ApiWrapper
|
11
13
|
from classiq._internals.async_utils import syncify_function
|
12
14
|
from classiq._internals.jobs import JobID, JobPoller
|
15
|
+
from classiq.exceptions import ClassiqAPIError
|
16
|
+
|
17
|
+
|
18
|
+
class ExecutionJob(ExecutionJobDetails):
|
19
|
+
_result: Optional[ResultsCollection] = PrivateAttr(default=None)
|
20
|
+
|
21
|
+
def __init__(self, details: ExecutionJobDetails) -> None:
|
22
|
+
super().__init__(**details.dict())
|
13
23
|
|
24
|
+
def _update_details(self, details: ExecutionJobDetails) -> None:
|
25
|
+
for k, v in details.dict().items():
|
26
|
+
setattr(self, k, v)
|
14
27
|
|
15
|
-
@
|
16
|
-
|
17
|
-
|
18
|
-
|
28
|
+
@classmethod
|
29
|
+
async def from_id_async(cls, id: str) -> "ExecutionJob":
|
30
|
+
details = await ApiWrapper.call_get_execution_job_details(JobID(job_id=id))
|
31
|
+
return cls(details)
|
32
|
+
|
33
|
+
@classmethod
|
34
|
+
def from_id(cls, id: str) -> "ExecutionJob":
|
35
|
+
return syncify_function(cls.from_id_async)(id)
|
36
|
+
|
37
|
+
@property
|
38
|
+
def _job_id(self) -> JobID:
|
39
|
+
return JobID(job_id=self.id)
|
19
40
|
|
20
41
|
async def result_async(
|
21
42
|
self, timeout_sec: Optional[float] = None
|
22
43
|
) -> ResultsCollection:
|
44
|
+
await self.poll_async(timeout_sec=timeout_sec)
|
45
|
+
|
46
|
+
if self.status == JobStatus.FAILED:
|
47
|
+
raise ClassiqAPIError(self.error or "")
|
48
|
+
if self.status == JobStatus.CANCELLED:
|
49
|
+
raise ClassiqAPIError("Job has been cancelled.")
|
50
|
+
|
23
51
|
if self._result is None:
|
24
|
-
self._result =
|
52
|
+
self._result = (
|
53
|
+
await ApiWrapper.call_get_execution_job_result(self._job_id)
|
54
|
+
).results
|
25
55
|
return self._result
|
26
56
|
|
27
57
|
result = syncify_function(result_async)
|
28
58
|
|
29
|
-
async def
|
30
|
-
self
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
59
|
+
async def poll_async(self, timeout_sec: Optional[float] = None) -> None:
|
60
|
+
if not self.status.is_final():
|
61
|
+
await self._poll_job(timeout_sec=timeout_sec)
|
62
|
+
|
63
|
+
poll = syncify_function(poll_async)
|
64
|
+
|
65
|
+
async def _poll_job(self, timeout_sec: Optional[float] = None) -> None:
|
66
|
+
def response_parser(json_response: JSONObject) -> Optional[bool]:
|
67
|
+
self._update_details(ExecutionJobDetails.parse_obj(json_response))
|
68
|
+
if self.status.is_final():
|
69
|
+
return True
|
70
|
+
return None
|
71
|
+
|
72
|
+
poller = JobPoller(base_url=EXECUTION_JOBS_FULL_PATH)
|
73
|
+
await poller.poll(
|
74
|
+
job_id=self._job_id,
|
75
|
+
response_parser=response_parser,
|
76
|
+
timeout_sec=timeout_sec,
|
39
77
|
)
|
40
|
-
return raw_result.results
|
classiq/executor.py
CHANGED
@@ -3,7 +3,6 @@ import functools
|
|
3
3
|
from typing import Optional, Tuple, Union
|
4
4
|
|
5
5
|
import more_itertools
|
6
|
-
from pydantic import parse_raw_as
|
7
6
|
from typing_extensions import TypeAlias
|
8
7
|
|
9
8
|
from classiq.interface.backend.backend_preferences import BackendPreferencesTypes
|
@@ -19,10 +18,7 @@ from classiq.interface.executor.execution_request import (
|
|
19
18
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
20
19
|
from classiq.interface.executor.quantum_program import MultipleArguments, QuantumProgram
|
21
20
|
from classiq.interface.executor.result import ExecutionDetails
|
22
|
-
from classiq.interface.generator.generated_circuit import
|
23
|
-
ExecutionCircuit,
|
24
|
-
GeneratedCircuit,
|
25
|
-
)
|
21
|
+
from classiq.interface.generator.generated_circuit import GeneratedCircuit
|
26
22
|
|
27
23
|
from classiq._internals.api_wrapper import ApiWrapper
|
28
24
|
from classiq._internals.async_utils import syncify_function
|
@@ -40,17 +36,14 @@ _MAX_ARGUMENTS_SIZE = 1024
|
|
40
36
|
|
41
37
|
def _parse_serialized_qprog(
|
42
38
|
quantum_program: SerializedQuantumProgram,
|
43
|
-
) ->
|
44
|
-
return
|
45
|
-
Union[GeneratedCircuit, ExecutionCircuit], # type:ignore[arg-type]
|
46
|
-
quantum_program,
|
47
|
-
)
|
39
|
+
) -> GeneratedCircuit:
|
40
|
+
return GeneratedCircuit.parse_raw(quantum_program)
|
48
41
|
|
49
42
|
|
50
43
|
async def execute_async(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
51
44
|
circuit = _parse_serialized_qprog(quantum_program)
|
52
45
|
result = await ApiWrapper.call_execute_generated_circuit(circuit)
|
53
|
-
return ExecutionJob(
|
46
|
+
return ExecutionJob(details=result)
|
54
47
|
|
55
48
|
|
56
49
|
execute = syncify_function(execute_async)
|
classiq/interface/_version.py
CHANGED
@@ -9,6 +9,7 @@ from pydantic import BaseModel, validator
|
|
9
9
|
from classiq.interface.backend import pydantic_backend
|
10
10
|
from classiq.interface.backend.quantum_backend_providers import (
|
11
11
|
EXACT_SIMULATORS,
|
12
|
+
AliceBobBackendNames,
|
12
13
|
AmazonBraketBackendNames,
|
13
14
|
AzureQuantumBackendNames,
|
14
15
|
ClassiqAerBackendNames,
|
@@ -55,6 +56,19 @@ class BackendPreferences(BaseModel):
|
|
55
56
|
AWS_DEFAULT_JOB_TIMEOUT_SECONDS = int(timedelta(minutes=5).total_seconds())
|
56
57
|
|
57
58
|
|
59
|
+
class AliceBobBackendPreferences(BackendPreferences):
|
60
|
+
backend_service_provider: ProviderTypeVendor.ALICE_BOB
|
61
|
+
api_key: pydantic_backend.PydanticAliceBobApiKeyType = pydantic.Field(
|
62
|
+
..., description="AliceBob API key"
|
63
|
+
)
|
64
|
+
|
65
|
+
@pydantic.root_validator(pre=True)
|
66
|
+
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
67
|
+
return values_with_discriminator(
|
68
|
+
values, "backend_service_provider", ProviderVendor.ALICE_AND_BOB
|
69
|
+
)
|
70
|
+
|
71
|
+
|
58
72
|
class ClassiqBackendPreferences(BackendPreferences):
|
59
73
|
backend_service_provider: ProviderTypeVendor.CLASSIQ
|
60
74
|
|
@@ -214,6 +228,7 @@ BackendPreferencesTypes = Union[
|
|
214
228
|
AwsBackendPreferences,
|
215
229
|
IonqBackendPreferences,
|
216
230
|
GCPBackendPreferences,
|
231
|
+
AliceBobBackendPreferences,
|
217
232
|
]
|
218
233
|
|
219
234
|
__all__ = [
|
@@ -230,6 +245,8 @@ __all__ = [
|
|
230
245
|
"IonqBackendNames",
|
231
246
|
"ClassiqNvidiaBackendNames",
|
232
247
|
"GCPBackendPreferences",
|
248
|
+
"AliceBobBackendPreferences",
|
249
|
+
"AliceBobBackendNames",
|
233
250
|
]
|
234
251
|
|
235
252
|
|