classiq 0.42.2__py3-none-any.whl → 0.43.1__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.
Files changed (113) hide show
  1. classiq/__init__.py +2 -6
  2. classiq/_internals/api_wrapper.py +6 -12
  3. classiq/_internals/authentication/token_manager.py +5 -2
  4. classiq/_internals/jobs.py +5 -10
  5. classiq/analyzer/rb.py +3 -3
  6. classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
  7. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
  8. classiq/applications/finance/finance_model_constructor.py +16 -13
  9. classiq/applications/qsvm/__init__.py +1 -3
  10. classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
  11. classiq/exceptions.py +9 -4
  12. classiq/execution/execution_session.py +5 -2
  13. classiq/execution/qnn.py +1 -1
  14. classiq/executor.py +0 -2
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/chemistry/operator.py +19 -5
  17. classiq/interface/executor/constants.py +1 -0
  18. classiq/interface/finance/function_input.py +16 -10
  19. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
  21. classiq/interface/generator/arith/argument_utils.py +20 -3
  22. classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
  23. classiq/interface/generator/arith/binary_ops.py +8 -14
  24. classiq/interface/generator/arith/extremum_operations.py +30 -0
  25. classiq/interface/generator/arith/number_utils.py +1 -1
  26. classiq/interface/generator/arith/unary_ops.py +1 -3
  27. classiq/interface/generator/compiler_keywords.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
  29. classiq/interface/generator/expressions/enums/__init__.py +0 -20
  30. classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
  31. classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
  32. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
  33. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
  34. classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
  35. classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
  36. classiq/interface/generator/function_params.py +0 -4
  37. classiq/interface/generator/functions/__init__.py +0 -20
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
  39. classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
  40. classiq/interface/generator/functions/classical_type.py +22 -69
  41. classiq/interface/generator/functions/port_declaration.py +0 -11
  42. classiq/interface/generator/model/__init__.py +0 -1
  43. classiq/interface/generator/model/model.py +9 -185
  44. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
  45. classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
  46. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
  47. classiq/interface/generator/types/enum_declaration.py +57 -0
  48. classiq/interface/jobs.py +36 -65
  49. classiq/interface/model/bind_operation.py +3 -0
  50. classiq/interface/model/classical_parameter_declaration.py +3 -0
  51. classiq/interface/model/handle_binding.py +7 -0
  52. classiq/interface/model/inplace_binary_operation.py +13 -15
  53. classiq/interface/model/model.py +8 -20
  54. classiq/interface/model/native_function_definition.py +0 -17
  55. classiq/interface/model/quantum_function_call.py +63 -182
  56. classiq/interface/model/quantum_type.py +71 -10
  57. classiq/interface/server/routes.py +0 -6
  58. classiq/qmod/__init__.py +3 -3
  59. classiq/qmod/builtins/__init__.py +10 -1
  60. classiq/qmod/builtins/classical_execution_primitives.py +4 -2
  61. classiq/qmod/builtins/enums.py +177 -0
  62. classiq/qmod/builtins/functions.py +1 -2
  63. classiq/qmod/builtins/operations.py +2 -4
  64. classiq/qmod/builtins/structs.py +16 -17
  65. classiq/qmod/declaration_inferrer.py +23 -20
  66. classiq/qmod/model_state_container.py +2 -0
  67. classiq/qmod/native/pretty_printer.py +31 -13
  68. classiq/qmod/pretty_print/pretty_printer.py +52 -27
  69. classiq/qmod/qmod_constant.py +7 -3
  70. classiq/qmod/qmod_parameter.py +2 -1
  71. classiq/qmod/qmod_struct.py +9 -33
  72. classiq/qmod/qmod_variable.py +55 -22
  73. classiq/qmod/quantum_callable.py +6 -1
  74. classiq/qmod/quantum_expandable.py +29 -11
  75. classiq/qmod/quantum_function.py +8 -4
  76. classiq/qmod/semantics/annotation.py +38 -0
  77. classiq/qmod/semantics/error_manager.py +49 -0
  78. classiq/qmod/semantics/static_semantics_visitor.py +308 -0
  79. classiq/qmod/semantics/validation/func_call_validation.py +149 -0
  80. classiq/qmod/semantics/validation/types_validation.py +21 -0
  81. classiq/qmod/symbolic.py +6 -6
  82. classiq/qmod/symbolic_expr.py +26 -11
  83. classiq/qmod/utilities.py +23 -1
  84. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
  85. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
  86. classiq/_internals/_qfunc_ext.py +0 -6
  87. classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
  88. classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
  89. classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
  90. classiq/interface/generator/expressions/enums/chemistry.py +0 -28
  91. classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
  92. classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
  93. classiq/interface/generator/expressions/enums/optimizers.py +0 -9
  94. classiq/interface/generator/expressions/enums/pauli.py +0 -8
  95. classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
  96. classiq/interface/generator/functions/foreign_function_definition.py +0 -114
  97. classiq/interface/generator/functions/function_implementation.py +0 -107
  98. classiq/interface/generator/functions/native_function_definition.py +0 -155
  99. classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
  100. classiq/interface/generator/functions/register.py +0 -44
  101. classiq/interface/generator/functions/register_mapping_data.py +0 -106
  102. classiq/interface/generator/inequality_mixer.py +0 -51
  103. classiq/interface/generator/model/classical_main_validator.py +0 -106
  104. classiq/interface/generator/range_mixer.py +0 -56
  105. classiq/interface/generator/state_propagator.py +0 -74
  106. classiq/interface/model/resolvers/function_call_resolver.py +0 -64
  107. classiq/interface/model/validations/__init__.py +0 -0
  108. classiq/interface/model/validations/handle_validation_base.py +0 -55
  109. classiq/interface/model/validations/handles_validator.py +0 -153
  110. classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
  111. /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
  112. /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
  113. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
