classiq 0.43.3__py3-none-any.whl → 0.45.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.
Files changed (220) hide show
  1. classiq/__init__.py +8 -3
  2. classiq/_internals/api_wrapper.py +2 -2
  3. classiq/_internals/authentication/auth0.py +1 -1
  4. classiq/_internals/authentication/device.py +5 -1
  5. classiq/_internals/authentication/token_manager.py +5 -4
  6. classiq/_internals/client.py +5 -8
  7. classiq/_internals/config.py +1 -2
  8. classiq/_internals/host_checker.py +34 -13
  9. classiq/_internals/jobs.py +3 -3
  10. classiq/analyzer/analyzer.py +1 -1
  11. classiq/analyzer/analyzer_utilities.py +1 -1
  12. classiq/analyzer/rb.py +1 -1
  13. classiq/applications/chemistry/chemistry_model_constructor.py +13 -7
  14. classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
  15. classiq/applications/combinatorial_helpers/arithmetic/isolation.py +1 -1
  16. classiq/applications/combinatorial_helpers/encoding_mapping.py +1 -1
  17. classiq/applications/combinatorial_helpers/encoding_utils.py +2 -1
  18. classiq/applications/combinatorial_helpers/optimization_model.py +1 -1
  19. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
  20. classiq/applications/combinatorial_helpers/pyomo_utils.py +1 -2
  21. classiq/applications/combinatorial_helpers/transformations/encoding.py +1 -1
  22. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +5 -4
  23. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +1 -1
  24. classiq/applications/combinatorial_helpers/transformations/sign_seperation.py +1 -1
  25. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  26. classiq/applications/finance/finance_model_constructor.py +4 -3
  27. classiq/applications/grover/grover_model_constructor.py +7 -5
  28. classiq/applications/hamiltonian/__init__.py +0 -0
  29. classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
  30. classiq/applications/qnn/circuit_utils.py +1 -1
  31. classiq/applications/qnn/datasets/dataset_base_classes.py +2 -1
  32. classiq/applications/qnn/datasets/dataset_not.py +2 -1
  33. classiq/applications/qnn/qlayer.py +3 -2
  34. classiq/applications/qnn/torch_utils.py +2 -1
  35. classiq/applications/qsvm/qsvm_model_constructor.py +1 -1
  36. classiq/execution/execution_session.py +1 -1
  37. classiq/execution/jobs.py +5 -2
  38. classiq/interface/_version.py +1 -1
  39. classiq/interface/analyzer/cytoscape_graph.py +1 -2
  40. classiq/interface/analyzer/result.py +2 -3
  41. classiq/interface/ast_node.py +1 -18
  42. classiq/interface/backend/backend_preferences.py +11 -18
  43. classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
  44. classiq/interface/backend/pydantic_backend.py +0 -5
  45. classiq/interface/backend/quantum_backend_providers.py +4 -3
  46. classiq/interface/chemistry/fermionic_operator.py +1 -2
  47. classiq/interface/chemistry/ground_state_problem.py +2 -3
  48. classiq/interface/chemistry/molecule.py +1 -2
  49. classiq/interface/chemistry/operator.py +8 -10
  50. classiq/interface/combinatorial_optimization/encoding_types.py +1 -1
  51. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +1 -1
  52. classiq/interface/combinatorial_optimization/solver_types.py +1 -1
  53. classiq/interface/debug_info/__init__.py +0 -0
  54. classiq/interface/debug_info/debug_info.py +32 -0
  55. classiq/{exceptions.py → interface/exceptions.py} +4 -0
  56. classiq/interface/executor/aws_execution_cost.py +2 -2
  57. classiq/interface/executor/execution_preferences.py +2 -47
  58. classiq/interface/executor/execution_result.py +1 -2
  59. classiq/interface/executor/optimizer_preferences.py +2 -3
  60. classiq/interface/executor/quantum_code.py +1 -2
  61. classiq/interface/executor/quantum_instruction_set.py +2 -2
  62. classiq/interface/executor/register_initialization.py +1 -2
  63. classiq/interface/executor/result.py +29 -14
  64. classiq/interface/finance/function_input.py +6 -11
  65. classiq/interface/generator/amplitude_loading.py +2 -3
  66. classiq/interface/generator/ansatz_library.py +1 -1
  67. classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
  68. classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
  69. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
  70. classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
  71. classiq/interface/generator/application_apis/finance_declarations.py +37 -44
  72. classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
  73. classiq/interface/generator/arith/arithmetic.py +10 -8
  74. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +1 -2
  75. classiq/interface/generator/arith/arithmetic_expression_abc.py +22 -3
  76. classiq/interface/generator/arith/arithmetic_expression_parser.py +3 -4
  77. classiq/interface/generator/arith/arithmetic_expression_validator.py +1 -2
  78. classiq/interface/generator/arith/arithmetic_param_getters.py +1 -2
  79. classiq/interface/generator/arith/arithmetic_result_builder.py +15 -11
  80. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  81. classiq/interface/generator/arith/binary_ops.py +7 -7
  82. classiq/interface/generator/arith/endianness.py +1 -1
  83. classiq/interface/generator/arith/extremum_operations.py +44 -21
  84. classiq/interface/generator/arith/logical_ops.py +1 -2
  85. classiq/interface/generator/arith/register_user_input.py +1 -2
  86. classiq/interface/generator/arith/unary_ops.py +1 -2
  87. classiq/interface/generator/arith/uncomputation_methods.py +1 -1
  88. classiq/interface/generator/chemistry_function_params.py +1 -2
  89. classiq/interface/generator/circuit_code/circuit_code.py +1 -2
  90. classiq/interface/generator/circuit_code/types_and_constants.py +1 -2
  91. classiq/interface/generator/commuting_pauli_exponentiation.py +1 -2
  92. classiq/interface/generator/constant.py +1 -1
  93. classiq/interface/generator/control_state.py +1 -2
  94. classiq/interface/generator/custom_ansatz.py +1 -2
  95. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
  96. classiq/interface/generator/expressions/enums/finance_functions.py +4 -5
  97. classiq/interface/generator/expressions/evaluated_expression.py +1 -2
  98. classiq/interface/generator/expressions/expression.py +1 -2
  99. classiq/interface/generator/expressions/expression_constants.py +3 -1
  100. classiq/interface/generator/expressions/non_symbolic_expr.py +1 -1
  101. classiq/interface/generator/expressions/qmod_qarray_proxy.py +53 -70
  102. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +2 -7
  103. classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
  104. classiq/interface/generator/expressions/qmod_sized_proxy.py +1 -1
  105. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  106. classiq/interface/generator/function_params.py +2 -3
  107. classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
  108. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
  109. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
  110. classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
  111. classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
  112. classiq/interface/generator/functions/classical_function_declaration.py +14 -6
  113. classiq/interface/generator/functions/classical_type.py +7 -114
  114. classiq/interface/generator/functions/concrete_types.py +55 -0
  115. classiq/interface/generator/functions/function_declaration.py +10 -10
  116. classiq/interface/generator/functions/port_declaration.py +1 -2
  117. classiq/interface/generator/functions/type_name.py +80 -0
  118. classiq/interface/generator/generated_circuit_data.py +3 -3
  119. classiq/interface/generator/grover_diffuser.py +1 -2
  120. classiq/interface/generator/grover_operator.py +1 -2
  121. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +1 -2
  122. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +1 -2
  123. classiq/interface/generator/hardware/hardware_data.py +1 -2
  124. classiq/interface/generator/hardware_efficient_ansatz.py +2 -3
  125. classiq/interface/generator/hartree_fock.py +1 -2
  126. classiq/interface/generator/linear_pauli_rotations.py +1 -2
  127. classiq/interface/generator/mcmt_method.py +1 -1
  128. classiq/interface/generator/mcu.py +1 -2
  129. classiq/interface/generator/mcx.py +1 -2
  130. classiq/interface/generator/model/constraints.py +2 -3
  131. classiq/interface/generator/model/model.py +12 -2
  132. classiq/interface/generator/model/preferences/preferences.py +7 -3
  133. classiq/interface/generator/model/quantum_register.py +1 -2
  134. classiq/interface/generator/oracles/arithmetic_oracle.py +1 -2
  135. classiq/interface/generator/oracles/custom_oracle.py +1 -2
  136. classiq/interface/generator/oracles/oracle_abc.py +1 -2
  137. classiq/interface/generator/partitioned_register.py +1 -2
  138. classiq/interface/generator/piecewise_linear_amplitude_loading.py +1 -2
  139. classiq/interface/generator/preferences/optimization.py +1 -2
  140. classiq/interface/generator/qpe.py +1 -2
  141. classiq/interface/generator/qsvm.py +2 -3
  142. classiq/interface/generator/quantum_function_call.py +4 -2
  143. classiq/interface/generator/quantum_program.py +6 -7
  144. classiq/interface/generator/range_types.py +1 -1
  145. classiq/interface/generator/register_role.py +8 -2
  146. classiq/interface/generator/slice_parsing_utils.py +1 -2
  147. classiq/interface/generator/standard_gates/controlled_standard_gates.py +1 -2
  148. classiq/interface/generator/state_preparation/metrics.py +2 -3
  149. classiq/interface/generator/state_preparation/state_preparation.py +1 -2
  150. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
  151. classiq/interface/generator/transpiler_basis_gates.py +1 -1
  152. classiq/interface/generator/types/builtin_enum_declarations.py +38 -45
  153. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
  154. classiq/interface/generator/types/enum_declaration.py +1 -2
  155. classiq/interface/generator/types/qstruct_declaration.py +17 -0
  156. classiq/interface/generator/types/struct_declaration.py +2 -3
  157. classiq/interface/generator/ucc.py +1 -2
  158. classiq/interface/generator/unitary_gate.py +1 -2
  159. classiq/interface/generator/validations/flow_graph.py +1 -2
  160. classiq/interface/generator/validations/validator_functions.py +1 -2
  161. classiq/interface/hardware.py +1 -1
  162. classiq/interface/helpers/validation_helpers.py +2 -19
  163. classiq/interface/ide/visual_model.py +10 -4
  164. classiq/interface/interface_version.py +1 -0
  165. classiq/interface/jobs.py +2 -3
  166. classiq/interface/model/bind_operation.py +26 -7
  167. classiq/interface/model/classical_parameter_declaration.py +8 -5
  168. classiq/interface/model/control.py +5 -5
  169. classiq/interface/model/handle_binding.py +185 -12
  170. classiq/interface/model/inplace_binary_operation.py +17 -6
  171. classiq/interface/model/model.py +29 -7
  172. classiq/interface/model/native_function_definition.py +8 -4
  173. classiq/interface/model/parameter.py +13 -0
  174. classiq/interface/model/port_declaration.py +21 -4
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +22 -8
  176. classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
  177. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
  178. classiq/interface/model/quantum_function_call.py +136 -194
  179. classiq/interface/model/quantum_function_declaration.py +147 -165
  180. classiq/interface/model/quantum_lambda_function.py +23 -6
  181. classiq/interface/model/quantum_statement.py +34 -8
  182. classiq/interface/model/quantum_type.py +41 -11
  183. classiq/interface/model/quantum_variable_declaration.py +1 -1
  184. classiq/interface/model/statement_block.py +2 -0
  185. classiq/interface/model/validation_handle.py +8 -2
  186. classiq/interface/server/global_versions.py +4 -4
  187. classiq/interface/server/routes.py +2 -0
  188. classiq/interface/source_reference.py +59 -0
  189. classiq/qmod/__init__.py +2 -3
  190. classiq/qmod/builtins/classical_execution_primitives.py +1 -1
  191. classiq/qmod/builtins/functions.py +39 -11
  192. classiq/qmod/builtins/operations.py +172 -41
  193. classiq/qmod/classical_function.py +1 -1
  194. classiq/qmod/declaration_inferrer.py +102 -57
  195. classiq/qmod/expression_query.py +1 -1
  196. classiq/qmod/model_state_container.py +2 -0
  197. classiq/qmod/native/pretty_printer.py +71 -53
  198. classiq/qmod/pretty_print/pretty_printer.py +98 -52
  199. classiq/qmod/qfunc.py +11 -5
  200. classiq/qmod/qmod_constant.py +1 -1
  201. classiq/qmod/qmod_parameter.py +27 -4
  202. classiq/qmod/qmod_variable.py +405 -174
  203. classiq/qmod/quantum_callable.py +3 -3
  204. classiq/qmod/quantum_expandable.py +128 -68
  205. classiq/qmod/quantum_function.py +24 -5
  206. classiq/qmod/semantics/annotation.py +13 -15
  207. classiq/qmod/semantics/error_manager.py +36 -10
  208. classiq/qmod/semantics/static_semantics_visitor.py +164 -76
  209. classiq/qmod/semantics/validation/func_call_validation.py +43 -97
  210. classiq/qmod/semantics/validation/handle_validation.py +85 -0
  211. classiq/qmod/semantics/validation/types_validation.py +108 -1
  212. classiq/qmod/symbolic.py +2 -1
  213. classiq/qmod/type_attribute_remover.py +32 -0
  214. classiq/qmod/utilities.py +26 -5
  215. classiq/{interface/ide/show.py → show.py} +1 -1
  216. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/METADATA +3 -3
  217. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/RECORD +219 -207
  218. classiq/qmod/qmod_struct.py +0 -13
  219. /classiq/{_internals → interface}/enum_utils.py +0 -0
  220. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/WHEEL +0 -0
