classiq 0.56.1__py3-none-any.whl → 0.58.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/analyzer/show_interactive_hack.py +16 -4
- classiq/applications/combinatorial_helpers/encoding_utils.py +1 -0
- classiq/applications/combinatorial_helpers/transformations/encoding.py +3 -1
- classiq/execution/jobs.py +8 -1
- classiq/executor.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +27 -5
- classiq/interface/backend/pydantic_backend.py +0 -1
- classiq/interface/execution/jobs.py +4 -1
- classiq/interface/executor/execution_request.py +19 -5
- classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
- classiq/interface/generator/functions/type_name.py +7 -9
- classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
- classiq/model_expansions/closure.py +24 -6
- classiq/model_expansions/evaluators/parameter_types.py +1 -2
- classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
- classiq/model_expansions/function_builder.py +13 -0
- classiq/model_expansions/interpreter.py +9 -14
- classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
- classiq/model_expansions/quantum_operations/classicalif.py +2 -2
- classiq/model_expansions/quantum_operations/control.py +7 -5
- classiq/model_expansions/quantum_operations/emitter.py +1 -186
- classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
- classiq/model_expansions/quantum_operations/invert.py +2 -2
- classiq/model_expansions/quantum_operations/phase.py +3 -1
- classiq/model_expansions/quantum_operations/power.py +2 -2
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
- classiq/model_expansions/quantum_operations/repeat.py +2 -2
- classiq/model_expansions/transformers/__init__.py +0 -0
- classiq/model_expansions/transformers/var_splitter.py +237 -0
- classiq/qmod/builtins/classical_functions.py +1 -0
- classiq/qmod/builtins/functions/state_preparation.py +1 -1
- classiq/qmod/create_model_function.py +25 -20
- classiq/qmod/native/pretty_printer.py +19 -4
- classiq/qmod/pretty_print/pretty_printer.py +53 -28
- classiq/qmod/qfunc.py +18 -16
- classiq/qmod/quantum_function.py +30 -24
- classiq/qmod/semantics/qstruct_annotator.py +23 -0
- classiq/qmod/semantics/static_semantics_visitor.py +4 -1
- classiq/qmod/write_qmod.py +3 -1
- classiq/synthesis.py +3 -1
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/METADATA +1 -1
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/RECORD +46 -42
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/WHEEL +0 -0
@@ -10,7 +10,7 @@ from classiq._internals.async_utils import syncify_function
|
|
10
10
|
from classiq.analyzer.url_utils import circuit_page_uri, client_ide_base_url
|
11
11
|
|
12
12
|
|
13
|
-
async def handle_remote_app(circuit: QuantumProgram,
|
13
|
+
async def handle_remote_app(circuit: QuantumProgram, display_url: bool = True) -> None:
|
14
14
|
if circuit.outputs.get(QuantumFormat.QASM) is None:
|
15
15
|
raise ClassiqAnalyzerVisualizationError(
|
16
16
|
"Missing QASM transpilation: visualization is only supported "
|
@@ -23,14 +23,26 @@ async def handle_remote_app(circuit: QuantumProgram, dispaly_url: bool = False)
|
|
23
23
|
circuit_page_uri(circuit_id=circuit_dataid.id, circuit_version=circuit.version),
|
24
24
|
)
|
25
25
|
|
26
|
-
if
|
26
|
+
if display_url:
|
27
27
|
print(f"Opening: {app_url}") # noqa: T201
|
28
28
|
|
29
29
|
webbrowser.open_new_tab(app_url)
|
30
30
|
|
31
31
|
|
32
|
-
async def _show_interactive(self: QuantumProgram,
|
33
|
-
|
32
|
+
async def _show_interactive(self: QuantumProgram, display_url: bool = True) -> None:
|
33
|
+
"""
|
34
|
+
Displays the interactive representation of the quantum program in the Classiq IDE.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
self:
|
38
|
+
The serialized quantum program to be displayed.
|
39
|
+
display_url:
|
40
|
+
Whether to print the url
|
41
|
+
|
42
|
+
Links:
|
43
|
+
[Visualization tool](https://docs.classiq.io/latest/reference-manual/analyzer/quantum-program-visualization-tool/)
|
44
|
+
"""
|
45
|
+
await handle_remote_app(self, display_url)
|
34
46
|
|
35
47
|
|
36
48
|
QuantumProgram.show = syncify_function(_show_interactive) # type: ignore[attr-defined]
|
@@ -85,7 +85,9 @@ class ModelEncoder:
|
|
85
85
|
encoding_expr = self._get_encoding_expr(var_data, encoding_vars)
|
86
86
|
|
87
87
|
self._add_expr_constraint(
|
88
|
-
constraint_name=var_data.name
|
88
|
+
constraint_name=var_data.name
|
89
|
+
+ encoding_utils.ENCODED_SUFFIX
|
90
|
+
+ encoding_utils.CONSTRAINT_SUFFIX,
|
89
91
|
expr=EqualityExpression(args=[var_data, encoding_expr]),
|
90
92
|
)
|
91
93
|
|
classiq/execution/jobs.py
CHANGED
@@ -8,7 +8,7 @@ from classiq.interface.exceptions import (
|
|
8
8
|
ClassiqError,
|
9
9
|
)
|
10
10
|
from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
11
|
-
from classiq.interface.executor.execution_request import ExecutionJobDetails
|
11
|
+
from classiq.interface.executor.execution_request import ExecutionJobDetails, JobCost
|
12
12
|
from classiq.interface.executor.execution_result import ResultsCollection
|
13
13
|
from classiq.interface.executor.result import (
|
14
14
|
EstimationResult,
|
@@ -92,6 +92,13 @@ class ExecutionJob:
|
|
92
92
|
else:
|
93
93
|
return f"{class_name}(name={self.name!r}, id={self.id!r})"
|
94
94
|
|
95
|
+
def cost(self, *, verbose: bool = False) -> Union[str, JobCost]:
|
96
|
+
if self._details.cost is None:
|
97
|
+
self._details.cost = JobCost()
|
98
|
+
if verbose:
|
99
|
+
return self._details.cost
|
100
|
+
return f"{self._details.cost.total_cost} {self._details.cost.currency_code}"
|
101
|
+
|
95
102
|
@classmethod
|
96
103
|
async def from_id_async(cls, id: str) -> "ExecutionJob":
|
97
104
|
details = await ApiWrapper.call_get_execution_job_details(JobID(job_id=id))
|
classiq/executor.py
CHANGED
@@ -46,7 +46,7 @@ def execute(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
|
46
46
|
Returns:
|
47
47
|
ExecutionJob: The result of the execution.
|
48
48
|
|
49
|
-
For examples please see [Execution Documentation](https://docs.classiq.io/latest/user-guide/
|
49
|
+
For examples please see [Execution Documentation](https://docs.classiq.io/latest/user-guide/execution/)
|
50
50
|
"""
|
51
51
|
return async_utils.run(execute_async(quantum_program))
|
52
52
|
|
classiq/interface/_version.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import warnings
|
3
4
|
from collections.abc import Iterable
|
4
5
|
from typing import Any, Optional, Union
|
5
6
|
|
@@ -21,7 +22,7 @@ from classiq.interface.backend.quantum_backend_providers import (
|
|
21
22
|
ProviderTypeVendor,
|
22
23
|
ProviderVendor,
|
23
24
|
)
|
24
|
-
from classiq.interface.exceptions import ClassiqValueError
|
25
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
|
25
26
|
from classiq.interface.hardware import Provider
|
26
27
|
|
27
28
|
|
@@ -117,8 +118,8 @@ class AliceBobBackendPreferences(BackendPreferences):
|
|
117
118
|
average_nb_photons: Optional[float] = pydantic.Field(
|
118
119
|
default=None, description="Average number of photons"
|
119
120
|
)
|
120
|
-
api_key: pydantic_backend.PydanticAliceBobApiKeyType = pydantic.Field(
|
121
|
-
|
121
|
+
api_key: Optional[pydantic_backend.PydanticAliceBobApiKeyType] = pydantic.Field(
|
122
|
+
default=None, description="AliceBob API key"
|
122
123
|
)
|
123
124
|
|
124
125
|
@property
|
@@ -131,6 +132,17 @@ class AliceBobBackendPreferences(BackendPreferences):
|
|
131
132
|
}
|
132
133
|
return {k: v for k, v in parameters.items() if v is not None}
|
133
134
|
|
135
|
+
@pydantic.field_validator("api_key", mode="after")
|
136
|
+
@classmethod
|
137
|
+
def _validate_api_key(cls, api_key: Optional[str]) -> Optional[str]:
|
138
|
+
if api_key is not None:
|
139
|
+
warnings.warn(
|
140
|
+
"API key is no longer required for Alice&Bob backends.",
|
141
|
+
ClassiqDeprecationWarning,
|
142
|
+
stacklevel=2,
|
143
|
+
)
|
144
|
+
return api_key
|
145
|
+
|
134
146
|
|
135
147
|
class ClassiqBackendPreferences(BackendPreferences):
|
136
148
|
"""
|
@@ -230,6 +242,7 @@ class IBMBackendPreferences(BackendPreferences):
|
|
230
242
|
access_token (Optional[str]): The IBM Quantum access token to be used with IBM Quantum hosted backends. Defaults to `None`.
|
231
243
|
provider (IBMBackendProvider): Specifications for identifying a single IBM Quantum provider. Defaults to a new `IBMBackendProvider`.
|
232
244
|
qctrl_api_key (Optional[str]): QCTRL API key to access QCTRL optimization abilities.
|
245
|
+
run_through_classiq (bool): Run through Classiq's credentials. Defaults to `False`.
|
233
246
|
|
234
247
|
See examples in the [IBM Quantum Backend Documentation](https://docs.classiq.io/latest/reference-manual/executor/cloud-providers/ibm-backends/?h=).
|
235
248
|
"""
|
@@ -250,6 +263,10 @@ class IBMBackendPreferences(BackendPreferences):
|
|
250
263
|
default=None,
|
251
264
|
description="QCTRL API key to access QCTRL optimization abilities",
|
252
265
|
)
|
266
|
+
run_through_classiq: bool = pydantic.Field(
|
267
|
+
default=False,
|
268
|
+
description="Run through Classiq's credentials",
|
269
|
+
)
|
253
270
|
|
254
271
|
|
255
272
|
class AzureCredential(BaseSettings):
|
@@ -341,6 +358,7 @@ class IonqBackendPreferences(BackendPreferences):
|
|
341
358
|
backend_service_provider (ProviderTypeVendor.IONQ): Indicates the backend service provider as IonQ.
|
342
359
|
api_key (PydanticIonQApiKeyType): The IonQ API key required for accessing IonQ's quantum computing services.
|
343
360
|
error_mitigation (bool): A configuration option to enable or disable error mitigation during execution. Defaults to `False`.
|
361
|
+
run_through_classiq (bool): Running through Classiq's credentials while using user's allocated budget.
|
344
362
|
|
345
363
|
See examples in the [IonQ Backend Documentation](https://docs.classiq.io/latest/reference-manual/executor/cloud-providers/ionq-backends/?h=).
|
346
364
|
"""
|
@@ -348,13 +366,17 @@ class IonqBackendPreferences(BackendPreferences):
|
|
348
366
|
backend_service_provider: ProviderTypeVendor.IONQ = pydantic.Field(
|
349
367
|
default=ProviderVendor.IONQ
|
350
368
|
)
|
351
|
-
api_key: pydantic_backend.PydanticIonQApiKeyType = pydantic.Field(
|
352
|
-
|
369
|
+
api_key: Optional[pydantic_backend.PydanticIonQApiKeyType] = pydantic.Field(
|
370
|
+
default=None, description="IonQ API key"
|
353
371
|
)
|
354
372
|
error_mitigation: bool = pydantic.Field(
|
355
373
|
default=False,
|
356
374
|
description="Error mitigation configuration.",
|
357
375
|
)
|
376
|
+
run_through_classiq: bool = pydantic.Field(
|
377
|
+
default=False,
|
378
|
+
description="Running through Classiq's credentials while using user's allocated budget.",
|
379
|
+
)
|
358
380
|
|
359
381
|
|
360
382
|
class GCPBackendPreferences(BackendPreferences):
|
@@ -7,7 +7,6 @@ AZURE_QUANTUM_RESOURCE_ID_REGEX = r"^/subscriptions/([a-fA-F0-9-]*)/resourceGrou
|
|
7
7
|
_IONQ_API_KEY_LENGTH: int = 32
|
8
8
|
_ALICE_BOB_API_KEY_LENGTH: int = 72
|
9
9
|
INVALID_API_KEY: str = _IONQ_API_KEY_LENGTH * "a"
|
10
|
-
INVALID_API_KEY_ALICE_BOB: str = _ALICE_BOB_API_KEY_LENGTH * "a"
|
11
10
|
INVALID_EMAIL_OQC: str = "aa@aa.aa"
|
12
11
|
INVALID_PASSWORD_OQC: str = "Aa1!Aa1!"
|
13
12
|
|
@@ -1,8 +1,9 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Optional
|
3
3
|
|
4
|
-
from pydantic import BaseModel
|
4
|
+
from pydantic import BaseModel, Field
|
5
5
|
|
6
|
+
from classiq.interface.executor.execution_request import JobCost
|
6
7
|
from classiq.interface.jobs import JobStatus
|
7
8
|
|
8
9
|
|
@@ -23,6 +24,8 @@ class ExecutionJobDetailsV1(BaseModel, extra="ignore"):
|
|
23
24
|
|
24
25
|
error: Optional[str] = None
|
25
26
|
|
27
|
+
cost: Optional[JobCost] = Field(default=None)
|
28
|
+
|
26
29
|
|
27
30
|
class ExecutionJobsQueryResultsV1(BaseModel, extra="ignore"):
|
28
31
|
results: list[ExecutionJobDetailsV1]
|
@@ -43,23 +43,37 @@ class QuantumProgramExecutionRequest(ExecutionRequest):
|
|
43
43
|
execution_payload: QuantumCodeExecution
|
44
44
|
|
45
45
|
|
46
|
+
class ProviderJobs(BaseModel):
|
47
|
+
provider_job_id: str = Field(default="DUMMY")
|
48
|
+
cost: float = Field(default=0)
|
49
|
+
|
50
|
+
|
51
|
+
class JobCost(BaseModel):
|
52
|
+
total_cost: float = Field(default=0)
|
53
|
+
currency_code: str = Field(default="USD")
|
54
|
+
organization: Optional[str] = Field(default=None)
|
55
|
+
jobs: list[ProviderJobs] = Field(default=[])
|
56
|
+
|
57
|
+
|
46
58
|
class ExecutionJobDetails(VersionedModel):
|
47
59
|
id: str
|
48
60
|
|
49
|
-
name: Optional[str]
|
61
|
+
name: Optional[str] = Field(default=None)
|
50
62
|
start_time: datetime
|
51
|
-
end_time: Optional[datetime]
|
63
|
+
end_time: Optional[datetime] = Field(default=None)
|
52
64
|
|
53
|
-
provider: Optional[str]
|
54
|
-
backend_name: Optional[str]
|
65
|
+
provider: Optional[str] = Field(default=None)
|
66
|
+
backend_name: Optional[str] = Field(default=None)
|
55
67
|
|
56
68
|
status: JobStatus
|
57
69
|
|
58
|
-
num_shots: Optional[int]
|
70
|
+
num_shots: Optional[int] = Field(default=None)
|
59
71
|
program_id: Optional[str] = Field(default=None)
|
60
72
|
|
61
73
|
error: Optional[str] = Field(default=None)
|
62
74
|
|
75
|
+
cost: Optional[JobCost] = Field(default=None)
|
76
|
+
|
63
77
|
|
64
78
|
class ExecutionJobsQueryResults(VersionedModel):
|
65
79
|
results: list[ExecutionJobDetails]
|
@@ -16,9 +16,6 @@ DEFAULT_SUPPORTED_FUNC_NAMES: set[str] = {"CLShift", "CRShift", "min", "max"}
|
|
16
16
|
DEFAULT_EXPRESSION_TYPE = "arithmetic"
|
17
17
|
IDENITIFIER_REGEX = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
|
18
18
|
|
19
|
-
_REPEATED_VARIABLES_ERROR_MESSAGE: str = (
|
20
|
-
"Repeated variables in the beginning of an arithmetic expression are not allowed."
|
21
|
-
)
|
22
19
|
ValidKeyValuePairs: TypeAlias = dict[str, set[str]]
|
23
20
|
|
24
21
|
SupportedNodesTypes = Union[
|
@@ -107,13 +104,25 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
107
104
|
raise ClassiqArithmeticError("Must call `validate` before getting ast_obj")
|
108
105
|
return self._ast_obj
|
109
106
|
|
110
|
-
|
111
|
-
|
107
|
+
def _check_repeated_variables(
|
108
|
+
self, variables: tuple[Any, Any], expr: ast.AST, error_suffix: str
|
109
|
+
) -> None:
|
110
|
+
if (
|
111
|
+
isinstance(expr, ast.BinOp)
|
112
|
+
and isinstance(expr.op, ast.Pow)
|
113
|
+
and ast.Pow not in self.supported_nodes
|
114
|
+
):
|
115
|
+
raise ClassiqValueError(
|
116
|
+
"Raising to a power (<var> ** <exp>) and multiplying a variable by "
|
117
|
+
"itself (<var> * <var>) are not supported"
|
118
|
+
)
|
112
119
|
if (
|
113
120
|
all(isinstance(var, ast.Name) for var in variables)
|
114
121
|
and variables[0].id == variables[1].id
|
115
122
|
):
|
116
|
-
raise ClassiqValueError(
|
123
|
+
raise ClassiqValueError(
|
124
|
+
f"Expression {ast.unparse(expr)!r} is not supported ({error_suffix})"
|
125
|
+
)
|
117
126
|
|
118
127
|
@staticmethod
|
119
128
|
def _check_multiple_comparators(node: ast.Compare) -> None:
|
@@ -135,7 +144,11 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
135
144
|
)
|
136
145
|
|
137
146
|
def validate_Compare(self, node: ast.Compare) -> None: # noqa: N802
|
138
|
-
self._check_repeated_variables(
|
147
|
+
self._check_repeated_variables(
|
148
|
+
(node.left, node.comparators[0]),
|
149
|
+
node,
|
150
|
+
"both sides of the comparison are identical",
|
151
|
+
)
|
139
152
|
self._check_multiple_comparators(node)
|
140
153
|
|
141
154
|
def visit_Compare(self, node: ast.Compare) -> None:
|
@@ -143,7 +156,9 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
143
156
|
self.generic_visit(node)
|
144
157
|
|
145
158
|
def validate_BinOp(self, node: ast.BinOp) -> None: # noqa: N802
|
146
|
-
self._check_repeated_variables(
|
159
|
+
self._check_repeated_variables(
|
160
|
+
(node.left, node.right), node, "both sides of the operation are identical"
|
161
|
+
)
|
147
162
|
|
148
163
|
def visit_BinOp(self, node: ast.BinOp) -> None:
|
149
164
|
self.validate_BinOp(node)
|
@@ -151,7 +166,11 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
151
166
|
|
152
167
|
def validate_Call(self, node: ast.Call) -> None: # noqa: N802
|
153
168
|
if len(node.args) >= 2:
|
154
|
-
self._check_repeated_variables(
|
169
|
+
self._check_repeated_variables(
|
170
|
+
(node.args[0], node.args[1]),
|
171
|
+
node,
|
172
|
+
"the first two call arguments are identical",
|
173
|
+
)
|
155
174
|
node_id = AstNodeRewrite().extract_node_id(node)
|
156
175
|
if node_id not in self._supported_functions:
|
157
176
|
raise ClassiqValueError(f"{node_id} not in supported functions")
|
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
6
|
+
from classiq.interface.exceptions import ClassiqInternalError
|
6
7
|
from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
|
7
8
|
from classiq.interface.generator.functions.classical_type import (
|
8
9
|
ClassicalType,
|
@@ -57,18 +58,15 @@ class TypeName(ClassicalType, QuantumType):
|
|
57
58
|
|
58
59
|
@property
|
59
60
|
def fields(self) -> Mapping[str, "ConcreteQuantumType"]:
|
60
|
-
from classiq.qmod.model_state_container import QMODULE
|
61
|
-
|
62
61
|
if self._assigned_fields is None:
|
63
|
-
|
64
|
-
self._assigned_fields = {
|
65
|
-
field_name: field_type.model_copy()
|
66
|
-
for field_name, field_type in qstruct_fields.items()
|
67
|
-
}
|
68
|
-
|
62
|
+
raise ClassiqInternalError("Fields not set")
|
69
63
|
return self._assigned_fields
|
70
64
|
|
71
|
-
|
65
|
+
@property
|
66
|
+
def has_fields(self) -> bool:
|
67
|
+
return self._assigned_fields is not None
|
68
|
+
|
69
|
+
def set_fields(self, fields: Mapping[str, "ConcreteQuantumType"]) -> None:
|
72
70
|
self._assigned_fields = fields
|
73
71
|
|
74
72
|
|
@@ -21,6 +21,7 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
|
21
21
|
from classiq.interface.model.quantum_function_declaration import (
|
22
22
|
NamedParamsQuantumFunctionDeclaration,
|
23
23
|
PositionalArg,
|
24
|
+
QuantumOperandDeclaration,
|
24
25
|
)
|
25
26
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
26
27
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -213,26 +214,43 @@ def _generate_arg_id(
|
|
213
214
|
arg_name = arg_declaration.name
|
214
215
|
if isinstance(arg_declaration, ClassicalParameterDeclaration):
|
215
216
|
return {arg_name: evaluated_classical_param_to_str(arg_value)}
|
216
|
-
|
217
|
+
if isinstance(arg_declaration, PortDeclaration):
|
218
|
+
return {arg_name: _evaluated_port_to_str(arg_value)}
|
219
|
+
if isinstance(arg_declaration, QuantumOperandDeclaration):
|
220
|
+
return {arg_name: _evaluated_operand_to_str(arg_value)}
|
217
221
|
|
218
222
|
|
219
|
-
@singledispatch
|
220
223
|
def _evaluated_arg_to_str(arg: Any) -> str:
|
221
224
|
if isinstance(arg, str):
|
222
225
|
return arg
|
223
226
|
return str(uuid.uuid4())
|
224
227
|
|
225
228
|
|
226
|
-
@
|
229
|
+
@singledispatch
|
230
|
+
def _evaluated_port_to_str(arg: Any) -> str:
|
231
|
+
return _evaluated_arg_to_str(arg)
|
232
|
+
|
233
|
+
|
234
|
+
@_evaluated_port_to_str.register
|
227
235
|
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
228
236
|
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
229
237
|
|
230
238
|
|
231
|
-
@
|
239
|
+
@_evaluated_port_to_str.register
|
232
240
|
def _evaluated_symbol_to_str(port: Symbol) -> str:
|
233
241
|
return repr(port)
|
234
242
|
|
235
243
|
|
236
|
-
@
|
237
|
-
def _evaluated_operand_to_str(
|
244
|
+
@singledispatch
|
245
|
+
def _evaluated_operand_to_str(arg: Any) -> str:
|
246
|
+
return _evaluated_arg_to_str(arg)
|
247
|
+
|
248
|
+
|
249
|
+
@_evaluated_operand_to_str.register
|
250
|
+
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
238
251
|
return operand.closure_id
|
252
|
+
|
253
|
+
|
254
|
+
@_evaluated_operand_to_str.register
|
255
|
+
def _evaluated_list_operands_to_str(operands: list) -> str:
|
256
|
+
return json.dumps([_evaluated_operand_to_str(ope) for ope in operands])
|
@@ -35,7 +35,6 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
|
|
35
35
|
set_is_signed,
|
36
36
|
set_length,
|
37
37
|
set_size,
|
38
|
-
set_struct_fields,
|
39
38
|
)
|
40
39
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
41
40
|
|
@@ -198,7 +197,7 @@ def _evaluate_qstruct_in_quantum_symbol(
|
|
198
197
|
field_name: evaluate_type_in_quantum_symbol(field_type, scope, param_name)
|
199
198
|
for field_name, field_type in type_to_update.fields.items()
|
200
199
|
}
|
201
|
-
|
200
|
+
type_to_update.set_fields(new_fields)
|
202
201
|
return type_to_update
|
203
202
|
|
204
203
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from collections.abc import Mapping
|
2
1
|
from typing import Optional
|
3
2
|
|
4
3
|
from classiq.interface.exceptions import (
|
@@ -157,12 +156,6 @@ def set_element_type(
|
|
157
156
|
quantum_array.element_type = element_type
|
158
157
|
|
159
158
|
|
160
|
-
def set_struct_fields(
|
161
|
-
quantum_struct: TypeName, fields: Mapping[str, ConcreteQuantumType]
|
162
|
-
) -> None:
|
163
|
-
quantum_struct._set_fields(fields)
|
164
|
-
|
165
|
-
|
166
159
|
def set_length(quantum_array: QuantumBitvector, length: int) -> None:
|
167
160
|
quantum_array.length = Expression(expr=str(length))
|
168
161
|
|
@@ -26,6 +26,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
26
26
|
PositionalArg,
|
27
27
|
)
|
28
28
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
29
|
+
from classiq.interface.source_reference import SourceReference
|
29
30
|
|
30
31
|
from classiq.model_expansions.capturing.captured_var_manager import update_captured_vars
|
31
32
|
from classiq.model_expansions.capturing.mangling_utils import demangle_name
|
@@ -82,6 +83,7 @@ class OperationBuilder:
|
|
82
83
|
self._operations: list[OperationContext] = []
|
83
84
|
self._blocks: list[str] = []
|
84
85
|
self._functions_scope = functions_scope
|
86
|
+
self._current_source_ref: Optional[SourceReference] = None
|
85
87
|
|
86
88
|
@property
|
87
89
|
def current_operation(self) -> Closure:
|
@@ -99,6 +101,8 @@ class OperationBuilder:
|
|
99
101
|
return self._operations[-1].blocks[self._blocks[-1]].statements
|
100
102
|
|
101
103
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
104
|
+
if self._current_source_ref is not None:
|
105
|
+
statement.source_ref = self._current_source_ref
|
102
106
|
self._current_statements.append(statement)
|
103
107
|
|
104
108
|
@property
|
@@ -131,6 +135,15 @@ class OperationBuilder:
|
|
131
135
|
self._update_captured_vars()
|
132
136
|
self._operations.pop()
|
133
137
|
|
138
|
+
@contextmanager
|
139
|
+
def source_ref_context(
|
140
|
+
self, source_ref: Optional[SourceReference]
|
141
|
+
) -> Iterator[None]:
|
142
|
+
previous_source_ref = self._current_source_ref
|
143
|
+
self._current_source_ref = source_ref
|
144
|
+
yield
|
145
|
+
self._current_source_ref = previous_source_ref
|
146
|
+
|
134
147
|
def _update_captured_vars(self) -> None:
|
135
148
|
for block in self._operations[-1].blocks.values():
|
136
149
|
block.captured_vars = update_captured_vars(block.captured_vars)
|
@@ -6,8 +6,10 @@ from typing import Any, Optional, cast
|
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
from numpy.random import permutation
|
9
|
+
from pydantic import ValidationError
|
9
10
|
|
10
11
|
from classiq.interface.exceptions import (
|
12
|
+
ClassiqError,
|
11
13
|
ClassiqExpansionError,
|
12
14
|
ClassiqInternalExpansionError,
|
13
15
|
)
|
@@ -92,12 +94,6 @@ from classiq.qmod.builtins.functions import permute
|
|
92
94
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
93
95
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
94
96
|
|
95
|
-
STATEMENT_TYPES_FOR_SOURCE_REFERENCE_PROPAGATION: tuple[type[QuantumStatement], ...] = (
|
96
|
-
QuantumFunctionCall,
|
97
|
-
VariableDeclarationStatement,
|
98
|
-
QuantumAssignmentOperation,
|
99
|
-
)
|
100
|
-
|
101
97
|
|
102
98
|
class Interpreter:
|
103
99
|
def __init__(
|
@@ -168,6 +164,8 @@ class Interpreter:
|
|
168
164
|
except Exception as e:
|
169
165
|
if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
|
170
166
|
raise e
|
167
|
+
if not isinstance(e, (ClassiqError, ValidationError)):
|
168
|
+
raise ClassiqInternalExpansionError(str(e)) from None
|
171
169
|
prefix = ""
|
172
170
|
if not isinstance(e, ClassiqExpansionError):
|
173
171
|
prefix = f"{type(e).__name__}: "
|
@@ -333,17 +331,14 @@ class Interpreter:
|
|
333
331
|
self.emit_statement(statement)
|
334
332
|
|
335
333
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
336
|
-
|
334
|
+
source_ref = statement.source_ref
|
335
|
+
error_context = (
|
337
336
|
self._error_manager.node_context(statement)
|
338
|
-
if
|
337
|
+
if source_ref is not None
|
339
338
|
else nullcontext()
|
340
|
-
)
|
339
|
+
)
|
340
|
+
with error_context, self._builder.source_ref_context(source_ref):
|
341
341
|
self.emit(statement)
|
342
|
-
if isinstance(
|
343
|
-
statement,
|
344
|
-
STATEMENT_TYPES_FOR_SOURCE_REFERENCE_PROPAGATION,
|
345
|
-
):
|
346
|
-
self._builder.current_statement.source_ref = statement.source_ref
|
347
342
|
|
348
343
|
def _expand_operation(self, operation: Closure) -> OperationContext:
|
349
344
|
with self._builder.operation_context(operation) as context:
|