@@ -1,10 +1,11 @@
1
1
  import ast
2
2
  import functools
3
+ from dataclasses import is_dataclass
4
+ from enum import EnumMeta
3
5
  from inspect import isclass
4
6
  from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
5
7
 
6
8
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
7
- from classiq.interface.generator.functions.classical_type import CStructBase
8
9
  from classiq.interface.generator.model.constraints import Constraints
9
10
  from classiq.interface.generator.model.preferences.preferences import Preferences
10
11
  from classiq.interface.model.model import Model, SerializedModel
@@ -15,7 +16,7 @@ from classiq.interface.model.quantum_function_declaration import (
15
16
 
16
17
  from classiq.exceptions import ClassiqError
17
18
  from classiq.qmod.classical_function import CFunc
18
- from classiq.qmod.declaration_inferrer import ENUM_TYPE_MAPPING, infer_func_decl
19
+ from classiq.qmod.declaration_inferrer import infer_func_decl
19
20
  from classiq.qmod.qmod_constant import QConstant
20
21
  from classiq.qmod.qmod_parameter import CArray, CParam
21
22
  from classiq.qmod.qmod_variable import QVar
@@ -71,6 +72,7 @@ class QFunc(QExpandable):
71
72
  preferences: Optional[Preferences] = None,
72
73
  classical_execution_function: Optional[CFunc] = None,
73
74
  ) -> Model:
75
+ self._qmodule.enum_decls = dict()
74
76
  self._qmodule.type_decls = dict()
75
77
  self._qmodule.native_defs = dict()
76
78
  self._qmodule.constants = dict()
@@ -89,6 +91,7 @@ class QFunc(QExpandable):
89
91
  return Model(
90
92
  constants=list(self._qmodule.constants.values()),
91
93
  functions=list(self._qmodule.native_defs.values()),
94
+ enums=list(self._qmodule.enum_decls.values()),
92
95
  types=list(self._qmodule.type_decls.values()),
93
96
  **{key: value for key, value in model_extra_settings if value},
94
97
  )
@@ -166,12 +169,13 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
166
169
  or isclass(annotation)
167
170
  and issubclass(annotation, CParam)
168
171
  or isclass(annotation)
169
- and issubclass(annotation, CStructBase)
172
+ and is_dataclass(annotation)
173
+ or isclass(annotation)
174
+ and isinstance(annotation, EnumMeta)
170
175
  or get_origin(annotation) is CArray
