classiq 0.43.2__py3-none-any.whl → 0.44.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 +7 -1
- classiq/_internals/client.py +4 -7
- classiq/_internals/host_checker.py +34 -12
- classiq/_internals/jobs.py +2 -2
- classiq/applications/chemistry/chemistry_model_constructor.py +12 -6
- classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/finance/finance_model_constructor.py +3 -2
- classiq/applications/grover/grover_model_constructor.py +7 -5
- classiq/applications/hamiltonian/__init__.py +0 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
- classiq/applications/qnn/qlayer.py +1 -1
- classiq/exceptions.py +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -18
- classiq/interface/backend/backend_preferences.py +15 -16
- classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
- classiq/interface/backend/pydantic_backend.py +0 -5
- classiq/interface/backend/quantum_backend_providers.py +3 -2
- classiq/interface/chemistry/operator.py +5 -1
- classiq/interface/debug_info/__init__.py +0 -0
- classiq/interface/debug_info/debug_info.py +32 -0
- classiq/interface/executor/execution_preferences.py +1 -45
- classiq/interface/executor/result.py +25 -12
- classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
- classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
- classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
- classiq/interface/generator/application_apis/finance_declarations.py +36 -22
- classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
- classiq/interface/generator/arith/arithmetic_expression_abc.py +21 -1
- classiq/interface/generator/arith/binary_ops.py +5 -4
- classiq/interface/generator/arith/extremum_operations.py +43 -19
- classiq/interface/generator/constant.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
- classiq/interface/generator/expressions/expression_constants.py +3 -1
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -66
- classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
- classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
- classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
- classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
- classiq/interface/generator/functions/classical_function_declaration.py +14 -6
- classiq/interface/generator/functions/classical_type.py +7 -76
- classiq/interface/generator/functions/concrete_types.py +55 -0
- classiq/interface/generator/functions/function_declaration.py +10 -10
- classiq/interface/generator/functions/type_name.py +104 -0
- classiq/interface/generator/generated_circuit_data.py +3 -3
- classiq/interface/generator/model/model.py +11 -0
- classiq/interface/generator/model/preferences/preferences.py +5 -0
- classiq/interface/generator/quantum_function_call.py +3 -0
- classiq/interface/generator/quantum_program.py +2 -2
- classiq/interface/generator/register_role.py +7 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
- classiq/interface/generator/types/qstruct_declaration.py +17 -0
- classiq/interface/generator/types/struct_declaration.py +1 -1
- classiq/interface/helpers/validation_helpers.py +1 -17
- classiq/interface/ide/visual_model.py +9 -2
- classiq/interface/interface_version.py +1 -0
- classiq/interface/model/bind_operation.py +25 -5
- classiq/interface/model/classical_parameter_declaration.py +8 -5
- classiq/interface/model/control.py +5 -5
- classiq/interface/model/handle_binding.py +185 -12
- classiq/interface/model/inplace_binary_operation.py +16 -4
- classiq/interface/model/model.py +28 -5
- classiq/interface/model/native_function_definition.py +8 -4
- classiq/interface/model/parameter.py +14 -0
- classiq/interface/model/port_declaration.py +20 -2
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +21 -6
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
- classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
- classiq/interface/model/quantum_function_call.py +135 -192
- classiq/interface/model/quantum_function_declaration.py +147 -165
- classiq/interface/model/quantum_lambda_function.py +24 -6
- classiq/interface/model/quantum_statement.py +34 -8
- classiq/interface/model/quantum_type.py +61 -10
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/interface/model/statement_block.py +2 -0
- classiq/interface/model/validation_handle.py +7 -0
- classiq/interface/server/global_versions.py +4 -4
- classiq/interface/server/routes.py +2 -0
- classiq/interface/source_reference.py +59 -0
- classiq/qmod/__init__.py +2 -3
- classiq/qmod/builtins/functions.py +39 -11
- classiq/qmod/builtins/operations.py +171 -40
- classiq/qmod/declaration_inferrer.py +99 -56
- classiq/qmod/expression_query.py +1 -1
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/pretty_printer.py +71 -53
- classiq/qmod/pretty_print/pretty_printer.py +98 -52
- classiq/qmod/qfunc.py +11 -5
- classiq/qmod/qmod_parameter.py +1 -2
- classiq/qmod/qmod_variable.py +364 -172
- classiq/qmod/quantum_callable.py +3 -3
- classiq/qmod/quantum_expandable.py +119 -65
- classiq/qmod/quantum_function.py +15 -3
- classiq/qmod/semantics/annotation.py +12 -13
- classiq/qmod/semantics/error_manager.py +36 -10
- classiq/qmod/semantics/static_semantics_visitor.py +163 -75
- classiq/qmod/semantics/validation/func_call_validation.py +42 -96
- classiq/qmod/semantics/validation/handle_validation.py +85 -0
- classiq/qmod/semantics/validation/types_validation.py +108 -1
- classiq/qmod/type_attribute_remover.py +32 -0
- classiq/qmod/utilities.py +26 -5
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/METADATA +3 -3
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/RECORD +111 -99
- classiq/qmod/qmod_struct.py +0 -13
- /classiq/{interface/ide/show.py → show.py} +0 -0
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/WHEEL +0 -0
@@ -1,41 +1,72 @@
|
|
1
1
|
from contextlib import contextmanager
|
2
|
-
from typing import
|
2
|
+
from typing import (
|
3
|
+
Any,
|
4
|
+
Dict,
|
5
|
+
Iterator,
|
6
|
+
List,
|
7
|
+
Mapping,
|
8
|
+
Optional,
|
9
|
+
Sequence,
|
10
|
+
Set,
|
11
|
+
Tuple,
|
12
|
+
Type,
|
13
|
+
)
|
3
14
|
|
4
15
|
from classiq.interface.generator.function_params import PortDirection
|
16
|
+
from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
|
5
17
|
from classiq.interface.generator.functions.port_declaration import (
|
6
18
|
PortDeclarationDirection,
|
7
19
|
)
|
8
20
|
from classiq.interface.generator.visitor import Visitor
|
9
21
|
from classiq.interface.model.handle_binding import (
|
22
|
+
FieldHandleBinding,
|
10
23
|
HandleBinding,
|
11
24
|
SlicedHandleBinding,
|
12
25
|
SubscriptHandleBinding,
|
13
26
|
)
|
27
|
+
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
14
28
|
from classiq.interface.model.model import Model
|
15
29
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
16
30
|
from classiq.interface.model.port_declaration import PortDeclaration
|
17
31
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
18
32
|
from classiq.interface.model.quantum_function_declaration import (
|
33
|
+
AnonQuantumOperandDeclaration,
|
19
34
|
QuantumFunctionDeclaration,
|
20
35
|
QuantumOperandDeclaration,
|
21
36
|
)
|
22
37
|
from classiq.interface.model.quantum_lambda_function import (
|
23
38
|
QuantumLambdaFunction,
|
24
39
|
)
|
25
|
-
from classiq.interface.model.quantum_statement import QuantumOperation
|
40
|
+
from classiq.interface.model.quantum_statement import HandleMetadata, QuantumOperation
|
26
41
|
from classiq.interface.model.validation_handle import HandleState
|
27
42
|
from classiq.interface.model.variable_declaration_statement import (
|
28
43
|
VariableDeclarationStatement,
|
29
44
|
)
|
30
45
|
from classiq.interface.model.within_apply_operation import WithinApply
|
31
46
|
|
47
|
+
from classiq import AnonClassicalParameterDeclaration
|
32
48
|
from classiq.exceptions import ClassiqSemanticError
|
33
49
|
from classiq.qmod.semantics.annotation import annotate_function_call_decl
|
34
50
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
35
51
|
from classiq.qmod.semantics.validation.func_call_validation import (
|
52
|
+
check_no_overlapping_quantum_args,
|
36
53
|
validate_call_arguments,
|
37
54
|
)
|
38
|
-
from classiq.qmod.semantics.validation.
|
55
|
+
from classiq.qmod.semantics.validation.handle_validation import resolve_handle
|
56
|
+
from classiq.qmod.semantics.validation.types_validation import (
|
57
|
+
check_cstruct_has_fields,
|
58
|
+
check_duplicate_types,
|
59
|
+
check_qstruct_fields_are_defined,
|
60
|
+
check_qstruct_flexibility,
|
61
|
+
check_qstruct_has_fields,
|
62
|
+
check_qstruct_is_not_recursive,
|
63
|
+
)
|
64
|
+
|
65
|
+
HANDLE_BINDING_PART_MESSAGE = {
|
66
|
+
SubscriptHandleBinding: "array subscript",
|
67
|
+
SlicedHandleBinding: "array slice",
|
68
|
+
FieldHandleBinding: "field access",
|
69
|
+
}
|
39
70
|
|
40
71
|
|
41
72
|
class StaticScope:
|
@@ -45,16 +76,18 @@ class StaticScope:
|
|
45
76
|
parameters: List[str],
|
46
77
|
operands: Dict[str, QuantumOperandDeclaration],
|
47
78
|
variables_to_states: Dict[str, HandleState],
|
79
|
+
variables_to_types: Dict[str, ConcreteQuantumType],
|
48
80
|
) -> None:
|
49
81
|
self.parameters = parameters
|
50
82
|
self.operands = operands
|
51
83
|
self.variable_states = variables_to_states
|
84
|
+
self.variables_to_types = variables_to_types
|
52
85
|
|
53
86
|
|
54
87
|
class StaticSemanticsVisitor(Visitor):
|
55
88
|
def __init__(
|
56
89
|
self,
|
57
|
-
functions_dict:
|
90
|
+
functions_dict: Mapping[str, QuantumFunctionDeclaration],
|
58
91
|
constants: List[str],
|
59
92
|
) -> None:
|
60
93
|
self._scope: List[StaticScope] = []
|
@@ -73,25 +106,46 @@ class StaticSemanticsVisitor(Visitor):
|
|
73
106
|
self._scope.pop()
|
74
107
|
|
75
108
|
def visit_Model(self, model: Model) -> None:
|
76
|
-
check_duplicate_types([*model.enums, *model.types])
|
109
|
+
check_duplicate_types([*model.enums, *model.types, *model.qstructs])
|
110
|
+
for qstruct in model.qstructs:
|
111
|
+
check_qstruct_has_fields(qstruct)
|
112
|
+
if check_qstruct_fields_are_defined(
|
113
|
+
qstruct
|
114
|
+
) and check_qstruct_is_not_recursive(qstruct):
|
115
|
+
check_qstruct_flexibility(qstruct)
|
116
|
+
for cstruct in model.types:
|
117
|
+
check_cstruct_has_fields(cstruct)
|
77
118
|
self.visit_BaseModel(model)
|
78
119
|
|
79
120
|
def visit_NativeFunctionDefinition(
|
80
121
|
self, func_def: NativeFunctionDefinition
|
81
122
|
) -> None:
|
82
|
-
if len(func_def.body) == 0:
|
83
|
-
return
|
84
123
|
scope = StaticScope(
|
85
|
-
parameters=list(func_def.
|
86
|
-
operands=dict(func_def.
|
124
|
+
parameters=list(func_def.param_names) + self._constants,
|
125
|
+
operands=dict(func_def.operand_declarations_dict),
|
87
126
|
variables_to_states=initialize_variables_to_state(
|
88
|
-
|
127
|
+
func_def.port_declarations
|
89
128
|
),
|
129
|
+
variables_to_types={
|
130
|
+
port.name: port.quantum_type for port in func_def.port_declarations
|
131
|
+
},
|
90
132
|
)
|
91
|
-
with self.scoped_visit(scope):
|
133
|
+
with self.scoped_visit(scope), self._error_manager.call(func_def.name):
|
134
|
+
parameter_declaration_names = [
|
135
|
+
decl.name for decl in func_def.positional_arg_declarations
|
136
|
+
]
|
137
|
+
seen_names: Set[str] = set()
|
138
|
+
for name in parameter_declaration_names:
|
139
|
+
if name in seen_names:
|
140
|
+
self._error_manager.add_error(
|
141
|
+
f"duplicate parameter declaration name {name!r}"
|
142
|
+
)
|
143
|
+
seen_names.add(name)
|
144
|
+
if len(func_def.body) == 0:
|
145
|
+
return
|
92
146
|
self.visit(func_def.body)
|
93
147
|
with self._error_manager.node_context(func_def.body[-1]):
|
94
|
-
for port_decl in func_def.port_declarations
|
148
|
+
for port_decl in func_def.port_declarations:
|
95
149
|
handle_state = self.current_scope.variable_states[port_decl.name]
|
96
150
|
expected_terminal_state = EXPECTED_TERMINAL_STATES.get(
|
97
151
|
port_decl.direction
|
@@ -110,6 +164,7 @@ class StaticSemanticsVisitor(Visitor):
|
|
110
164
|
parameters=self.current_scope.parameters,
|
111
165
|
operands=self.current_scope.operands,
|
112
166
|
variables_to_states=self.current_scope.variable_states.copy(),
|
167
|
+
variables_to_types=self.current_scope.variables_to_types.copy(),
|
113
168
|
)
|
114
169
|
with self.scoped_visit(scope):
|
115
170
|
self.visit(within_apply.compute)
|
@@ -147,9 +202,13 @@ class StaticSemanticsVisitor(Visitor):
|
|
147
202
|
**self.current_scope.operands,
|
148
203
|
},
|
149
204
|
)
|
150
|
-
|
151
|
-
|
152
|
-
|
205
|
+
elif isinstance(op, InplaceBinaryOperation):
|
206
|
+
check_no_overlapping_quantum_args(
|
207
|
+
[op.target, op.value], op.operation.value
|
208
|
+
)
|
209
|
+
self._handle_inputs(op.readable_inputs)
|
210
|
+
self._handle_outputs(op.readable_outputs)
|
211
|
+
self._handle_inouts(op.readable_inouts)
|
153
212
|
self.generic_visit(op)
|
154
213
|
|
155
214
|
def visit_VariableDeclarationStatement(
|
@@ -163,89 +222,117 @@ class StaticSemanticsVisitor(Visitor):
|
|
163
222
|
return
|
164
223
|
|
165
224
|
self.current_scope.variable_states[declaration.name] = HandleState.UNINITIALIZED
|
225
|
+
self.current_scope.variables_to_types[declaration.name] = (
|
226
|
+
declaration.quantum_type
|
227
|
+
)
|
166
228
|
|
167
229
|
def visit_QuantumLambdaFunction(self, lambda_func: QuantumLambdaFunction) -> None:
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
for param in self.current_scope.parameters
|
172
|
-
+ list(lambda_func.func_decl.param_decls.keys())
|
173
|
-
]
|
174
|
-
ports = list(lambda_func.func_decl.port_declarations.values())
|
175
|
-
for i, port in enumerate(ports):
|
176
|
-
ports[i] = port.copy(
|
177
|
-
update={"name": lambda_func.rename_params.get(port.name, port.name)}
|
178
|
-
)
|
179
|
-
variables_to_states = self.current_scope.variable_states.copy()
|
180
|
-
original_operands = {
|
181
|
-
**dict(lambda_func.func_decl.operand_declarations),
|
182
|
-
**self.current_scope.operands,
|
183
|
-
}
|
184
|
-
renamed_operands: Dict[str, QuantumOperandDeclaration] = {}
|
185
|
-
for operand_name, operand_decl in original_operands.items():
|
186
|
-
renamed_name = lambda_func.rename_params.get(operand_name, operand_name)
|
187
|
-
renamed_operands[renamed_name] = operand_decl.copy(
|
188
|
-
update={"name": renamed_name}
|
189
|
-
)
|
230
|
+
renamed_parameters, renamed_operands, renamed_ports = (
|
231
|
+
self._get_renamed_parameters(lambda_func)
|
232
|
+
)
|
190
233
|
scope = StaticScope(
|
191
|
-
parameters=renamed_parameters,
|
192
|
-
operands=renamed_operands,
|
234
|
+
parameters=self.current_scope.parameters + renamed_parameters,
|
235
|
+
operands={**self.current_scope.operands, **renamed_operands},
|
193
236
|
variables_to_states={
|
194
|
-
**
|
195
|
-
**initialize_variables_to_state(
|
237
|
+
**self.current_scope.variable_states.copy(),
|
238
|
+
**initialize_variables_to_state(renamed_ports),
|
196
239
|
},
|
240
|
+
variables_to_types=self.current_scope.variables_to_types
|
241
|
+
| {port.name: port.quantum_type for port in renamed_ports},
|
197
242
|
)
|
198
243
|
with self.scoped_visit(scope):
|
199
244
|
self.generic_visit(lambda_func)
|
200
245
|
|
201
|
-
def
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
246
|
+
def _get_renamed_parameters(
|
247
|
+
self, lambda_func: QuantumLambdaFunction
|
248
|
+
) -> Tuple[List[str], Dict[str, QuantumOperandDeclaration], List[PortDeclaration]]:
|
249
|
+
renamed_parameters: List[str] = []
|
250
|
+
renamed_operands: Dict[str, QuantumOperandDeclaration] = {}
|
251
|
+
renamed_ports: List[PortDeclaration] = []
|
252
|
+
for idx, param in enumerate(lambda_func.func_decl.positional_arg_declarations):
|
253
|
+
param_name = lambda_func.get_rename_params()[idx]
|
254
|
+
if isinstance(param, AnonClassicalParameterDeclaration):
|
255
|
+
renamed_parameters.append(param_name)
|
256
|
+
elif isinstance(param, AnonQuantumOperandDeclaration):
|
257
|
+
renamed_operands[param_name] = param.rename(param_name)
|
258
|
+
else:
|
259
|
+
renamed_ports.append(param.rename(param_name))
|
260
|
+
return renamed_parameters, renamed_operands, renamed_ports
|
261
|
+
|
262
|
+
def visit_HandleBinding(self, handle: HandleBinding) -> None:
|
263
|
+
resolve_handle(self.current_scope, handle)
|
264
|
+
|
265
|
+
def _handle_state_changing_ios(
|
266
|
+
self,
|
267
|
+
ios: Sequence[HandleMetadata],
|
268
|
+
state: HandleState,
|
269
|
+
state_change_verb: str,
|
270
|
+
) -> None:
|
271
|
+
for handle_metadata in ios:
|
272
|
+
handle_binding = handle_metadata.handle
|
273
|
+
if isinstance(
|
274
|
+
handle_binding,
|
275
|
+
(SubscriptHandleBinding, SlicedHandleBinding, FieldHandleBinding),
|
276
|
+
):
|
207
277
|
self._error_manager.add_error(
|
208
|
-
f"
|
278
|
+
f"Cannot use {HANDLE_BINDING_PART_MESSAGE[type(handle_binding)]} of variable {handle_binding.name!r} in {state_change_verb} context"
|
209
279
|
)
|
210
280
|
continue
|
211
|
-
|
212
|
-
self.current_scope.variable_states[handle_binding.name] = (
|
213
|
-
HandleState.UNINITIALIZED
|
214
|
-
)
|
215
|
-
|
216
|
-
def _handle_outputs(self, outputs: Mapping[str, HandleBinding]) -> None:
|
217
|
-
for handle_binding in outputs.values():
|
218
|
-
handle_wiring_state = self.current_scope.variable_states[
|
281
|
+
handle_wiring_state = self.current_scope.variable_states.get(
|
219
282
|
handle_binding.name
|
220
|
-
|
221
|
-
|
222
|
-
|
283
|
+
)
|
284
|
+
if handle_wiring_state is not state:
|
285
|
+
state_prefix = (
|
286
|
+
""
|
287
|
+
if handle_wiring_state is None
|
288
|
+
else f"{handle_wiring_state.name.lower()} "
|
289
|
+
)
|
290
|
+
location = (
|
291
|
+
f" {handle_metadata.readable_location}"
|
292
|
+
if handle_metadata.readable_location is not None
|
293
|
+
else ""
|
294
|
+
)
|
223
295
|
self._error_manager.add_error(
|
224
|
-
f"
|
296
|
+
f"Cannot use {state_prefix}quantum variable {handle_binding.name!r}"
|
297
|
+
f"{location}"
|
225
298
|
)
|
226
|
-
continue
|
227
299
|
|
228
|
-
self.current_scope.variable_states[handle_binding.name] =
|
229
|
-
HandleState.INITIALIZED
|
230
|
-
)
|
300
|
+
self.current_scope.variable_states[handle_binding.name] = ~state
|
231
301
|
|
232
|
-
def
|
233
|
-
self
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
) -> None:
|
302
|
+
def _handle_inputs(self, inputs: Sequence[HandleMetadata]) -> None:
|
303
|
+
self._handle_state_changing_ios(
|
304
|
+
inputs, HandleState.INITIALIZED, "uninitialization"
|
305
|
+
)
|
306
|
+
|
307
|
+
def _handle_outputs(self, outputs: Sequence[HandleMetadata]) -> None:
|
308
|
+
self._handle_state_changing_ios(
|
309
|
+
outputs, HandleState.UNINITIALIZED, "initialization"
|
310
|
+
)
|
311
|
+
|
312
|
+
def _handle_inouts(self, inouts: Sequence[HandleMetadata]) -> None:
|
238
313
|
sliced_handles = set()
|
239
314
|
whole_handles = set()
|
240
315
|
|
241
|
-
for
|
316
|
+
for handle_metadata in inouts:
|
317
|
+
handle_binding = handle_metadata.handle
|
242
318
|
handle_wiring_state = self.current_scope.variable_states[
|
243
319
|
handle_binding.name
|
244
320
|
]
|
245
321
|
|
246
322
|
if handle_wiring_state is not HandleState.INITIALIZED:
|
323
|
+
state_prefix = (
|
324
|
+
""
|
325
|
+
if handle_wiring_state is None
|
326
|
+
else f"{handle_wiring_state.name.lower()} "
|
327
|
+
)
|
328
|
+
location = (
|
329
|
+
f" {handle_metadata.readable_location}"
|
330
|
+
if handle_metadata.readable_location is not None
|
331
|
+
else ""
|
332
|
+
)
|
247
333
|
self._error_manager.add_error(
|
248
|
-
f"
|
334
|
+
f"Cannot use {state_prefix}quantum variable {handle_binding.name!r}"
|
335
|
+
f"{location}"
|
249
336
|
)
|
250
337
|
|
251
338
|
if isinstance(
|
@@ -275,7 +362,7 @@ def resolve_function_calls(
|
|
275
362
|
|
276
363
|
|
277
364
|
def static_semantics_analysis_pass(
|
278
|
-
model: Model, error_type: Type[Exception] = ClassiqSemanticError
|
365
|
+
model: Model, error_type: Optional[Type[Exception]] = ClassiqSemanticError
|
279
366
|
) -> None:
|
280
367
|
StaticSemanticsVisitor(
|
281
368
|
{
|
@@ -284,7 +371,8 @@ def static_semantics_analysis_pass(
|
|
284
371
|
},
|
285
372
|
[const.name for const in model.constants],
|
286
373
|
).visit(model)
|
287
|
-
|
374
|
+
if error_type is not None:
|
375
|
+
ErrorManager().report_errors(error_type)
|
288
376
|
|
289
377
|
|
290
378
|
EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
|
@@ -294,7 +382,7 @@ EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
|
|
294
382
|
|
295
383
|
|
296
384
|
def initialize_variables_to_state(
|
297
|
-
port_declarations:
|
385
|
+
port_declarations: Sequence[PortDeclaration],
|
298
386
|
) -> Dict[str, HandleState]:
|
299
387
|
variables_to_state: Dict[str, HandleState] = dict()
|
300
388
|
|
@@ -1,13 +1,12 @@
|
|
1
|
-
import
|
2
|
-
from typing import Mapping, Set
|
1
|
+
from typing import List, Mapping
|
3
2
|
|
4
|
-
from classiq.interface.generator.
|
5
|
-
|
6
|
-
)
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
7
5
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
8
6
|
from classiq.interface.model.quantum_function_declaration import (
|
7
|
+
AnonQuantumFunctionDeclaration,
|
8
|
+
AnonQuantumOperandDeclaration,
|
9
9
|
QuantumFunctionDeclaration,
|
10
|
-
QuantumOperandDeclaration,
|
11
10
|
)
|
12
11
|
from classiq.interface.model.quantum_lambda_function import (
|
13
12
|
QuantumLambdaFunction,
|
@@ -22,128 +21,75 @@ def validate_call_arguments(
|
|
22
21
|
fc: QuantumFunctionCall,
|
23
22
|
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
24
23
|
) -> None:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def _check_ports_against_declaration(
|
40
|
-
call: QuantumFunctionCall, decl: QuantumFunctionDeclaration
|
41
|
-
) -> None:
|
42
|
-
call_input_names = set(call.inputs.keys())
|
43
|
-
|
44
|
-
_check_params_against_declaration(
|
45
|
-
call_input_names,
|
46
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Input),
|
47
|
-
call.func_name,
|
48
|
-
)
|
49
|
-
|
50
|
-
call_output_names = set(call.outputs.keys())
|
51
|
-
|
52
|
-
_check_params_against_declaration(
|
53
|
-
call_output_names,
|
54
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Output),
|
55
|
-
call.func_name,
|
56
|
-
)
|
57
|
-
|
58
|
-
inout_params = set(call.inouts.keys())
|
59
|
-
|
60
|
-
_check_params_against_declaration(
|
61
|
-
inout_params,
|
62
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Inout),
|
63
|
-
call.func_name,
|
64
|
-
)
|
24
|
+
pos_args = fc.positional_args
|
25
|
+
pos_params = fc.func_decl.positional_arg_declarations
|
26
|
+
if len(pos_args) != len(pos_params):
|
27
|
+
ErrorManager().add_error(
|
28
|
+
f"Function {fc.func_name} takes {len(pos_params)} arguments but "
|
29
|
+
f"{len(pos_args)} were given."
|
30
|
+
)
|
31
|
+
for arg, param in zip(pos_args, pos_params):
|
32
|
+
if not isinstance(arg, (Expression, HandleBinding)) and isinstance(
|
33
|
+
param, AnonQuantumOperandDeclaration
|
34
|
+
):
|
35
|
+
_check_operand_against_declaration(param, arg, function_dict, fc.func_name)
|
36
|
+
check_no_overlapping_quantum_args(fc.ports, fc.func_name)
|
65
37
|
|
66
38
|
|
67
39
|
def _check_operand_against_declaration(
|
68
|
-
|
69
|
-
operand_decl: QuantumOperandDeclaration,
|
40
|
+
operand_decl: AnonQuantumOperandDeclaration,
|
70
41
|
operand_argument: QuantumOperand,
|
71
42
|
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
43
|
+
func_name: str,
|
72
44
|
in_list: bool = False,
|
73
45
|
) -> None:
|
74
46
|
if isinstance(operand_argument, list):
|
75
47
|
if in_list:
|
76
48
|
ErrorManager().add_error(
|
77
|
-
f"{str(operand_argument)!r} argument to {
|
78
|
-
f"a valid operand. Nested operand lists are not permitted"
|
49
|
+
f"{str(operand_argument)!r} argument to {func_name!r} is not "
|
50
|
+
f"a valid operand. Nested operand lists are not permitted."
|
79
51
|
)
|
80
52
|
return
|
81
53
|
for arg in operand_argument:
|
82
54
|
_check_operand_against_declaration(
|
83
|
-
|
55
|
+
operand_decl, arg, function_dict, func_name, in_list=True
|
84
56
|
)
|
85
57
|
return
|
86
|
-
operand_arg_decl:
|
58
|
+
operand_arg_decl: AnonQuantumFunctionDeclaration
|
87
59
|
if isinstance(operand_argument, str):
|
88
60
|
if operand_argument not in function_dict:
|
89
61
|
ErrorManager().add_error(
|
90
|
-
f"{operand_argument!r} argument to {
|
91
|
-
f"registered function"
|
62
|
+
f"{operand_argument!r} argument to {func_name!r} is not a "
|
63
|
+
f"registered function."
|
92
64
|
)
|
93
65
|
return
|
94
66
|
operand_arg_decl = function_dict[operand_argument]
|
95
67
|
elif isinstance(operand_argument, QuantumLambdaFunction):
|
96
|
-
if operand_argument.func_decl is None:
|
97
|
-
return
|
98
68
|
operand_arg_decl = operand_argument.func_decl
|
99
69
|
else:
|
100
70
|
raise ClassiqError(
|
101
|
-
f"{str(operand_argument)!r} argument to {
|
102
|
-
f"valid operand"
|
71
|
+
f"{str(operand_argument)!r} argument to {func_name!r} is not a "
|
72
|
+
f"valid operand."
|
103
73
|
)
|
104
|
-
num_arg_parameters = len(operand_arg_decl.
|
105
|
-
num_decl_parameters = len(operand_decl.
|
74
|
+
num_arg_parameters = len(operand_arg_decl.positional_arg_declarations)
|
75
|
+
num_decl_parameters = len(operand_decl.positional_arg_declarations)
|
106
76
|
if num_arg_parameters != num_decl_parameters:
|
107
77
|
ErrorManager().add_error(
|
108
|
-
f"Signature of argument {operand_argument!r} to {
|
78
|
+
f"Signature of argument {operand_argument!r} to {func_name!r} "
|
109
79
|
f"does not match the signature of parameter {operand_decl.name!r}. "
|
110
80
|
f"{operand_decl.name!r} accepts {num_decl_parameters} parameters but "
|
111
|
-
f"{operand_argument!r} accepts {num_arg_parameters} parameters"
|
112
|
-
)
|
113
|
-
|
114
|
-
|
115
|
-
def _check_operands_against_declaration(
|
116
|
-
call: QuantumFunctionCall,
|
117
|
-
decl: QuantumFunctionDeclaration,
|
118
|
-
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
119
|
-
) -> None:
|
120
|
-
for operand_parameter, operand_argument in call.operands.items():
|
121
|
-
_check_operand_against_declaration(
|
122
|
-
call,
|
123
|
-
decl.operand_declarations[operand_parameter],
|
124
|
-
operand_argument,
|
125
|
-
function_dict,
|
81
|
+
f"{operand_argument!r} accepts {num_arg_parameters} parameters."
|
126
82
|
)
|
127
83
|
|
128
84
|
|
129
|
-
def
|
130
|
-
|
131
|
-
param_decls: Set[str],
|
132
|
-
callee_name: str,
|
85
|
+
def check_no_overlapping_quantum_args(
|
86
|
+
args: List[HandleBinding], func_name: str
|
133
87
|
) -> None:
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
if unknown_params:
|
143
|
-
ErrorManager().add_error(error_msg)
|
144
|
-
|
145
|
-
missing_params = param_decls - call_params
|
146
|
-
if missing_params:
|
147
|
-
ErrorManager().add_error(
|
148
|
-
f"Missing parameters {missing_params} in call to {callee_name!r}."
|
149
|
-
)
|
88
|
+
for idx, arg in enumerate(args):
|
89
|
+
for other_arg in args[idx + 1 :]:
|
90
|
+
if arg.overlaps(other_arg):
|
91
|
+
ErrorManager().add_error(
|
92
|
+
f"Cannot use the same part of variable {arg.name!r} in multiple "
|
93
|
+
f"arguments to function {func_name!r}."
|
94
|
+
)
|
95
|
+
break
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Optional, Union
|
2
|
+
|
3
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
4
|
+
from classiq.interface.model.handle_binding import (
|
5
|
+
ConcreteHandleBinding,
|
6
|
+
FieldHandleBinding,
|
7
|
+
HandleBinding,
|
8
|
+
NestedHandleBinding,
|
9
|
+
SlicedHandleBinding,
|
10
|
+
SubscriptHandleBinding,
|
11
|
+
)
|
12
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
|
13
|
+
|
14
|
+
import classiq.qmod.semantics.error_manager as error_manager
|
15
|
+
from classiq.qmod.model_state_container import QMODULE
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from classiq.qmod.semantics.static_semantics_visitor import StaticScope
|
19
|
+
|
20
|
+
|
21
|
+
def resolve_handle(scope: "StaticScope", handle: HandleBinding) -> None:
|
22
|
+
if handle.name not in scope.variables_to_types:
|
23
|
+
error_manager.append_error(handle, f"Variable {handle.name!r} is undefined.")
|
24
|
+
return
|
25
|
+
_resolve_handle_recursively(scope.variables_to_types[handle.name], handle)
|
26
|
+
|
27
|
+
|
28
|
+
def _resolve_handle_recursively(
|
29
|
+
qtype: QuantumType, handle: ConcreteHandleBinding
|
30
|
+
) -> Optional[QuantumType]:
|
31
|
+
if isinstance(handle, NestedHandleBinding):
|
32
|
+
return _resolve_nested_handle(qtype, handle)
|
33
|
+
return qtype
|
34
|
+
|
35
|
+
|
36
|
+
def _resolve_nested_handle(
|
37
|
+
qtype: QuantumType, handle: NestedHandleBinding
|
38
|
+
) -> Optional[QuantumType]:
|
39
|
+
nested_qtype = _resolve_handle_recursively(qtype, handle.base_handle)
|
40
|
+
if nested_qtype is None:
|
41
|
+
return None
|
42
|
+
if isinstance(handle, (SubscriptHandleBinding, SlicedHandleBinding)):
|
43
|
+
return _resolve_subscript_sliced_handle(nested_qtype, handle)
|
44
|
+
if TYPE_CHECKING:
|
45
|
+
assert isinstance(handle, FieldHandleBinding)
|
46
|
+
return _resolve_field_handle(nested_qtype, handle)
|
47
|
+
|
48
|
+
|
49
|
+
def _resolve_subscript_sliced_handle(
|
50
|
+
qtype: QuantumType, handle: Union[SubscriptHandleBinding, SlicedHandleBinding]
|
51
|
+
) -> Optional[QuantumType]:
|
52
|
+
if not isinstance(qtype, QuantumBitvector):
|
53
|
+
error_manager.append_error(handle, f"{qtype.type_name} is not subscriptable.")
|
54
|
+
return None
|
55
|
+
return qtype.element_type if isinstance(handle, SubscriptHandleBinding) else qtype
|
56
|
+
|
57
|
+
|
58
|
+
def _validate_field_access(qtype: QuantumType, handle: FieldHandleBinding) -> bool:
|
59
|
+
if not isinstance(qtype, TypeName):
|
60
|
+
error_manager.append_error(handle, f"{qtype.type_name} has no fields.")
|
61
|
+
return False
|
62
|
+
if qtype.name not in QMODULE.qstruct_decls:
|
63
|
+
error_manager.append_error(
|
64
|
+
handle, f"{qtype.type_name} is not a quantum struct."
|
65
|
+
)
|
66
|
+
return False
|
67
|
+
if handle.field not in qtype.fields:
|
68
|
+
error_manager.append_error(
|
69
|
+
handle,
|
70
|
+
f"Struct {qtype.type_name} has no field {handle.field!r}. "
|
71
|
+
f"Available fields: {', '.join(qtype.fields.keys())}",
|
72
|
+
)
|
73
|
+
return False
|
74
|
+
|
75
|
+
return True
|
76
|
+
|
77
|
+
|
78
|
+
def _resolve_field_handle(
|
79
|
+
qtype: QuantumType, handle: FieldHandleBinding
|
80
|
+
) -> Optional[QuantumType]:
|
81
|
+
if _validate_field_access(qtype, handle):
|
82
|
+
if TYPE_CHECKING:
|
83
|
+
assert isinstance(qtype, TypeName)
|
84
|
+
return qtype.fields[handle.field]
|
85
|
+
return None
|