classiq 0.36.1__py3-none-any.whl → 0.37.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 +1 -0
- classiq/_internals/api_wrapper.py +24 -6
- classiq/_internals/authentication/device.py +6 -3
- classiq/_internals/authentication/token_manager.py +21 -5
- classiq/_internals/client.py +7 -2
- classiq/_internals/config.py +12 -0
- classiq/_internals/host_checker.py +1 -1
- classiq/_internals/jobs.py +3 -1
- classiq/_internals/type_validation.py +3 -6
- classiq/analyzer/analyzer.py +1 -0
- classiq/analyzer/rb.py +3 -5
- classiq/applications_model_constructors/chemistry_model_constructor.py +0 -1
- classiq/applications_model_constructors/grover_model_constructor.py +27 -18
- classiq/execution/jobs.py +13 -4
- classiq/executor.py +3 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +0 -6
- classiq/interface/analyzer/result.py +0 -4
- classiq/interface/backend/backend_preferences.py +2 -2
- classiq/interface/backend/quantum_backend_providers.py +1 -1
- classiq/interface/execution/resource_estimator.py +7 -0
- classiq/interface/execution/result.py +5 -0
- classiq/interface/generator/ansatz_library.py +3 -3
- classiq/interface/generator/arith/binary_ops.py +1 -3
- classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
- classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +140 -14
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +3 -20
- classiq/interface/generator/functions/native_function_definition.py +3 -3
- classiq/interface/generator/model/constraints.py +3 -3
- classiq/interface/generator/model/preferences/preferences.py +10 -8
- classiq/interface/generator/noise_properties.py +5 -5
- classiq/interface/generator/qpe.py +5 -5
- classiq/interface/generator/quantum_function_call.py +5 -3
- classiq/interface/generator/visitor.py +1 -2
- classiq/interface/hardware.py +1 -1
- classiq/interface/model/native_function_definition.py +2 -24
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -2
- classiq/interface/model/quantum_expressions/control_state.py +38 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +12 -9
- classiq/interface/model/quantum_function_call.py +3 -0
- classiq/interface/model/quantum_function_declaration.py +3 -3
- classiq/interface/model/quantum_if_operation.py +95 -0
- classiq/interface/model/validations/handles_validator.py +7 -15
- classiq/interface/server/routes.py +10 -6
- classiq/model/function_handler.pyi +85 -85
- classiq/model/model.py +1 -0
- classiq/qmod/__init__.py +4 -1
- classiq/qmod/builtins/__init__.py +13 -1
- classiq/qmod/builtins/classical_execution_primitives.py +109 -0
- classiq/qmod/builtins/classical_functions.py +68 -0
- classiq/qmod/builtins/functions.py +47 -21
- classiq/qmod/builtins/operations.py +15 -29
- classiq/qmod/classical_function.py +40 -0
- classiq/qmod/declaration_inferrer.py +5 -2
- classiq/qmod/qmod_variable.py +15 -3
- classiq/qmod/quantum_callable.py +24 -3
- classiq/qmod/quantum_expandable.py +99 -17
- classiq/qmod/quantum_function.py +12 -2
- classiq/qmod/symbolic.py +109 -107
- classiq/qmod/symbolic_expr.py +1 -4
- classiq/qmod/symbolic_type.py +8 -0
- classiq/quantum_functions/decorators.py +2 -4
- classiq/quantum_functions/function_library.py +1 -0
- {classiq-0.36.1.dist-info → classiq-0.37.0.dist-info}/METADATA +1 -1
- {classiq-0.36.1.dist-info → classiq-0.37.0.dist-info}/RECORD +69 -61
- classiq/interface/model/local_variable_declaration.py +0 -7
- {classiq-0.36.1.dist-info → classiq-0.37.0.dist-info}/WHEEL +0 -0
classiq/__init__.py
CHANGED
@@ -24,6 +24,10 @@ from classiq._internals.jobs import JobPoller
|
|
24
24
|
from classiq.exceptions import ClassiqAPIError, ClassiqValueError
|
25
25
|
|
26
26
|
ResultType = TypeVar("ResultType", bound=pydantic.BaseModel)
|
27
|
+
CLASSIQ_ACCEPT_HEADER = "X-Classiq-Accept-Version"
|
28
|
+
|
29
|
+
_ACCEPT_HEADER = "X-Classiq-Accept-Version"
|
30
|
+
_CONTENT_TYPE_HEADER = "X-Classiq-Content-Type-Version"
|
27
31
|
|
28
32
|
|
29
33
|
class HTTPMethod(StrEnum):
|
@@ -72,11 +76,13 @@ class ApiWrapper:
|
|
72
76
|
body: Optional[Dict] = None,
|
73
77
|
params: Optional[Dict] = None,
|
74
78
|
use_versioned_url: bool = True,
|
79
|
+
headers: Optional[Dict[str, str]] = None,
|
75
80
|
) -> dict:
|
76
81
|
res = await client().call_api(
|
77
82
|
http_method=http_method,
|
78
83
|
url=url,
|
79
84
|
body=body,
|
85
|
+
headers=headers,
|
80
86
|
params=params,
|
81
87
|
use_versioned_url=use_versioned_url,
|
82
88
|
)
|
@@ -101,10 +107,16 @@ class ApiWrapper:
|
|
101
107
|
url=routes.CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL,
|
102
108
|
model=circuit,
|
103
109
|
)
|
110
|
+
headers = {
|
111
|
+
_ACCEPT_HEADER: "v1",
|
112
|
+
_CONTENT_TYPE_HEADER: execution_input["version"],
|
113
|
+
}
|
104
114
|
data = await cls._call_task(
|
105
115
|
http_method=HTTPMethod.POST,
|
106
|
-
|
116
|
+
headers=headers,
|
117
|
+
url=routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
|
107
118
|
body=execution_input,
|
119
|
+
use_versioned_url=False,
|
108
120
|
)
|
109
121
|
return execution_request.ExecutionJobDetails.parse_obj(data)
|
110
122
|
|
@@ -113,9 +125,12 @@ class ApiWrapper:
|
|
113
125
|
cls,
|
114
126
|
job_id: JobID,
|
115
127
|
) -> execution_request.ExecutionJobDetails:
|
128
|
+
headers = {_CONTENT_TYPE_HEADER: "v1"}
|
116
129
|
data = await cls._call_task(
|
117
130
|
http_method=HTTPMethod.GET,
|
118
|
-
|
131
|
+
headers=headers,
|
132
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
|
133
|
+
use_versioned_url=False,
|
119
134
|
)
|
120
135
|
return execution_request.ExecutionJobDetails.parse_obj(data)
|
121
136
|
|
@@ -123,11 +138,13 @@ class ApiWrapper:
|
|
123
138
|
async def call_get_execution_job_result(
|
124
139
|
cls,
|
125
140
|
job_id: JobID,
|
141
|
+
version: str,
|
126
142
|
) -> classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults:
|
127
143
|
data = await cls._call_task(
|
128
144
|
http_method=HTTPMethod.GET,
|
129
|
-
url=f"{routes.
|
145
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/result",
|
130
146
|
use_versioned_url=False,
|
147
|
+
headers={CLASSIQ_ACCEPT_HEADER: version},
|
131
148
|
)
|
132
149
|
return classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults.parse_obj(
|
133
150
|
data
|
@@ -141,7 +158,7 @@ class ApiWrapper:
|
|
141
158
|
) -> ExecutionJobDetailsV1:
|
142
159
|
data = await cls._call_task(
|
143
160
|
http_method=HTTPMethod.PATCH,
|
144
|
-
url=f"{routes.
|
161
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
|
145
162
|
params={
|
146
163
|
"name": name,
|
147
164
|
},
|
@@ -157,7 +174,7 @@ class ApiWrapper:
|
|
157
174
|
) -> ExecutionJobsQueryResultsV1:
|
158
175
|
data = await cls._call_task(
|
159
176
|
http_method=HTTPMethod.GET,
|
160
|
-
url=f"{routes.
|
177
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}",
|
161
178
|
params={
|
162
179
|
"offset": offset,
|
163
180
|
"limit": limit,
|
@@ -277,7 +294,8 @@ class ApiWrapper:
|
|
277
294
|
cls, problem: ground_state_problem.CHEMISTRY_PROBLEMS_TYPE
|
278
295
|
) -> operator.PauliOperator:
|
279
296
|
poller = JobPoller(
|
280
|
-
base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH,
|
297
|
+
base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH,
|
298
|
+
use_versioned_url=False,
|
281
299
|
)
|
282
300
|
result = await poller.run_pydantic(problem, timeout_sec=None)
|
283
301
|
return _parse_job_response(result, operator.PauliOperator)
|
@@ -18,7 +18,9 @@ class DeviceRegistrar:
|
|
18
18
|
_TIMEOUT_SEC: float = timedelta(minutes=15).total_seconds()
|
19
19
|
|
20
20
|
@classmethod
|
21
|
-
async def register(
|
21
|
+
async def register(
|
22
|
+
cls, get_refresh_token: bool = True, text_only: bool = False
|
23
|
+
) -> Tokens:
|
22
24
|
auth0_client = Auth0()
|
23
25
|
data: Dict[str, Any] = await auth0_client.get_device_data(
|
24
26
|
get_refresh_token=get_refresh_token
|
@@ -27,9 +29,10 @@ class DeviceRegistrar:
|
|
27
29
|
print(f"Your user code: {data['user_code']}")
|
28
30
|
verification_url = data["verification_uri_complete"]
|
29
31
|
print(
|
30
|
-
f"If a browser doesn't automatically open, please visit
|
32
|
+
f"If a browser doesn't automatically open, please visit this URL from any trusted device: {verification_url}"
|
31
33
|
)
|
32
|
-
|
34
|
+
if not text_only:
|
35
|
+
webbrowser.open(verification_url)
|
33
36
|
timeout = min(data["expires_in"], cls._TIMEOUT_SEC)
|
34
37
|
return await cls._poll_tokens(
|
35
38
|
auth0_client=auth0_client,
|
@@ -7,6 +7,7 @@ from typing import Optional
|
|
7
7
|
from classiq._internals.authentication import password_manager as pm
|
8
8
|
from classiq._internals.authentication.auth0 import Auth0
|
9
9
|
from classiq._internals.authentication.device import DeviceRegistrar, Tokens
|
10
|
+
from classiq._internals.config import Configuration
|
10
11
|
from classiq.exceptions import (
|
11
12
|
ClassiqAuthenticationError,
|
12
13
|
ClassiqPasswordManagerSelectionError,
|
@@ -17,9 +18,14 @@ _logger = logging.getLogger(__name__)
|
|
17
18
|
|
18
19
|
|
19
20
|
class TokenManager:
|
20
|
-
def __init__(
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
config: Configuration,
|
24
|
+
password_manager: Optional[pm.PasswordManager] = None,
|
25
|
+
) -> None:
|
26
|
+
self._config = config
|
21
27
|
if password_manager is None:
|
22
|
-
password_manager = self._make_password_manager()
|
28
|
+
password_manager = self._make_password_manager(self._config)
|
23
29
|
# We use threading.Lock instead of asyncio.Lock because the latter is coupled
|
24
30
|
# to a specific event loop, which is undesirable
|
25
31
|
self._lock = threading.Lock()
|
@@ -32,15 +38,23 @@ class TokenManager:
|
|
32
38
|
)
|
33
39
|
|
34
40
|
@classmethod
|
35
|
-
def
|
41
|
+
def _should_skip_authentication(cls) -> bool:
|
36
42
|
parser = argparse.ArgumentParser()
|
37
43
|
parser.add_argument(
|
38
44
|
"--skip-authentication", action="store_true", required=False
|
39
45
|
)
|
40
46
|
args, _ = parser.parse_known_args()
|
41
47
|
|
42
|
-
|
48
|
+
return args.skip_authentication
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def _make_password_manager(cls, config: Configuration) -> pm.PasswordManager:
|
52
|
+
should_skip_authentication = cls._should_skip_authentication()
|
53
|
+
|
54
|
+
if should_skip_authentication:
|
43
55
|
return pm.DummyPasswordManager()
|
56
|
+
if config.text_only:
|
57
|
+
return pm.FilePasswordManager()
|
44
58
|
for password_manager in PASSWORD_MANAGERS:
|
45
59
|
if password_manager.is_supported():
|
46
60
|
return password_manager
|
@@ -108,5 +122,7 @@ class TokenManager:
|
|
108
122
|
async def _authentication_helper(self) -> None:
|
109
123
|
# TODO: consider using refresh token rotation
|
110
124
|
# (https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation)
|
111
|
-
tokens = await DeviceRegistrar.register(
|
125
|
+
tokens = await DeviceRegistrar.register(
|
126
|
+
get_refresh_token=True, text_only=self._config.text_only
|
127
|
+
)
|
112
128
|
self._save_tokens(tokens, force_override_refresh_token=True)
|
classiq/_internals/client.py
CHANGED
@@ -135,7 +135,7 @@ class Client:
|
|
135
135
|
|
136
136
|
def __init__(self, conf: config.Configuration) -> None:
|
137
137
|
self._config = conf
|
138
|
-
self._token_manager = token_manager.TokenManager()
|
138
|
+
self._token_manager = token_manager.TokenManager(config=self._config)
|
139
139
|
self._ssl_context = ssl.create_default_context()
|
140
140
|
self._HTTP_TIMEOUT_SECONDS = (
|
141
141
|
3600 # Needs to be synced with load-balancer timeout
|
@@ -205,6 +205,7 @@ class Client:
|
|
205
205
|
body: Optional[Dict] = None,
|
206
206
|
params: Optional[Dict] = None,
|
207
207
|
use_versioned_url: bool = True,
|
208
|
+
headers: Optional[Dict[str, str]] = None,
|
208
209
|
) -> Union[Dict, str]:
|
209
210
|
if use_versioned_url:
|
210
211
|
url = self.make_versioned_url(url)
|
@@ -214,6 +215,7 @@ class Client:
|
|
214
215
|
url=url,
|
215
216
|
json=body,
|
216
217
|
params=params,
|
218
|
+
headers=headers,
|
217
219
|
)
|
218
220
|
self.handle_response(response)
|
219
221
|
return response.json()
|
@@ -223,12 +225,15 @@ class Client:
|
|
223
225
|
http_method: str,
|
224
226
|
url: str,
|
225
227
|
body: Optional[Dict] = None,
|
228
|
+
headers: Optional[Dict] = None,
|
226
229
|
use_versioned_url: bool = True,
|
227
230
|
) -> Union[Dict, str]:
|
228
231
|
if use_versioned_url:
|
229
232
|
url = self.make_versioned_url(url)
|
230
233
|
with httpx.Client(**self._make_client_args()) as sync_client:
|
231
|
-
response = sync_client.request(
|
234
|
+
response = sync_client.request(
|
235
|
+
method=http_method, url=url, json=body, headers=headers
|
236
|
+
)
|
232
237
|
self.handle_response(response)
|
233
238
|
return response.json()
|
234
239
|
|
classiq/_internals/config.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Configuration of the SDK module."""
|
2
|
+
|
2
3
|
import os
|
3
4
|
import pathlib
|
4
5
|
from typing import List, Optional, Union
|
@@ -32,6 +33,9 @@ class Configuration(BaseModel):
|
|
32
33
|
mode: SDKMode = pydantic.Field(
|
33
34
|
default=SDKMode.PROD, description="The operational mode of the SDK"
|
34
35
|
)
|
36
|
+
text_only: bool = pydantic.Field(
|
37
|
+
default=False, description="Should classiq avoid relying on GUI"
|
38
|
+
)
|
35
39
|
|
36
40
|
|
37
41
|
_DEFAULT_CONFIG_FILES = [str(pathlib.Path("classiq", "config.ini"))]
|
@@ -94,6 +98,13 @@ def init(args: Optional[Union[str, List[str]]] = None) -> Configuration:
|
|
94
98
|
type=SDKMode,
|
95
99
|
default=SDKMode.PROD,
|
96
100
|
)
|
101
|
+
arg_parser.add_argument(
|
102
|
+
"--text-only",
|
103
|
+
dest="classiq_text_only",
|
104
|
+
help="Should classiq avoid relying on GUI",
|
105
|
+
env_var="CLASSIQ_TEXT_ONLY",
|
106
|
+
action="store_true",
|
107
|
+
)
|
97
108
|
|
98
109
|
parsed_args, _ = arg_parser.parse_known_args(args=args)
|
99
110
|
return Configuration(
|
@@ -101,4 +112,5 @@ def init(args: Optional[Union[str, List[str]]] = None) -> Configuration:
|
|
101
112
|
ide=parsed_args.classiq_ide,
|
102
113
|
should_check_host=not parsed_args.classiq_skip_check_host,
|
103
114
|
mode=parsed_args.classiq_mode,
|
115
|
+
text_only=parsed_args.classiq_text_only,
|
104
116
|
)
|
@@ -45,7 +45,7 @@ class HostChecker:
|
|
45
45
|
if lhs_version == cls._UNKNOWN_VERSION or rhs_version == cls._UNKNOWN_VERSION:
|
46
46
|
# In case one of those versions is unknown, they are considered equal
|
47
47
|
_logger.debug(
|
48
|
-
"Either
|
48
|
+
"Either %s or %s is an unknown version. Assuming both versions are equal.",
|
49
49
|
lhs_version,
|
50
50
|
rhs_version,
|
51
51
|
)
|
classiq/_internals/jobs.py
CHANGED
@@ -56,8 +56,10 @@ class JobPoller:
|
|
56
56
|
base_url: str,
|
57
57
|
required_headers: Optional[Set[str]] = None,
|
58
58
|
use_versioned_url: bool = True,
|
59
|
+
additional_headers: Optional[Dict[str, str]] = None,
|
59
60
|
) -> None:
|
60
61
|
self._required_headers = required_headers or set()
|
62
|
+
self._additional_headers = additional_headers
|
61
63
|
client_instance = client()
|
62
64
|
self._base_url = (
|
63
65
|
client_instance.make_versioned_url(base_url)
|
@@ -93,7 +95,7 @@ class JobPoller:
|
|
93
95
|
# Update headers in case they change
|
94
96
|
self._async_client.headers.update(client().get_headers())
|
95
97
|
response = await self._async_client.request(
|
96
|
-
method=http_method, url=url, json=body
|
98
|
+
method=http_method, url=url, json=body, headers=self._additional_headers
|
97
99
|
)
|
98
100
|
client().handle_response(response)
|
99
101
|
return response
|
@@ -11,8 +11,7 @@ U = TypeVar("U")
|
|
11
11
|
@overload
|
12
12
|
def validate_type(
|
13
13
|
obj: Any, expected_type: Type[T], operation: str, exception_type: Type[Exception]
|
14
|
-
) -> T:
|
15
|
-
...
|
14
|
+
) -> T: ...
|
16
15
|
|
17
16
|
|
18
17
|
@overload
|
@@ -21,8 +20,7 @@ def validate_type(
|
|
21
20
|
expected_type: Tuple[Type[T], Type[U]],
|
22
21
|
operation: str,
|
23
22
|
exception_type: Type[Exception],
|
24
|
-
) -> Union[T, U]:
|
25
|
-
...
|
23
|
+
) -> Union[T, U]: ...
|
26
24
|
|
27
25
|
|
28
26
|
@overload
|
@@ -31,8 +29,7 @@ def validate_type(
|
|
31
29
|
expected_type: Tuple[Type[T], ...],
|
32
30
|
operation: str,
|
33
31
|
exception_type: Type[Exception],
|
34
|
-
) -> Any:
|
35
|
-
...
|
32
|
+
) -> Any: ...
|
36
33
|
|
37
34
|
|
38
35
|
def validate_type(
|
classiq/analyzer/analyzer.py
CHANGED
classiq/analyzer/rb.py
CHANGED
@@ -12,7 +12,7 @@ from classiq.interface.analyzer.result import RbResults
|
|
12
12
|
from classiq._internals.api_wrapper import ApiWrapper
|
13
13
|
from classiq._internals.async_utils import Asyncify
|
14
14
|
from classiq.exceptions import ClassiqAnalyzerError
|
15
|
-
from classiq.executor import
|
15
|
+
from classiq.executor import BackendPreferencesAndResult
|
16
16
|
|
17
17
|
|
18
18
|
class RBAnalysis(metaclass=Asyncify):
|
@@ -86,8 +86,7 @@ def _strict_string(arg: Union[Enum, str]) -> str:
|
|
86
86
|
|
87
87
|
|
88
88
|
def order_executor_data_by_hardware(
|
89
|
-
mixed_data: List[
|
90
|
-
clifford_numbers_per_program: Dict[str, int],
|
89
|
+
mixed_data: List[BackendPreferencesAndResult],
|
91
90
|
) -> List[AnalysisRBParams]:
|
92
91
|
hardware_names: Set[str] = {
|
93
92
|
_strict_string(hardware.backend_name) for hardware, _, _ in mixed_data
|
@@ -96,9 +95,8 @@ def order_executor_data_by_hardware(
|
|
96
95
|
name: list() for name in hardware_names
|
97
96
|
}
|
98
97
|
cliffords_dicts: Dict[str, List[int]] = {name: list() for name in hardware_names}
|
99
|
-
for hardware,
|
98
|
+
for hardware, num_clifford, result in mixed_data:
|
100
99
|
hw_name: str = _strict_string(hardware.backend_name)
|
101
|
-
num_clifford: int = clifford_numbers_per_program[program.code] # type: ignore[index]
|
102
100
|
counts_dicts[hw_name].append(result.counts) # type: ignore[union-attr]
|
103
101
|
cliffords_dicts[hw_name].append(num_clifford)
|
104
102
|
|
@@ -27,7 +27,6 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
27
27
|
PortDeclarationDirection,
|
28
28
|
)
|
29
29
|
from classiq.interface.model.handle_binding import HandleBinding
|
30
|
-
from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
|
31
30
|
from classiq.interface.model.model import Model, SerializedModel
|
32
31
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
33
32
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -6,9 +6,11 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
6
6
|
)
|
7
7
|
from classiq.interface.model.bind_operation import BindOperation
|
8
8
|
from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
|
9
|
-
from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
|
10
9
|
from classiq.interface.model.model import Model, SerializedModel
|
11
10
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
11
|
+
from classiq.interface.model.numeric_reinterpretation import (
|
12
|
+
NumericReinterpretationOperation,
|
13
|
+
)
|
12
14
|
from classiq.interface.model.port_declaration import PortDeclaration
|
13
15
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
14
16
|
ArithmeticOperation,
|
@@ -18,10 +20,9 @@ from classiq.interface.model.quantum_function_call import (
|
|
18
20
|
QuantumLambdaFunction,
|
19
21
|
)
|
20
22
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
21
|
-
from classiq.interface.model.quantum_type import
|
22
|
-
|
23
|
-
|
24
|
-
QuantumType,
|
23
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
24
|
+
from classiq.interface.model.variable_declaration_statement import (
|
25
|
+
VariableDeclarationStatement,
|
25
26
|
)
|
26
27
|
|
27
28
|
from classiq import RegisterUserInput
|
@@ -108,13 +109,6 @@ def _construct_arithmetic_oracle(
|
|
108
109
|
)
|
109
110
|
|
110
111
|
|
111
|
-
def get_quantum_type(fraction_places: int) -> QuantumType:
|
112
|
-
if fraction_places > 0:
|
113
|
-
return QuantumFixedReal(fraction_places=Expression(expr=f"{fraction_places}"))
|
114
|
-
|
115
|
-
return QuantumInteger()
|
116
|
-
|
117
|
-
|
118
112
|
def grover_main_port_declarations(
|
119
113
|
definitions: List[Tuple[str, RegisterUserInput]],
|
120
114
|
direction: PortDeclarationDirection,
|
@@ -123,25 +117,38 @@ def grover_main_port_declarations(
|
|
123
117
|
name: PortDeclaration(
|
124
118
|
name=name,
|
125
119
|
size=Expression(expr=f"{reg.size}"),
|
126
|
-
quantum_type=
|
120
|
+
quantum_type=QuantumNumeric(),
|
127
121
|
direction=direction,
|
128
122
|
)
|
129
123
|
for name, reg in definitions
|
130
124
|
}
|
131
125
|
|
132
126
|
|
133
|
-
def
|
127
|
+
def _generate_variable_declaration_statements(
|
134
128
|
definitions: List[Tuple[str, RegisterUserInput]]
|
135
|
-
) -> List[
|
136
|
-
ret = [
|
129
|
+
) -> List[VariableDeclarationStatement]:
|
130
|
+
ret = [VariableDeclarationStatement(name="gsq")]
|
137
131
|
if len(definitions) >= 2:
|
138
132
|
ret += [
|
139
|
-
|
133
|
+
VariableDeclarationStatement(name=f"split{i}")
|
140
134
|
for i in range(len(definitions) - 2)
|
141
135
|
]
|
142
136
|
return ret
|
143
137
|
|
144
138
|
|
139
|
+
def reinterpret_registers(
|
140
|
+
definitions: List[Tuple[str, RegisterUserInput]]
|
141
|
+
) -> List[QuantumStatement]:
|
142
|
+
return [
|
143
|
+
NumericReinterpretationOperation(
|
144
|
+
target=HandleBinding(name=name),
|
145
|
+
fraction_digits=Expression(expr=f"{definition.fraction_places}"),
|
146
|
+
is_signed=Expression(expr=f"{definition.is_signed}"),
|
147
|
+
)
|
148
|
+
for name, definition in definitions
|
149
|
+
]
|
150
|
+
|
151
|
+
|
145
152
|
def construct_grover_model(
|
146
153
|
definitions: List[Tuple[str, RegisterUserInput]],
|
147
154
|
expression: str,
|
@@ -163,6 +170,7 @@ def construct_grover_model(
|
|
163
170
|
name=_PREDICATE_FUNCTION_NAME,
|
164
171
|
port_declarations=predicate_port_decls,
|
165
172
|
body=[
|
173
|
+
*reinterpret_registers(definitions),
|
166
174
|
ArithmeticOperation(
|
167
175
|
expression=Expression(expr=expression),
|
168
176
|
result_var=HandleBinding(name="res"),
|
@@ -175,8 +183,8 @@ def construct_grover_model(
|
|
175
183
|
port_declarations=grover_main_port_declarations(
|
176
184
|
definitions, PortDeclarationDirection.Output
|
177
185
|
),
|
178
|
-
local_handles=_generate_local_variable_declarations(definitions),
|
179
186
|
body=[
|
187
|
+
*_generate_variable_declaration_statements(definitions),
|
180
188
|
QuantumFunctionCall(
|
181
189
|
function="allocate",
|
182
190
|
positional_args=[
|
@@ -207,6 +215,7 @@ def construct_grover_model(
|
|
207
215
|
[reg.size for _, reg in definitions],
|
208
216
|
"gsq",
|
209
217
|
),
|
218
|
+
*reinterpret_registers(definitions),
|
210
219
|
],
|
211
220
|
),
|
212
221
|
],
|
classiq/execution/jobs.py
CHANGED
@@ -7,9 +7,9 @@ from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
|
7
7
|
from classiq.interface.executor.execution_request import ExecutionJobDetails
|
8
8
|
from classiq.interface.executor.execution_result import ResultsCollection
|
9
9
|
from classiq.interface.jobs import JobStatus, JSONObject
|
10
|
-
from classiq.interface.server.routes import
|
10
|
+
from classiq.interface.server.routes import EXECUTION_JOBS_NON_VERSIONED_FULL_PATH
|
11
11
|
|
12
|
-
from classiq._internals.api_wrapper import ApiWrapper
|
12
|
+
from classiq._internals.api_wrapper import CLASSIQ_ACCEPT_HEADER, ApiWrapper
|
13
13
|
from classiq._internals.async_utils import syncify_function
|
14
14
|
from classiq._internals.client import client
|
15
15
|
from classiq._internals.jobs import JobID, JobPoller
|
@@ -17,6 +17,9 @@ from classiq.exceptions import ClassiqAPIError
|
|
17
17
|
|
18
18
|
_JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
|
19
19
|
|
20
|
+
_JOB_DETAILS_VERSION = "v1"
|
21
|
+
_JOB_RESULT_VERSION = "v1"
|
22
|
+
|
20
23
|
|
21
24
|
class ExecutionJob:
|
22
25
|
_details: _JobDetails
|
@@ -98,7 +101,9 @@ class ExecutionJob:
|
|
98
101
|
|
99
102
|
if self._result is None:
|
100
103
|
self._result = (
|
101
|
-
await ApiWrapper.call_get_execution_job_result(
|
104
|
+
await ApiWrapper.call_get_execution_job_result(
|
105
|
+
job_id=self._job_id, version=_JOB_RESULT_VERSION
|
106
|
+
)
|
102
107
|
).results
|
103
108
|
return self._result
|
104
109
|
|
@@ -117,7 +122,11 @@ class ExecutionJob:
|
|
117
122
|
return True
|
118
123
|
return None
|
119
124
|
|
120
|
-
poller = JobPoller(
|
125
|
+
poller = JobPoller(
|
126
|
+
base_url=EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
|
127
|
+
use_versioned_url=False,
|
128
|
+
additional_headers={CLASSIQ_ACCEPT_HEADER: _JOB_DETAILS_VERSION},
|
129
|
+
)
|
121
130
|
await poller.poll(
|
122
131
|
job_id=self._job_id,
|
123
132
|
response_parser=response_parser,
|
classiq/executor.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Executor module, implementing facilities for executing quantum programs using Classiq platform."""
|
2
|
+
|
2
3
|
import functools
|
3
4
|
from typing import Optional, Tuple, Union
|
4
5
|
|
@@ -32,8 +33,8 @@ from classiq.synthesis import SerializedQuantumProgram
|
|
32
33
|
|
33
34
|
BatchExecutionResult: TypeAlias = Union[ExecutionDetails, BaseException]
|
34
35
|
ProgramAndResult: TypeAlias = Tuple[QuantumProgram, BatchExecutionResult]
|
35
|
-
|
36
|
-
BackendPreferencesTypes,
|
36
|
+
BackendPreferencesAndResult: TypeAlias = Tuple[
|
37
|
+
BackendPreferencesTypes, int, BatchExecutionResult
|
37
38
|
]
|
38
39
|
_MAX_ARGUMENTS_SIZE = 1024
|
39
40
|
|
classiq/interface/_version.py
CHANGED
@@ -51,12 +51,6 @@ class AnalysisHardwareListParams(AnalysisParams, HardwareListParams):
|
|
51
51
|
transpilation_params: AnalysisHardwareTranspilationParams
|
52
52
|
|
53
53
|
|
54
|
-
class ResourceEstimatorParams(pydantic.BaseModel):
|
55
|
-
circuit_id: str
|
56
|
-
error_budget: float
|
57
|
-
physical_error_rate: float
|
58
|
-
|
59
|
-
|
60
54
|
class HardwareParams(pydantic.BaseModel):
|
61
55
|
device: PydanticNonEmptyString = pydantic.Field(default=None, description="Devices")
|
62
56
|
provider: AnalyzerProviderVendor
|
@@ -136,10 +136,6 @@ class DevicesResult(VersionedModel):
|
|
136
136
|
status: GraphStatus
|
137
137
|
|
138
138
|
|
139
|
-
class ResourceEstimatorResult(VersionedModel):
|
140
|
-
report_json: str
|
141
|
-
|
142
|
-
|
143
139
|
class QuantumCircuitProperties(pydantic.BaseModel):
|
144
140
|
depth: pydantic.NonNegativeInt = pydantic.Field(
|
145
141
|
default=..., description="Circuit depth"
|
@@ -204,8 +204,8 @@ class GCPBackendPreferences(BackendPreferences):
|
|
204
204
|
|
205
205
|
class OQCBackendPreferences(BackendPreferences):
|
206
206
|
backend_service_provider: ProviderTypeVendor.OQC
|
207
|
-
|
208
|
-
|
207
|
+
username: str = pydantic.Field(description="OQC username")
|
208
|
+
password: str = pydantic.Field(description="OQC password")
|
209
209
|
|
210
210
|
@pydantic.root_validator(pre=True)
|
211
211
|
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
@@ -52,9 +52,9 @@ class RandomTwoQubitGatesArgs(CustomAnsatzArgs):
|
|
52
52
|
|
53
53
|
|
54
54
|
class TwoLocalArgs(CustomAnsatzArgs):
|
55
|
-
rotation_blocks: Optional[
|
56
|
-
|
57
|
-
|
55
|
+
rotation_blocks: Optional[Union[RotationBlocksType, List[RotationBlocksType]]] = (
|
56
|
+
RotationBlocksType.ry
|
57
|
+
)
|
58
58
|
entanglement_blocks: Optional[
|
59
59
|
Union[EntanglementBlocksType, List[EntanglementBlocksType]]
|
60
60
|
] = EntanglementBlocksType.cx
|
@@ -583,9 +583,7 @@ class Modulo(EffectiveUnaryOpParams[int]):
|
|
583
583
|
) -> int:
|
584
584
|
repr_qubits_float = math.log2(right_arg)
|
585
585
|
repr_qubits = round(repr_qubits_float)
|
586
|
-
assert (
|
587
|
-
abs(repr_qubits - repr_qubits_float) < 10**-8
|
588
|
-
), NOT_POWER_OF_TWO_ERROR_MSG
|
586
|
+
assert abs(repr_qubits - repr_qubits_float) < 10**-8, NOT_POWER_OF_TWO_ERROR_MSG
|
589
587
|
output_size = values.get("output_size")
|
590
588
|
if output_size is not None:
|
591
589
|
repr_qubits = min(repr_qubits, output_size)
|
@@ -11,6 +11,8 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
|
|
11
11
|
"get_type",
|
12
12
|
"struct_literal",
|
13
13
|
"get_field",
|
14
|
+
"fraction_digits",
|
15
|
+
"is_signed",
|
14
16
|
"molecule_problem_to_hamiltonian",
|
15
17
|
"fock_hamiltonian_problem_to_hamiltonian",
|
16
18
|
"molecule_ground_state_solution_post_process",
|