171
176
  or (get_origin(annotation) or annotation) is QCallable
172
177
  or (get_origin(annotation) or annotation) is QCallableList
173
178
  or QVar.from_type_hint(annotation) is not None
174
- or annotation in ENUM_TYPE_MAPPING
175
179
  )
176
180
  }
177
181
  if _illegal_params:
@@ -0,0 +1,38 @@
1
+ from typing import List, Mapping
2
+
3
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
+ from classiq.interface.model.quantum_function_declaration import (
5
+ QuantumFunctionDeclaration,
6
+ )
7
+ from classiq.interface.model.quantum_lambda_function import (
8
+ QuantumCallable,
9
+ QuantumLambdaFunction,
10
+ QuantumOperand,
11
+ )
12
+
13
+ from classiq.exceptions import ClassiqError
14
+
15
+
16
+ def annotate_function_call_decl(
17
+ fc: QuantumFunctionCall,
18
+ function_dict: Mapping[str, QuantumFunctionDeclaration],
19
+ ) -> None:
20
+ if fc._func_decl is None:
21
+ func_decl = function_dict.get(fc.func_name)
22
+ if func_decl is None:
23
+ raise ClassiqError(
24
+ f"Error resolving function {fc.func_name}, the function is not found in included library."
25
+ )
26
+ fc.set_func_decl(func_decl)
27
+
28
+ for name, op in fc.operands.items():
29
+ op_decl = fc.func_decl.operand_declarations[name]
30
+ for qlambda in _get_lambda_defs(op):
31
+ if isinstance(qlambda, QuantumLambdaFunction):
32
+ qlambda.set_op_decl(op_decl)
33
+
34
+
35
+ def _get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
36
+ if isinstance(operand, list):
37
+ return operand
38
+ return [operand]
@@ -0,0 +1,49 @@
1
+ from contextlib import contextmanager
2
+ from typing import Iterator, List, Type
3
+
4
+ from classiq.interface.ast_node import ASTNode
5
+
6
+
7
+ class ErrorManager:
8
+ def __new__(cls) -> "ErrorManager":
9
+ if not hasattr(cls, "_instance"):
10
+ cls._instance = super().__new__(cls)
11
+ return cls._instance
12
+
13
+ def __init__(self) -> None:
14
+ if hasattr(self, "_instantiated"):
15
+ return
16
+ self._instantiated = True
17
+ self._errors: List[str] = []
18
+ self._current_nodes_stack: List[ASTNode] = []
19
+
20
+ def add_error(self, error: str) -> None:
21
+ source_referenced_error = (
22
+ f"{error}\n\t\tat {self._current_nodes_stack[-1].source_ref}"
23
+ if self._current_nodes_stack
24
+ and self._current_nodes_stack[-1].source_ref is not None
25
+ else error
26
+ )
27
+ self._errors.append(source_referenced_error)
28
+
29
+ def get_errors(self) -> List[str]:
30
+ return self._errors
31
+
32
+ def clear(self) -> None:
33
+ self._current_nodes_stack = []
34
+ self._errors = []
35
+
36
+ def has_errors(self) -> bool:
37
+ return len(self._errors) > 0
38
+
39
+ def report_errors(self, error_type: Type[Exception]) -> None:
40
+ if self.has_errors():
41
+ errors = self._errors
42
+ self.clear()
43
+ raise error_type("\n\t" + "\n\t".join(errors))
44
+
45
+ @contextmanager
46
+ def node_context(self, node: ASTNode) -> Iterator[None]:
47
+ self._current_nodes_stack.append(node)
48
+ yield
49
+ self._current_nodes_stack.pop()
@@ -0,0 +1,308 @@
1
+ from contextlib import contextmanager
2
+ from typing import Any, Dict, Iterator, List, Mapping, Type, Union
3
+
4
+ from classiq.interface.generator.function_params import PortDirection
5
+ from classiq.interface.generator.functions.port_declaration import (
6
+ PortDeclarationDirection,
7
+ )
8
+ from classiq.interface.generator.visitor import Visitor
9
+ from classiq.interface.model.handle_binding import (
10
+ HandleBinding,
11
+ SlicedHandleBinding,
12
+ SubscriptHandleBinding,
13
+ )
14
+ from classiq.interface.model.model import Model
15
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
16
+ from classiq.interface.model.port_declaration import PortDeclaration
17
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
18
+ from classiq.interface.model.quantum_function_declaration import (
19
+ QuantumFunctionDeclaration,
20
+ QuantumOperandDeclaration,
21
+ )
22
+ from classiq.interface.model.quantum_lambda_function import (
23
+ QuantumLambdaFunction,
24
+ )
25
+ from classiq.interface.model.quantum_statement import QuantumOperation
26
+ from classiq.interface.model.validation_handle import HandleState
27
+ from classiq.interface.model.variable_declaration_statement import (
28
+ VariableDeclarationStatement,
29
+ )
30
+ from classiq.interface.model.within_apply_operation import WithinApply
31
+
32
+ from classiq.exceptions import ClassiqSemanticError
33
+ from classiq.qmod.semantics.annotation import annotate_function_call_decl
34
+ from classiq.qmod.semantics.error_manager import ErrorManager
35
+ from classiq.qmod.semantics.validation.func_call_validation import (
36
+ validate_call_arguments,
37
+ )
38
+ from classiq.qmod.semantics.validation.types_validation import check_duplicate_types
39
+
40
+
41
+ class StaticScope:
42
+
43
+ def __init__(
44
+ self,
45
+ parameters: List[str],
46
+ operands: Dict[str, QuantumOperandDeclaration],
47
+ variables_to_states: Dict[str, HandleState],
48
+ ) -> None:
49
+ self.parameters = parameters
50
+ self.operands = operands
51
+ self.variable_states = variables_to_states
52
+
53
+
54
+ class StaticSemanticsVisitor(Visitor):
55
+ def __init__(
56
+ self,
57
+ functions_dict: Dict[str, QuantumFunctionDeclaration],
58
+ constants: List[str],
59
+ ) -> None:
60
+ self._scope: List[StaticScope] = []
61
+ self._error_manager = ErrorManager()
62
+ self._functions_dict = functions_dict
63
+ self._constants = constants
64
+
65
+ @property
66
+ def current_scope(self) -> StaticScope:
67
+ return self._scope[-1]
68
+
69
+ @contextmanager
70
+ def scoped_visit(self, scope: StaticScope) -> Iterator[None]:
71
+ self._scope.append(scope)
72
+ yield
73
+ self._scope.pop()
74
+
75
+ def visit_Model(self, model: Model) -> None:
76
+ check_duplicate_types([*model.enums, *model.types])
77
+ self.visit_BaseModel(model)
78
+
79
+ def visit_NativeFunctionDefinition(
80
+ self, func_def: NativeFunctionDefinition
81
+ ) -> None:
82
+ if len(func_def.body) == 0:
83
+ return
84
+ scope = StaticScope(
85
+ parameters=list(func_def.param_decls.keys()) + self._constants,
86
+ operands=dict(func_def.operand_declarations),
87
+ variables_to_states=initialize_variables_to_state(
88
+ list(func_def.port_declarations.values())
89
+ ),
90
+ )
91
+ with self.scoped_visit(scope):
92
+ self.visit(func_def.body)
93
+ with self._error_manager.node_context(func_def.body[-1]):
94
+ for port_decl in func_def.port_declarations.values():
95
+ handle_state = self.current_scope.variable_states[port_decl.name]
96
+ expected_terminal_state = EXPECTED_TERMINAL_STATES.get(
97
+ port_decl.direction
98
+ )
99
+ if (
100
+ expected_terminal_state is not None
101
+ and handle_state is not expected_terminal_state
102
+ ):
103
+ self._error_manager.add_error(
104
+ f"At the end of the function, port `{port_decl.name}` is expected to be {expected_terminal_state.name.lower()} but it isn't"
105
+ )
106
+
107
+ def visit_WithinApply(self, within_apply: WithinApply) -> None:
108
+ initial_variables_to_state = self.current_scope.variable_states.copy()
109
+ scope = StaticScope(
110
+ parameters=self.current_scope.parameters,
111
+ operands=self.current_scope.operands,
112
+ variables_to_states=self.current_scope.variable_states.copy(),
113
+ )
114
+ with self.scoped_visit(scope):
115
+ self.visit(within_apply.compute)
116
+ compute_captured_variables = {
117
+ var
118
+ for var, state in self.current_scope.variable_states.items()
119
+ if var in initial_variables_to_state
120
+ and state != initial_variables_to_state[var]
121
+ }
122
+ self.visit(within_apply.action)
123
+ variables_to_state = self.current_scope.variable_states.copy()
124
+ self.current_scope.variable_states.update(
125
+ {
126
+ var: state
127
+ for var, state in variables_to_state.items()
128
+ if var in self.current_scope.variable_states
129
+ and var not in compute_captured_variables
130
+ }
131
+ )
132
+
133
+ def visit_QuantumOperation(self, op: QuantumOperation) -> None:
134
+ with self._error_manager.node_context(op):
135
+ if isinstance(op, QuantumFunctionCall):
136
+ annotate_function_call_decl(
137
+ op,
138
+ {
139
+ **self._functions_dict,
140
+ **self.current_scope.operands,
141
+ },
142
+ )
143
+ validate_call_arguments(
144
+ op,
145
+ {
146
+ **self._functions_dict,
147
+ **self.current_scope.operands,
148
+ },
149
+ )
150
+ self._handle_inputs(op.wiring_inputs)
151
+ self._handle_outputs(op.wiring_outputs)
152
+ self._handle_inouts(op.wiring_inouts)
153
+ self.generic_visit(op)
154
+
155
+ def visit_VariableDeclarationStatement(
156
+ self, declaration: VariableDeclarationStatement
157
+ ) -> None:
158
+ handle_wiring_state = self.current_scope.variable_states.get(declaration.name)
159
+ if handle_wiring_state is not None:
160
+ self._error_manager.add_error(
161
+ f"Trying to declare a variable of the same name as previously declared variable {declaration.name}"
162
+ )
163
+ return
164
+
165
+ self.current_scope.variable_states[declaration.name] = HandleState.UNINITIALIZED
166
+
167
+ def visit_QuantumLambdaFunction(self, lambda_func: QuantumLambdaFunction) -> None:
168
+ assert lambda_func.func_decl is not None
169
+ renamed_parameters = [
170
+ lambda_func.rename_params.get(param, param)
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
+ )
190
+ scope = StaticScope(
191
+ parameters=renamed_parameters,
192
+ operands=renamed_operands,
193
+ variables_to_states={
194
+ **variables_to_states,
195
+ **initialize_variables_to_state(ports),
196
+ },
197
+ )
198
+ with self.scoped_visit(scope):
199
+ self.generic_visit(lambda_func)
200
+
201
+ def _handle_inputs(self, inputs: Mapping[str, HandleBinding]) -> None:
202
+ for handle_binding in inputs.values():
203
+ handle_wiring_state = self.current_scope.variable_states[
204
+ handle_binding.name
205
+ ]
206
+ if handle_wiring_state is not HandleState.INITIALIZED:
207
+ self._error_manager.add_error(
208
+ f"Trying to access handle {handle_binding.name!r} as input but it is in incorrect state"
209
+ )
210
+ 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[
219
+ handle_binding.name
220
+ ]
221
+
222
+ if handle_wiring_state is not HandleState.UNINITIALIZED:
223
+ self._error_manager.add_error(
224
+ f"Trying to access handle {handle_binding.name!r} as output but it is in incorrect state"
225
+ )
226
+ continue
227
+
228
+ self.current_scope.variable_states[handle_binding.name] = (
229
+ HandleState.INITIALIZED
230
+ )
231
+
232
+ def _handle_inouts(
233
+ self,
234
+ inouts: Mapping[
235
+ str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
236
+ ],
237
+ ) -> None:
238
+ sliced_handles = set()
239
+ whole_handles = set()
240
+
241
+ for handle_binding in inouts.values():
242
+ handle_wiring_state = self.current_scope.variable_states[
243
+ handle_binding.name
244
+ ]
245
+
246
+ if handle_wiring_state is not HandleState.INITIALIZED:
247
+ self._error_manager.add_error(
248
+ f"Trying to access handle {handle_binding.name!r} as inout but it is in incorrect state"
249
+ )
250
+
251
+ if isinstance(
252
+ handle_binding, (SlicedHandleBinding, SubscriptHandleBinding)
253
+ ):
254
+ sliced_handles.add(handle_binding.name)
255
+ else:
256
+ whole_handles.add(handle_binding.name)
257
+
258
+ for handle in sliced_handles & whole_handles:
259
+ self._error_manager.add_error(
260
+ f"Invalid use of inout handle {handle!r}, used both in slice or subscript and whole"
261
+ )
262
+
263
+
264
+ def resolve_function_calls(
265
+ root: Any,
266
+ quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
267
+ ) -> None:
268
+ StaticSemanticsVisitor(
269
+ {
270
+ **QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
271
+ **quantum_function_dict,
272
+ },
273
+ [],
274
+ ).visit(root)
275
+
276
+
277
+ def static_semantics_analysis_pass(
278
+ model: Model, error_type: Type[Exception] = ClassiqSemanticError
279
+ ) -> None:
280
+ StaticSemanticsVisitor(
281
+ {
282
+ **QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
283
+ **model.function_dict,
284
+ },
285
+ [const.name for const in model.constants],
286
+ ).visit(model)
287
+ ErrorManager().report_errors(error_type)
288
+
289
+
290
+ EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
291
+ PortDeclarationDirection.Output: HandleState.INITIALIZED,
292
+ PortDeclarationDirection.Inout: HandleState.INITIALIZED,
293
+ }
294
+
295
+
296
+ def initialize_variables_to_state(
297
+ port_declarations: List[PortDeclaration],
298
+ ) -> Dict[str, HandleState]:
299
+ variables_to_state: Dict[str, HandleState] = dict()
300
+
301
+ for port_decl in port_declarations:
302
+ variables_to_state[port_decl.name] = (
303
+ HandleState.INITIALIZED
304
+ if port_decl.direction.includes_port_direction(PortDirection.Input)
305
+ else HandleState.UNINITIALIZED
306
+ )
307
+
308
+ return variables_to_state
@@ -0,0 +1,149 @@
1
+ import re
2
+ from typing import Mapping, Set
3
+
4
+ from classiq.interface.generator.functions.port_declaration import (
5
+ PortDeclarationDirection,
6
+ )
7
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
8
+ from classiq.interface.model.quantum_function_declaration import (
9
+ QuantumFunctionDeclaration,
10
+ QuantumOperandDeclaration,
11
+ )
12
+ from classiq.interface.model.quantum_lambda_function import (
13
+ QuantumLambdaFunction,
14
+ QuantumOperand,
15
+ )
16
+
17
+ from classiq.exceptions import ClassiqError
18
+ from classiq.qmod.semantics.error_manager import ErrorManager
19
+
20
+
21
+ def validate_call_arguments(
22
+ fc: QuantumFunctionCall,
23
+ function_dict: Mapping[str, QuantumFunctionDeclaration],
24
+ ) -> None:
25
+ _check_params_against_declaration(
26
+ set(fc.params.keys()),
27
+ set(fc.func_decl.param_decls.keys()),
28
+ fc.func_decl.name,
29
+ )
30
+ _check_ports_against_declaration(fc, fc.func_decl)
31
+ _check_params_against_declaration(
32
+ set(fc.operands.keys()),
33
+ set(fc.func_decl.operand_declarations.keys()),
34
+ fc.func_name,
35
+ )
36
+ _check_operands_against_declaration(fc, fc.func_decl, function_dict)
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
+ )
65
+
66
+
67
+ def _check_operand_against_declaration(
68
+ call: QuantumFunctionCall,
69
+ operand_decl: QuantumOperandDeclaration,
70
+ operand_argument: QuantumOperand,
71
+ function_dict: Mapping[str, QuantumFunctionDeclaration],
72
+ in_list: bool = False,
73
+ ) -> None:
74
+ if isinstance(operand_argument, list):
75
+ if in_list:
76
+ ErrorManager().add_error(
77
+ f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not "
78
+ f"a valid operand. Nested operand lists are not permitted"
79
+ )
80
+ return
81
+ for arg in operand_argument:
82
+ _check_operand_against_declaration(
83
+ call, operand_decl, arg, function_dict, in_list=True
84
+ )
85
+ return
86
+ operand_arg_decl: QuantumFunctionDeclaration
87
+ if isinstance(operand_argument, str):
88
+ if operand_argument not in function_dict:
89
+ ErrorManager().add_error(
90
+ f"{operand_argument!r} argument to {call.func_decl.name!r} is not a "
91
+ f"registered function"
92
+ )
93
+ return
94
+ operand_arg_decl = function_dict[operand_argument]
95
+ elif isinstance(operand_argument, QuantumLambdaFunction):
96
+ if operand_argument.func_decl is None:
97
+ return
98
+ operand_arg_decl = operand_argument.func_decl
99
+ else:
100
+ raise ClassiqError(
101
+ f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not a "
102
+ f"valid operand"
103
+ )
104
+ num_arg_parameters = len(operand_arg_decl.get_positional_arg_decls())
105
+ num_decl_parameters = len(operand_decl.get_positional_arg_decls())
106
+ if num_arg_parameters != num_decl_parameters:
107
+ ErrorManager().add_error(
108
+ f"Signature of argument {operand_argument!r} to {call.func_decl.name!r} "
109
+ f"does not match the signature of parameter {operand_decl.name!r}. "
110
+ 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,
126
+ )
127
+
128
+
129
+ def _check_params_against_declaration(
130
+ call_params: Set[str],
131
+ param_decls: Set[str],
132
+ callee_name: str,
133
+ ) -> None:
134
+ unknown_params = call_params - param_decls
135
+ if any(re.match(r"arg\d+", param) for param in unknown_params):
136
+ error_msg = (
137
+ f"Unsupported passing of named function {callee_name!r} as an operand."
138
+ "\nSuggestion: replace the named function with lambda function."
139
+ )
140
+ else:
141
+ error_msg = f"Unknown parameters {unknown_params} in call to {callee_name!r}."
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
+ )
@@ -0,0 +1,21 @@
1
+ from typing import Sequence, Union
2
+
3
+ from classiq import EnumDeclaration, StructDeclaration
4
+ from classiq.qmod.semantics.error_manager import ErrorManager
5
+
6
+
7
+ def check_duplicate_types(
8
+ types: Sequence[Union[EnumDeclaration, StructDeclaration]]
9
+ ) -> None:
10
+ known_types = {
11
+ type_.name for type_ in EnumDeclaration.BUILTIN_ENUM_DECLARATIONS.values()
12
+ }
13
+ known_types |= {
14
+ type_.name for type_ in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.values()
15
+ }
16
+ for type_ in types:
17
+ if type_.name in known_types:
18
+ with ErrorManager().node_context(type_):
19
+ ErrorManager().add_error(f"Type {type_.name!r} already exists")
20
+ else:
21
+ known_types.add(type_.name)
classiq/qmod/symbolic.py CHANGED
@@ -25,12 +25,12 @@ from classiq.qmod.qmod_variable import QNum
25
25
  from classiq.qmod.symbolic_expr import SymbolicExpr
26
26
  from classiq.qmod.symbolic_type import SymbolicTypes
27
27
 
28
- pi = SymbolicExpr("pi")
29
- E = SymbolicExpr("E")
30
- I = SymbolicExpr("I") # noqa: E741
31
- GoldenRatio = SymbolicExpr("GoldenRatio")
32
- EulerGamma = SymbolicExpr("EulerGamma")
33
- Catalan = SymbolicExpr("Catalan")
28
+ pi = SymbolicExpr("pi", False)
29
+ E = SymbolicExpr("E", False)
30
+ I = SymbolicExpr("I", False) # noqa: E741
31
+ GoldenRatio = SymbolicExpr("GoldenRatio", False)
32
+ EulerGamma = SymbolicExpr("EulerGamma", False)
33
+ Catalan = SymbolicExpr("Catalan", False)
34
34
 
35
35
  T = TypeVar("T", bound=CParam)
36
36