classiq 0.34.0__py3-none-any.whl → 0.35.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/_internals/api_wrapper.py +52 -3
- classiq/_internals/client.py +4 -1
- classiq/applications_model_constructors/grover_model_constructor.py +1 -1
- classiq/execution/__init__.py +9 -2
- classiq/execution/jobs.py +84 -11
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +2 -0
- classiq/interface/execution/__init__.py +0 -0
- classiq/interface/execution/jobs.py +28 -0
- classiq/interface/generator/arith/arithmetic_expression_validator.py +1 -0
- classiq/interface/generator/arith/arithmetic_param_getters.py +12 -0
- classiq/interface/generator/arith/binary_ops.py +34 -0
- classiq/interface/generator/expressions/expression.py +3 -0
- classiq/interface/generator/expressions/qmod_sized_proxy.py +12 -2
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/function_params.py +4 -0
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +27 -0
- classiq/interface/generator/model/preferences/preferences.py +2 -0
- classiq/interface/hardware.py +1 -0
- classiq/interface/model/bind_operation.py +10 -0
- classiq/interface/model/handle_binding.py +18 -0
- classiq/interface/model/model.py +12 -3
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -1
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
- classiq/interface/model/quantum_expressions/quantum_expression.py +9 -6
- classiq/interface/server/routes.py +3 -0
- classiq/model/function_handler.pyi +85 -84
- classiq/qmod/__init__.py +2 -2
- classiq/qmod/builtins/__init__.py +8 -0
- classiq/qmod/{qmod_builtins.py → builtins/functions.py} +30 -136
- classiq/qmod/builtins/operations.py +19 -0
- classiq/qmod/builtins/structs.py +128 -0
- classiq/qmod/qmod_variable.py +4 -4
- classiq/qmod/quantum_callable.py +2 -2
- classiq/qmod/quantum_expandable.py +1 -1
- {classiq-0.34.0.dist-info → classiq-0.35.0.dist-info}/METADATA +1 -1
- {classiq-0.34.0.dist-info → classiq-0.35.0.dist-info}/RECORD +38 -33
- {classiq-0.34.0.dist-info → classiq-0.35.0.dist-info}/WHEEL +0 -0
@@ -7,6 +7,10 @@ import classiq.interface.pyomo_extension # noqa: F401 - patches pyomo to add fe
|
|
7
7
|
from classiq.interface.analyzer import analysis_params, result as analysis_result
|
8
8
|
from classiq.interface.analyzer.analysis_params import AnalysisRBParams
|
9
9
|
from classiq.interface.chemistry import ground_state_problem, operator
|
10
|
+
from classiq.interface.execution.jobs import (
|
11
|
+
ExecutionJobDetailsV1,
|
12
|
+
ExecutionJobsQueryResultsV1,
|
13
|
+
)
|
10
14
|
from classiq.interface.executor import execution_request, result as execute_result
|
11
15
|
from classiq.interface.generator import generated_circuit as generator_result
|
12
16
|
from classiq.interface.jobs import JobDescription, JobID, JobStatus, JSONObject
|
@@ -25,6 +29,7 @@ class HTTPMethod(StrEnum):
|
|
25
29
|
# Partial backport from Python 3.11
|
26
30
|
GET = "GET"
|
27
31
|
POST = "POST"
|
32
|
+
PATCH = "PATCH"
|
28
33
|
|
29
34
|
|
30
35
|
class StatusType(Protocol):
|
@@ -44,13 +49,19 @@ def _parse_job_response(
|
|
44
49
|
class ApiWrapper:
|
45
50
|
@classmethod
|
46
51
|
async def _call_task_pydantic(
|
47
|
-
cls,
|
52
|
+
cls,
|
53
|
+
http_method: str,
|
54
|
+
url: str,
|
55
|
+
model: pydantic.BaseModel,
|
56
|
+
use_versioned_url: bool = True,
|
48
57
|
) -> dict:
|
49
58
|
# TODO: we can't use model.dict() - it doesn't serialize complex class.
|
50
59
|
# This was added because JSON serializer doesn't serialize complex type, and pydantic does.
|
51
60
|
# We should add support for smarter json serialization.
|
52
61
|
body = json.loads(model.json())
|
53
|
-
return await cls._call_task(
|
62
|
+
return await cls._call_task(
|
63
|
+
http_method, url, body, use_versioned_url=use_versioned_url
|
64
|
+
)
|
54
65
|
|
55
66
|
@classmethod
|
56
67
|
async def _call_task(
|
@@ -59,9 +70,14 @@ class ApiWrapper:
|
|
59
70
|
url: str,
|
60
71
|
body: Optional[Dict] = None,
|
61
72
|
params: Optional[Dict] = None,
|
73
|
+
use_versioned_url: bool = True,
|
62
74
|
) -> dict:
|
63
75
|
res = await client().call_api(
|
64
|
-
http_method=http_method,
|
76
|
+
http_method=http_method,
|
77
|
+
url=url,
|
78
|
+
body=body,
|
79
|
+
params=params,
|
80
|
+
use_versioned_url=use_versioned_url,
|
65
81
|
)
|
66
82
|
if not isinstance(res, dict):
|
67
83
|
raise ClassiqValueError(f"Unexpected returned value: {res}")
|
@@ -108,6 +124,39 @@ class ApiWrapper:
|
|
108
124
|
)
|
109
125
|
return execution_request.ExecuteGeneratedCircuitResults.parse_obj(data)
|
110
126
|
|
127
|
+
@classmethod
|
128
|
+
async def call_patch_execution_job(
|
129
|
+
cls,
|
130
|
+
job_id: JobID,
|
131
|
+
name: str,
|
132
|
+
) -> ExecutionJobDetailsV1:
|
133
|
+
data = await cls._call_task(
|
134
|
+
http_method=HTTPMethod.PATCH,
|
135
|
+
url=f"{routes.EXECUTION_SERVICE_JOBS_FULL_PATH}/{job_id.job_id}",
|
136
|
+
params={
|
137
|
+
"name": name,
|
138
|
+
},
|
139
|
+
use_versioned_url=False,
|
140
|
+
)
|
141
|
+
return ExecutionJobDetailsV1.parse_obj(data)
|
142
|
+
|
143
|
+
@classmethod
|
144
|
+
async def call_query_execution_jobs(
|
145
|
+
cls,
|
146
|
+
offset: int,
|
147
|
+
limit: int,
|
148
|
+
) -> ExecutionJobsQueryResultsV1:
|
149
|
+
data = await cls._call_task(
|
150
|
+
http_method=HTTPMethod.GET,
|
151
|
+
url=f"{routes.EXECUTION_SERVICE_JOBS_FULL_PATH}",
|
152
|
+
params={
|
153
|
+
"offset": offset,
|
154
|
+
"limit": limit,
|
155
|
+
},
|
156
|
+
use_versioned_url=False,
|
157
|
+
)
|
158
|
+
return ExecutionJobsQueryResultsV1.parse_obj(data)
|
159
|
+
|
111
160
|
@classmethod
|
112
161
|
async def call_execute_estimate(
|
113
162
|
cls, request: execution_request.ExecutionRequest
|
classiq/_internals/client.py
CHANGED
@@ -195,11 +195,14 @@ class Client:
|
|
195
195
|
url: str,
|
196
196
|
body: Optional[Dict] = None,
|
197
197
|
params: Optional[Dict] = None,
|
198
|
+
use_versioned_url: bool = True,
|
198
199
|
) -> Union[Dict, str]:
|
200
|
+
if use_versioned_url:
|
201
|
+
url = self.make_versioned_url(url)
|
199
202
|
async with self.async_client() as async_client:
|
200
203
|
response = await async_client.request(
|
201
204
|
method=http_method,
|
202
|
-
url=
|
205
|
+
url=url,
|
203
206
|
json=body,
|
204
207
|
params=params,
|
205
208
|
)
|
classiq/execution/__init__.py
CHANGED
@@ -9,13 +9,20 @@ from ..interface.executor.execution_preferences import __all__ as _ep_all
|
|
9
9
|
from ..interface.executor.iqae_result import IQAEResult
|
10
10
|
from ..interface.executor.result import ExecutionDetails
|
11
11
|
from ..interface.executor.vqe_result import VQESolverResult
|
12
|
-
from .jobs import ExecutionJob
|
12
|
+
from .jobs import ExecutionJob, get_execution_jobs, get_execution_jobs_async
|
13
13
|
|
14
14
|
__all__ = (
|
15
15
|
_be_all
|
16
16
|
+ _ep_all
|
17
17
|
+ _exec_all
|
18
|
-
+ [
|
18
|
+
+ [
|
19
|
+
"ExecutionDetails",
|
20
|
+
"VQESolverResult",
|
21
|
+
"IQAEResult",
|
22
|
+
"ExecutionJob",
|
23
|
+
"get_execution_jobs",
|
24
|
+
"get_execution_jobs_async",
|
25
|
+
]
|
19
26
|
)
|
20
27
|
|
21
28
|
|
classiq/execution/jobs.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
from
|
1
|
+
import webbrowser
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import List, Optional, Union
|
4
|
+
from urllib.parse import urljoin
|
4
5
|
|
6
|
+
from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
5
7
|
from classiq.interface.executor.execution_request import (
|
6
8
|
ExecutionJobDetails,
|
7
9
|
ResultsCollection,
|
@@ -11,19 +13,67 @@ from classiq.interface.server.routes import EXECUTION_JOBS_FULL_PATH
|
|
11
13
|
|
12
14
|
from classiq._internals.api_wrapper import ApiWrapper
|
13
15
|
from classiq._internals.async_utils import syncify_function
|
16
|
+
from classiq._internals.client import client
|
14
17
|
from classiq._internals.jobs import JobID, JobPoller
|
15
18
|
from classiq.exceptions import ClassiqAPIError
|
16
19
|
|
20
|
+
_JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
|
21
|
+
|
22
|
+
|
23
|
+
class ExecutionJob:
|
24
|
+
_details: _JobDetails
|
25
|
+
_result: Optional[ResultsCollection]
|
26
|
+
|
27
|
+
def __init__(self, details: _JobDetails) -> None:
|
28
|
+
self._details = details
|
29
|
+
self._result = None
|
30
|
+
|
31
|
+
@property
|
32
|
+
def id(self) -> str:
|
33
|
+
return self._details.id
|
34
|
+
|
35
|
+
@property
|
36
|
+
def name(self) -> Optional[str]:
|
37
|
+
return self._details.name
|
38
|
+
|
39
|
+
@property
|
40
|
+
def start_time(self) -> datetime:
|
41
|
+
return self._details.start_time
|
42
|
+
|
43
|
+
@property
|
44
|
+
def end_time(self) -> Optional[datetime]:
|
45
|
+
return self._details.end_time
|
17
46
|
|
18
|
-
|
19
|
-
|
47
|
+
@property
|
48
|
+
def provider(self) -> Optional[str]:
|
49
|
+
return self._details.provider
|
20
50
|
|
21
|
-
|
22
|
-
|
51
|
+
@property
|
52
|
+
def backend_name(self) -> Optional[str]:
|
53
|
+
return self._details.backend_name
|
23
54
|
|
24
|
-
|
25
|
-
|
26
|
-
|
55
|
+
@property
|
56
|
+
def status(self) -> JobStatus:
|
57
|
+
return self._details.status
|
58
|
+
|
59
|
+
@property
|
60
|
+
def num_shots(self) -> Optional[int]:
|
61
|
+
return self._details.num_shots
|
62
|
+
|
63
|
+
@property
|
64
|
+
def program_id(self) -> Optional[str]:
|
65
|
+
return self._details.program_id
|
66
|
+
|
67
|
+
@property
|
68
|
+
def error(self) -> Optional[str]:
|
69
|
+
return self._details.error
|
70
|
+
|
71
|
+
def __repr__(self) -> str:
|
72
|
+
class_name = self.__class__.__name__
|
73
|
+
if self.name is None:
|
74
|
+
return f"{class_name}(id={self.id!r})"
|
75
|
+
else:
|
76
|
+
return f"{class_name}(name={self.name!r}, id={self.id!r})"
|
27
77
|
|
28
78
|
@classmethod
|
29
79
|
async def from_id_async(cls, id: str) -> "ExecutionJob":
|
@@ -64,7 +114,7 @@ class ExecutionJob(ExecutionJobDetails):
|
|
64
114
|
|
65
115
|
async def _poll_job(self, timeout_sec: Optional[float] = None) -> None:
|
66
116
|
def response_parser(json_response: JSONObject) -> Optional[bool]:
|
67
|
-
self.
|
117
|
+
self._details = ExecutionJobDetails.parse_obj(json_response)
|
68
118
|
if self.status.is_final():
|
69
119
|
return True
|
70
120
|
return None
|
@@ -75,3 +125,26 @@ class ExecutionJob(ExecutionJobDetails):
|
|
75
125
|
response_parser=response_parser,
|
76
126
|
timeout_sec=timeout_sec,
|
77
127
|
)
|
128
|
+
|
129
|
+
async def rename_async(self, name: str) -> None:
|
130
|
+
self._details = await ApiWrapper.call_patch_execution_job(self._job_id, name)
|
131
|
+
|
132
|
+
rename = syncify_function(rename_async)
|
133
|
+
|
134
|
+
@property
|
135
|
+
def ide_url(self) -> str:
|
136
|
+
base_url = client().config.ide
|
137
|
+
return urljoin(base_url, f"jobs/{self.id}")
|
138
|
+
|
139
|
+
def open_in_ide(self) -> None:
|
140
|
+
webbrowser.open_new_tab(self.ide_url)
|
141
|
+
|
142
|
+
|
143
|
+
async def get_execution_jobs_async(
|
144
|
+
offset: int = 0, limit: int = 50
|
145
|
+
) -> List[ExecutionJob]:
|
146
|
+
result = await ApiWrapper().call_query_execution_jobs(offset=offset, limit=limit)
|
147
|
+
return [ExecutionJob(details) for details in result.results]
|
148
|
+
|
149
|
+
|
150
|
+
get_execution_jobs = syncify_function(get_execution_jobs_async)
|
classiq/interface/_version.py
CHANGED
@@ -17,6 +17,7 @@ class ProviderVendor(StrEnum):
|
|
17
17
|
IONQ = "IonQ"
|
18
18
|
GOOGLE = "Google"
|
19
19
|
ALICE_AND_BOB = "Alice and Bob"
|
20
|
+
OQC = "OQC"
|
20
21
|
|
21
22
|
|
22
23
|
class ProviderTypeVendor:
|
@@ -27,6 +28,7 @@ class ProviderTypeVendor:
|
|
27
28
|
IONQ = Literal[ProviderVendor.IONQ]
|
28
29
|
GOOGLE = Literal[ProviderVendor.GOOGLE]
|
29
30
|
ALICE_BOB = Literal[ProviderVendor.ALICE_AND_BOB]
|
31
|
+
OQC = Literal[ProviderVendor.OQC]
|
30
32
|
|
31
33
|
|
32
34
|
class ClassiqAerBackendNames(StrEnum):
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import List, Optional
|
3
|
+
|
4
|
+
from pydantic import BaseModel, Extra
|
5
|
+
|
6
|
+
from classiq.interface.jobs import JobStatus
|
7
|
+
|
8
|
+
|
9
|
+
class ExecutionJobDetailsV1(BaseModel, extra=Extra.ignore):
|
10
|
+
id: str
|
11
|
+
|
12
|
+
name: Optional[str]
|
13
|
+
start_time: datetime
|
14
|
+
end_time: Optional[datetime]
|
15
|
+
|
16
|
+
provider: Optional[str]
|
17
|
+
backend_name: Optional[str]
|
18
|
+
|
19
|
+
status: JobStatus
|
20
|
+
|
21
|
+
num_shots: Optional[int]
|
22
|
+
program_id: Optional[str]
|
23
|
+
|
24
|
+
error: Optional[str]
|
25
|
+
|
26
|
+
|
27
|
+
class ExecutionJobsQueryResultsV1(BaseModel, extra=Extra.ignore):
|
28
|
+
results: List[ExecutionJobDetailsV1]
|
@@ -25,6 +25,7 @@ from classiq.interface.generator.arith.binary_ops import (
|
|
25
25
|
Modulo,
|
26
26
|
Multiplier,
|
27
27
|
NotEqual,
|
28
|
+
Power,
|
28
29
|
RegisterOrInt,
|
29
30
|
RShift,
|
30
31
|
Subtractor,
|
@@ -155,6 +156,16 @@ def multiplier_params_getter(
|
|
155
156
|
return Multiplier(left_arg=left_arg, right_arg=right_arg, output_size=output_size)
|
156
157
|
|
157
158
|
|
159
|
+
def power_params_getter(
|
160
|
+
left_arg: RegisterArithmeticInfo,
|
161
|
+
right_arg: int,
|
162
|
+
output_size: Optional[int] = None,
|
163
|
+
inplace_arg: Optional[str] = None,
|
164
|
+
target: Optional[RegisterArithmeticInfo] = None,
|
165
|
+
) -> ArithmeticOperationParams:
|
166
|
+
return Power(left_arg=left_arg, right_arg=right_arg, output_size=output_size)
|
167
|
+
|
168
|
+
|
158
169
|
def min_params_getter(
|
159
170
|
left_arg: RegisterOrFloat,
|
160
171
|
right_arg: RegisterOrFloat,
|
@@ -367,4 +378,5 @@ params_getter_map: Dict[str, ParamsGetter] = dict(
|
|
367
378
|
Mod=modulo_params_getter,
|
368
379
|
min=min_params_getter,
|
369
380
|
max=max_params_getter,
|
381
|
+
Pow=power_params_getter,
|
370
382
|
)
|
@@ -413,6 +413,40 @@ class LessEqual(Comparator):
|
|
413
413
|
output_name = "is_less_equal"
|
414
414
|
|
415
415
|
|
416
|
+
class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
|
417
|
+
output_name = "powered"
|
418
|
+
|
419
|
+
@pydantic.validator("right_arg", pre=True)
|
420
|
+
def _validate_legal_power(cls, right_arg: Any) -> pydantic.PositiveInt:
|
421
|
+
if not float(right_arg).is_integer():
|
422
|
+
raise ValueError("Power must be an integer")
|
423
|
+
if right_arg <= 0:
|
424
|
+
raise ValueError("Power must be greater than one")
|
425
|
+
return int(right_arg)
|
426
|
+
|
427
|
+
def _get_result_bounds(self) -> Tuple[float, float]:
|
428
|
+
if (self.right_arg % 2) or min(self.left_arg.bounds) >= 0:
|
429
|
+
return (
|
430
|
+
self.left_arg.bounds[0] ** self.right_arg,
|
431
|
+
self.left_arg.bounds[1] ** self.right_arg,
|
432
|
+
)
|
433
|
+
return 0.0, max(abs(bound) for bound in self.left_arg.bounds) ** self.right_arg
|
434
|
+
|
435
|
+
def _get_result_register(self) -> RegisterArithmeticInfo:
|
436
|
+
if self.output_size:
|
437
|
+
return RegisterArithmeticInfo(size=self.output_size)
|
438
|
+
|
439
|
+
fraction_places: int = self.left_arg.fraction_places * self.right_arg
|
440
|
+
bounds = self._get_result_bounds()
|
441
|
+
size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
|
442
|
+
return RegisterArithmeticInfo(
|
443
|
+
size=size,
|
444
|
+
is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
|
445
|
+
fraction_places=fraction_places,
|
446
|
+
bounds=bounds,
|
447
|
+
)
|
448
|
+
|
449
|
+
|
416
450
|
class EffectiveUnaryOpParams(
|
417
451
|
InplacableBinaryOpParams[RegisterArithmeticInfo, RightDataT], Generic[RightDataT]
|
418
452
|
):
|
@@ -1,6 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
from sympy import Symbol
|
2
|
+
|
3
|
+
|
4
|
+
class QmodSizedProxy(Symbol):
|
5
|
+
def __new__(cls, name, **assumptions):
|
6
|
+
return super().__new__(cls, name, **assumptions)
|
7
|
+
|
8
|
+
def __init__(self, name: str, size: int) -> None:
|
3
9
|
self._size = size
|
4
10
|
|
11
|
+
@property
|
12
|
+
def size(self) -> int:
|
13
|
+
return self.args[0]
|
14
|
+
|
5
15
|
def __len__(self) -> int:
|
6
16
|
return self._size
|
@@ -18,6 +18,7 @@ from classiq.interface.generator.arith.binary_ops import (
|
|
18
18
|
Modulo,
|
19
19
|
Multiplier,
|
20
20
|
NotEqual,
|
21
|
+
Power,
|
21
22
|
RShift,
|
22
23
|
Subtractor,
|
23
24
|
)
|
@@ -136,6 +137,7 @@ function_param_library_without_self_reference: FunctionParamLibrary = (
|
|
136
137
|
WeightedAdder,
|
137
138
|
LinearPauliRotations,
|
138
139
|
Multiplier,
|
140
|
+
Power,
|
139
141
|
LinearGCI,
|
140
142
|
HartreeFock,
|
141
143
|
UCC,
|
classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py
CHANGED
@@ -34,6 +34,32 @@ QFT = QuantumFunctionDeclaration.parse_raw(
|
|
34
34
|
}"""
|
35
35
|
)
|
36
36
|
|
37
|
+
STANDARD_QPE = QuantumFunctionDeclaration.parse_raw(
|
38
|
+
"""{
|
39
|
+
"name": "standard_qpe",
|
40
|
+
"param_decls": {
|
41
|
+
"precision": {
|
42
|
+
"kind": "int"
|
43
|
+
}
|
44
|
+
},
|
45
|
+
"port_declarations": {
|
46
|
+
"phase": {
|
47
|
+
"name": "phase",
|
48
|
+
"size": {
|
49
|
+
"expr": "precision"
|
50
|
+
},
|
51
|
+
"direction": "inout"
|
52
|
+
}
|
53
|
+
},
|
54
|
+
"operand_declarations": {
|
55
|
+
"unitary": {
|
56
|
+
"name": "unitary"
|
57
|
+
}
|
58
|
+
},
|
59
|
+
"positional_arg_declarations": []
|
60
|
+
}"""
|
61
|
+
)
|
62
|
+
|
37
63
|
QPE = QuantumFunctionDeclaration.parse_raw(
|
38
64
|
"""{
|
39
65
|
"name": "qpe",
|
@@ -660,6 +686,7 @@ FULL_HEA = QuantumFunctionDeclaration.parse_raw(
|
|
660
686
|
__all__ = [
|
661
687
|
"QFT_STEP",
|
662
688
|
"QFT",
|
689
|
+
"STANDARD_QPE",
|
663
690
|
"QPE",
|
664
691
|
"SINGLE_PAULI",
|
665
692
|
"LINEAR_PAULI_ROTATIONS",
|
classiq/interface/hardware.py
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
from typing import Mapping
|
2
2
|
|
3
|
+
import pydantic
|
4
|
+
|
3
5
|
from classiq.interface.model.handle_binding import HandleBinding
|
4
6
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
5
7
|
|
8
|
+
from classiq.exceptions import ClassiqValueError
|
9
|
+
|
6
10
|
BIND_INPUT_NAME = "bind_input"
|
7
11
|
BIND_OUTPUT_NAME = "bind_output"
|
8
12
|
|
@@ -18,3 +22,9 @@ class BindOperation(QuantumOperation):
|
|
18
22
|
@property
|
19
23
|
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
20
24
|
return {BIND_OUTPUT_NAME: self.out_handle}
|
25
|
+
|
26
|
+
@pydantic.validator("in_handle", "out_handle")
|
27
|
+
def validate_handle(cls, handle: HandleBinding) -> HandleBinding:
|
28
|
+
if not handle.is_bindable():
|
29
|
+
raise ClassiqValueError(f"Cannot bind '{handle}'") # noqa: B907
|
30
|
+
return handle
|
@@ -10,6 +10,12 @@ class HandleBinding(BaseModel):
|
|
10
10
|
frozen = True
|
11
11
|
extra = Extra.forbid
|
12
12
|
|
13
|
+
def __str__(self) -> str:
|
14
|
+
return self.name
|
15
|
+
|
16
|
+
def is_bindable(self) -> bool:
|
17
|
+
return True
|
18
|
+
|
13
19
|
|
14
20
|
class SubscriptHandleBinding(HandleBinding):
|
15
21
|
index: Expression
|
@@ -18,6 +24,12 @@ class SubscriptHandleBinding(HandleBinding):
|
|
18
24
|
frozen = True
|
19
25
|
extra = Extra.forbid
|
20
26
|
|
27
|
+
def __str__(self) -> str:
|
28
|
+
return f"{self.name}[{self.index}]"
|
29
|
+
|
30
|
+
def is_bindable(self) -> bool:
|
31
|
+
return False
|
32
|
+
|
21
33
|
|
22
34
|
class SlicedHandleBinding(HandleBinding):
|
23
35
|
start: Expression
|
@@ -26,3 +38,9 @@ class SlicedHandleBinding(HandleBinding):
|
|
26
38
|
class Config:
|
27
39
|
frozen = True
|
28
40
|
extra = Extra.forbid
|
41
|
+
|
42
|
+
def __str__(self) -> str:
|
43
|
+
return f"{self.name}[{self.start}:{self.end}]"
|
44
|
+
|
45
|
+
def is_bindable(self) -> bool:
|
46
|
+
return False
|
classiq/interface/model/model.py
CHANGED
@@ -54,8 +54,8 @@ TYPE_NAME_CONFLICT_USER = (
|
|
54
54
|
)
|
55
55
|
|
56
56
|
|
57
|
-
def
|
58
|
-
return
|
57
|
+
def _create_empty_main_function() -> NativeFunctionDefinition:
|
58
|
+
return NativeFunctionDefinition(name=MAIN_FUNCTION_NAME)
|
59
59
|
|
60
60
|
|
61
61
|
class VersionedSerializedModel(VersionedModel):
|
@@ -71,7 +71,7 @@ class Model(VersionedModel):
|
|
71
71
|
|
72
72
|
# Must be validated before logic_flow
|
73
73
|
functions: List[NativeFunctionDefinition] = pydantic.Field(
|
74
|
-
default_factory=
|
74
|
+
default_factory=list,
|
75
75
|
description="The user-defined custom type library.",
|
76
76
|
)
|
77
77
|
|
@@ -119,6 +119,15 @@ class Model(VersionedModel):
|
|
119
119
|
def function_dict(self) -> Dict[str, QuantumFunctionDeclaration]:
|
120
120
|
return nameables_to_dict(self.functions)
|
121
121
|
|
122
|
+
@pydantic.validator("functions", always=True)
|
123
|
+
def _add_empty_main(
|
124
|
+
cls, functions: List[NativeFunctionDefinition]
|
125
|
+
) -> List[NativeFunctionDefinition]:
|
126
|
+
function_dict = nameables_to_dict(functions)
|
127
|
+
if MAIN_FUNCTION_NAME not in function_dict:
|
128
|
+
functions.append(_create_empty_main_function())
|
129
|
+
return functions
|
130
|
+
|
122
131
|
@pydantic.root_validator()
|
123
132
|
def validate_static_correctness(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
124
133
|
functions: Optional[List[QuantumFunctionDeclaration]] = values.get("functions")
|
@@ -40,7 +40,8 @@ class AmplitudeLoadingOperation(QuantumExpressionOperation):
|
|
40
40
|
) -> Mapping[
|
41
41
|
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
42
42
|
]:
|
43
|
-
|
43
|
+
if len(self.var_handles) == 0:
|
44
|
+
return dict()
|
44
45
|
return {AMPLITUDE_IO_NAME: self.var_handles[0]}
|
45
46
|
|
46
47
|
def initialize_var_types(self, var_types: Dict[str, QuantumType]) -> None:
|
@@ -24,7 +24,9 @@ class ArithmeticOperation(QuantumExpressionOperation):
|
|
24
24
|
|
25
25
|
def initialize_var_types(self, var_types: Dict[str, QuantumType]) -> None:
|
26
26
|
super().initialize_var_types(var_types)
|
27
|
-
self._result_type = compute_arithmetic_result_type(
|
27
|
+
self._result_type = compute_arithmetic_result_type(
|
28
|
+
self.expression.expr, var_types
|
29
|
+
)
|
28
30
|
|
29
31
|
@property
|
30
32
|
def wiring_inouts(
|