@@ -1,41 +1,72 @@
1
1
  from contextlib import contextmanager
2
- from typing import Any, Dict, Iterator, List, Mapping, Type, Union
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
 
15
+ from classiq.interface.exceptions import ClassiqSemanticError
4
16
  from classiq.interface.generator.function_params import PortDirection
17
+ from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
5
18
  from classiq.interface.generator.functions.port_declaration import (
6
19
  PortDeclarationDirection,
7
20
  )
8
21
  from classiq.interface.generator.visitor import Visitor
9
22
  from classiq.interface.model.handle_binding import (
23
+ FieldHandleBinding,
10
24
  HandleBinding,
11
25
  SlicedHandleBinding,
12
26
  SubscriptHandleBinding,
13
27
  )
28
+ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
14
29
  from classiq.interface.model.model import Model
15
30
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
16
31
  from classiq.interface.model.port_declaration import PortDeclaration
17
32
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
18
33
  from classiq.interface.model.quantum_function_declaration import (
34
+ AnonQuantumOperandDeclaration,
19
35
  QuantumFunctionDeclaration,
20
36
  QuantumOperandDeclaration,
21
37
  )
22
38
  from classiq.interface.model.quantum_lambda_function import (
23
39
  QuantumLambdaFunction,
24
40
  )
25
- from classiq.interface.model.quantum_statement import QuantumOperation
41
+ from classiq.interface.model.quantum_statement import HandleMetadata, QuantumOperation
26
42
  from classiq.interface.model.validation_handle import HandleState
27
43
  from classiq.interface.model.variable_declaration_statement import (
28
44
  VariableDeclarationStatement,
29
45
  )
30
46
  from classiq.interface.model.within_apply_operation import WithinApply
31
47
 
32
- from classiq.exceptions import ClassiqSemanticError
48
+ from classiq import AnonClassicalParameterDeclaration
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.types_validation import check_duplicate_types
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: Dict[str, QuantumFunctionDeclaration],
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.param_decls.keys()) + self._constants,
86
- operands=dict(func_def.operand_declarations),
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
- list(func_def.port_declarations.values())
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.values():
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
- self._handle_inputs(op.wiring_inputs)
151
- self._handle_outputs(op.wiring_outputs)
152
- self._handle_inouts(op.wiring_inouts)
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
- 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
- )
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
- **variables_to_states,
195
- **initialize_variables_to_state(ports),
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 _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:
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"Trying to access handle {handle_binding.name!r} as input but it is in incorrect state"
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
- if handle_wiring_state is not HandleState.UNINITIALIZED:
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"Trying to access handle {handle_binding.name!r} as output but it is in incorrect state"
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 _handle_inouts(
233
- self,
234
- inouts: Mapping[
235
- str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
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 handle_binding in inouts.values():
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"Trying to access handle {handle_binding.name!r} as inout but it is in incorrect state"
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
- ErrorManager().report_errors(error_type)
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: List[PortDeclaration],
385
+ port_declarations: Sequence[PortDeclaration],
298
386
  ) -> Dict[str, HandleState]:
299
387
  variables_to_state: Dict[str, HandleState] = dict()
300
388
 
@@ -1,20 +1,19 @@
1
- import re
2
- from typing import Mapping, Set
1
+ from typing import List, Mapping
3
2
 
4
- from classiq.interface.generator.functions.port_declaration import (
5
- PortDeclarationDirection,
6
- )
3
+ from classiq.interface.exceptions import ClassiqError
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.model.handle_binding import HandleBinding
7
6
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
8
7
  from classiq.interface.model.quantum_function_declaration import (
8
+ AnonQuantumFunctionDeclaration,
9
+ AnonQuantumOperandDeclaration,
9
10
  QuantumFunctionDeclaration,
10
- QuantumOperandDeclaration,
11
11
  )
12
12
  from classiq.interface.model.quantum_lambda_function import (
13
13
  QuantumLambdaFunction,
14
14
  QuantumOperand,
15
15
  )
16
16
 
17
- from classiq.exceptions import ClassiqError
18
17
  from classiq.qmod.semantics.error_manager import ErrorManager
19
18
 
20
19
 
@@ -22,128 +21,75 @@ def validate_call_arguments(
22
21
  fc: QuantumFunctionCall,
23
22
  function_dict: Mapping[str, QuantumFunctionDeclaration],
24
23
  ) -> 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
- )
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
- call: QuantumFunctionCall,
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 {call.func_decl.name!r} is not "
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
- call, operand_decl, arg, function_dict, in_list=True
55
+ operand_decl, arg, function_dict, func_name, in_list=True
84
56
  )
85
57
  return
86
- operand_arg_decl: QuantumFunctionDeclaration
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 {call.func_decl.name!r} is not a "
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 {call.func_decl.name!r} is not a "
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.get_positional_arg_decls())
105
- num_decl_parameters = len(operand_decl.get_positional_arg_decls())
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 {call.func_decl.name!r} "
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 _check_params_against_declaration(
130
- call_params: Set[str],
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
- 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
- )
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