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.
Files changed (112) hide show
  1. classiq/__init__.py +7 -1
  2. classiq/_internals/client.py +4 -7
  3. classiq/_internals/host_checker.py +34 -12
  4. classiq/_internals/jobs.py +2 -2
  5. classiq/applications/chemistry/chemistry_model_constructor.py +12 -6
  6. classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  8. classiq/applications/finance/finance_model_constructor.py +3 -2
  9. classiq/applications/grover/grover_model_constructor.py +7 -5
  10. classiq/applications/hamiltonian/__init__.py +0 -0
  11. classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
  12. classiq/applications/qnn/qlayer.py +1 -1
  13. classiq/exceptions.py +4 -0
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/ast_node.py +1 -18
  16. classiq/interface/backend/backend_preferences.py +15 -16
  17. classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
  18. classiq/interface/backend/pydantic_backend.py +0 -5
  19. classiq/interface/backend/quantum_backend_providers.py +3 -2
  20. classiq/interface/chemistry/operator.py +5 -1
  21. classiq/interface/debug_info/__init__.py +0 -0
  22. classiq/interface/debug_info/debug_info.py +32 -0
  23. classiq/interface/executor/execution_preferences.py +1 -45
  24. classiq/interface/executor/result.py +25 -12
  25. classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
  26. classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
  27. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
  28. classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
  29. classiq/interface/generator/application_apis/finance_declarations.py +36 -22
  30. classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
  31. classiq/interface/generator/arith/arithmetic_expression_abc.py +21 -1
  32. classiq/interface/generator/arith/binary_ops.py +5 -4
  33. classiq/interface/generator/arith/extremum_operations.py +43 -19
  34. classiq/interface/generator/constant.py +1 -1
  35. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
  36. classiq/interface/generator/expressions/expression_constants.py +3 -1
  37. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -66
  38. classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
  39. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  40. classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
  41. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
  42. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
  43. classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
  44. classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
  45. classiq/interface/generator/functions/classical_function_declaration.py +14 -6
  46. classiq/interface/generator/functions/classical_type.py +7 -76
  47. classiq/interface/generator/functions/concrete_types.py +55 -0
  48. classiq/interface/generator/functions/function_declaration.py +10 -10
  49. classiq/interface/generator/functions/type_name.py +104 -0
  50. classiq/interface/generator/generated_circuit_data.py +3 -3
  51. classiq/interface/generator/model/model.py +11 -0
  52. classiq/interface/generator/model/preferences/preferences.py +5 -0
  53. classiq/interface/generator/quantum_function_call.py +3 -0
  54. classiq/interface/generator/quantum_program.py +2 -2
  55. classiq/interface/generator/register_role.py +7 -1
  56. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
  57. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
  58. classiq/interface/generator/types/qstruct_declaration.py +17 -0
  59. classiq/interface/generator/types/struct_declaration.py +1 -1
  60. classiq/interface/helpers/validation_helpers.py +1 -17
  61. classiq/interface/ide/visual_model.py +9 -2
  62. classiq/interface/interface_version.py +1 -0
  63. classiq/interface/model/bind_operation.py +25 -5
  64. classiq/interface/model/classical_parameter_declaration.py +8 -5
  65. classiq/interface/model/control.py +5 -5
  66. classiq/interface/model/handle_binding.py +185 -12
  67. classiq/interface/model/inplace_binary_operation.py +16 -4
  68. classiq/interface/model/model.py +28 -5
  69. classiq/interface/model/native_function_definition.py +8 -4
  70. classiq/interface/model/parameter.py +14 -0
  71. classiq/interface/model/port_declaration.py +20 -2
  72. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +21 -6
  73. classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
  74. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
  75. classiq/interface/model/quantum_function_call.py +135 -192
  76. classiq/interface/model/quantum_function_declaration.py +147 -165
  77. classiq/interface/model/quantum_lambda_function.py +24 -6
  78. classiq/interface/model/quantum_statement.py +34 -8
  79. classiq/interface/model/quantum_type.py +61 -10
  80. classiq/interface/model/quantum_variable_declaration.py +1 -1
  81. classiq/interface/model/statement_block.py +2 -0
  82. classiq/interface/model/validation_handle.py +7 -0
  83. classiq/interface/server/global_versions.py +4 -4
  84. classiq/interface/server/routes.py +2 -0
  85. classiq/interface/source_reference.py +59 -0
  86. classiq/qmod/__init__.py +2 -3
  87. classiq/qmod/builtins/functions.py +39 -11
  88. classiq/qmod/builtins/operations.py +171 -40
  89. classiq/qmod/declaration_inferrer.py +99 -56
  90. classiq/qmod/expression_query.py +1 -1
  91. classiq/qmod/model_state_container.py +2 -0
  92. classiq/qmod/native/pretty_printer.py +71 -53
  93. classiq/qmod/pretty_print/pretty_printer.py +98 -52
  94. classiq/qmod/qfunc.py +11 -5
  95. classiq/qmod/qmod_parameter.py +1 -2
  96. classiq/qmod/qmod_variable.py +364 -172
  97. classiq/qmod/quantum_callable.py +3 -3
  98. classiq/qmod/quantum_expandable.py +119 -65
  99. classiq/qmod/quantum_function.py +15 -3
  100. classiq/qmod/semantics/annotation.py +12 -13
  101. classiq/qmod/semantics/error_manager.py +36 -10
  102. classiq/qmod/semantics/static_semantics_visitor.py +163 -75
  103. classiq/qmod/semantics/validation/func_call_validation.py +42 -96
  104. classiq/qmod/semantics/validation/handle_validation.py +85 -0
  105. classiq/qmod/semantics/validation/types_validation.py +108 -1
  106. classiq/qmod/type_attribute_remover.py +32 -0
  107. classiq/qmod/utilities.py +26 -5
  108. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/METADATA +3 -3
  109. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/RECORD +111 -99
  110. classiq/qmod/qmod_struct.py +0 -13
  111. /classiq/{interface/ide/show.py → show.py} +0 -0
  112. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/WHEEL +0 -0
@@ -12,13 +12,13 @@ from typing import ( # type: ignore[attr-defined]
12
12
 
13
13
  from typing_extensions import ParamSpec
14
14
 
15
- from classiq.interface.ast_node import SourceReference
16
15
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
17
16
  from classiq.interface.model.quantum_function_declaration import (
18
- QuantumFunctionDeclaration,
17
+ AnonQuantumFunctionDeclaration,
19
18
  )
20
19
  from classiq.interface.model.quantum_statement import QuantumStatement
21
20
  from classiq.interface.model.quantum_type import QuantumType
21
+ from classiq.interface.source_reference import SourceReference
22
22
 
23
23
  from classiq.qmod.qmod_parameter import CInt
24
24
  from classiq.qmod.utilities import get_source_ref
@@ -58,7 +58,7 @@ class QCallable(Generic[P], ABC):
58
58
 
59
59
  @property
60
60
  @abstractmethod
61
- def func_decl(self) -> QuantumFunctionDeclaration:
61
+ def func_decl(self) -> AnonQuantumFunctionDeclaration:
62
62
  raise NotImplementedError
63
63
 
64
64
  # Support comma-separated generic args in older Python versions
@@ -20,24 +20,22 @@ from typing import (
20
20
  from sympy import Basic
21
21
  from typing_extensions import Self
22
22
 
23
- from classiq.interface.ast_node import SourceReference
24
23
  from classiq.interface.generator.expressions.expression import Expression
25
- from classiq.interface.generator.functions.classical_type import (
26
- PythonClassicalTypes,
27
- )
24
+ from classiq.interface.generator.functions.concrete_types import PythonClassicalTypes
28
25
  from classiq.interface.model.classical_parameter_declaration import (
29
- ClassicalParameterDeclaration,
26
+ AnonClassicalParameterDeclaration,
30
27
  )
31
- from classiq.interface.model.port_declaration import PortDeclaration
28
+ from classiq.interface.model.port_declaration import AnonPortDeclaration
32
29
  from classiq.interface.model.quantum_function_call import (
33
30
  ArgValue,
34
31
  OperandIdentifier,
35
32
  QuantumFunctionCall,
36
33
  )
37
34
  from classiq.interface.model.quantum_function_declaration import (
38
- PositionalArg,
35
+ AnonPositionalArg,
36
+ AnonQuantumFunctionDeclaration,
37
+ AnonQuantumOperandDeclaration,
39
38
  QuantumFunctionDeclaration,
40
- QuantumOperandDeclaration,
41
39
  )
42
40
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
43
41
  from classiq.interface.model.quantum_statement import QuantumStatement
@@ -45,14 +43,20 @@ from classiq.interface.model.quantum_type import QuantumType
45
43
  from classiq.interface.model.variable_declaration_statement import (
46
44
  VariableDeclarationStatement,
47
45
  )
46
+ from classiq.interface.source_reference import SourceReference
48
47
 
49
48
  from classiq.exceptions import ClassiqValueError
50
49
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
51
50
  from classiq.qmod.qmod_constant import QConstant
52
51
  from classiq.qmod.qmod_parameter import CInt, CParam, CParamScalar, create_param
53
- from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
52
+ from classiq.qmod.qmod_variable import (
53
+ QVar,
54
+ create_qvar_for_port_decl,
55
+ set_symbolic_qvar_properties,
56
+ )
54
57
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
55
58
  from classiq.qmod.symbolic_expr import SymbolicExpr
59
+ from classiq.qmod.type_attribute_remover import decl_without_type_attributes
56
60
  from classiq.qmod.utilities import mangle_keyword, qmod_val_to_expr_str
57
61
 
58
62
  ArgType = Union[CParam, QVar, QCallable]
@@ -89,11 +93,11 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
89
93
 
90
94
  def expand(self) -> None:
91
95
  if self not in QExpandable.STACK:
92
- with self:
96
+ with self, set_symbolic_qvar_properties(True):
93
97
  self._py_callable(*self._get_positional_args())
94
98
 
95
- def infer_rename_params(self) -> Dict[str, str]:
96
- return {}
99
+ def infer_rename_params(self) -> Optional[List[str]]:
100
+ return None
97
101
 
98
102
  def add_local_handle(
99
103
  self,
@@ -112,60 +116,94 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
112
116
 
113
117
  def _get_positional_args(self) -> List[ArgType]:
114
118
  result: List[ArgType] = []
115
- for arg in self.func_decl.get_positional_arg_decls():
116
- rename_dict = self.infer_rename_params()
117
- actual_name = rename_dict.get(arg.name, arg.name)
118
- if isinstance(arg, ClassicalParameterDeclaration):
119
+ for idx, arg in enumerate(self.func_decl.positional_arg_declarations):
120
+ rename_params = self.infer_rename_params()
121
+ actual_name = (
122
+ rename_params[idx] if rename_params is not None else arg.get_name()
123
+ )
124
+ if isinstance(arg, AnonClassicalParameterDeclaration):
119
125
  result.append(
120
126
  create_param(actual_name, arg.classical_type, self._qmodule)
121
127
  )
122
- elif isinstance(arg, PortDeclaration):
128
+ elif isinstance(arg, AnonPortDeclaration):
123
129
  result.append(create_qvar_for_port_decl(arg, actual_name))
124
130
  else:
125
- assert isinstance(arg, QuantumOperandDeclaration)
126
- result.append(QTerminalCallable(arg))
131
+ assert isinstance(arg, AnonQuantumOperandDeclaration)
132
+ result.append(QTerminalCallable(arg, idx))
127
133
  return result
128
134
 
129
135
  def create_quantum_function_call(
130
136
  self, source_ref_: SourceReference, *args: Any, **kwargs: Any
131
137
  ) -> QuantumFunctionCall:
138
+ func_decl = self.func_decl
139
+ if not isinstance(func_decl, QuantumFunctionDeclaration):
140
+ raise NotImplementedError
132
141
  return _create_quantum_function_call(
133
- self.func_decl, None, source_ref_, *args, **kwargs
142
+ func_decl, None, source_ref_, *args, **kwargs
134
143
  )
135
144
 
136
145
 
137
146
  class QLambdaFunction(QExpandable):
138
- def __init__(self, decl: QuantumFunctionDeclaration, py_callable: Callable) -> None:
147
+ def __init__(
148
+ self, decl: AnonQuantumFunctionDeclaration, py_callable: Callable
149
+ ) -> None:
139
150
  py_callable.__annotations__.pop("return", None)
140
151
  super().__init__(py_callable)
141
152
  self._decl = decl
142
153
 
143
154
  @property
144
- def func_decl(self) -> QuantumFunctionDeclaration:
155
+ def func_decl(self) -> AnonQuantumFunctionDeclaration:
145
156
  return self._decl
146
157
 
147
- def infer_rename_params(self) -> Dict[str, str]:
148
- py_params = inspect.getfullargspec(self._py_callable)
149
- decl_params = self.func_decl.get_positional_arg_decls()
150
- return {
151
- decl_param.name: py_param
152
- for decl_param, py_param in zip(decl_params, py_params.args)
153
- if decl_param.name != py_param
154
- }
158
+ def infer_rename_params(self) -> List[str]:
159
+ return inspect.getfullargspec(self._py_callable).args
155
160
 
156
161
 
157
162
  class QTerminalCallable(QCallable):
163
+ @overload
158
164
  def __init__(
159
165
  self,
160
166
  decl: QuantumFunctionDeclaration,
167
+ param_idx: Optional[int] = None,
161
168
  index_: Optional[Union[int, CParamScalar]] = None,
162
169
  ) -> None:
163
- self._decl = decl
170
+ pass
171
+
172
+ @overload
173
+ def __init__(
174
+ self,
175
+ decl: AnonQuantumFunctionDeclaration,
176
+ param_idx: int,
177
+ index_: Optional[Union[int, CParamScalar]] = None,
178
+ ) -> None:
179
+ pass
180
+
181
+ def __init__(
182
+ self,
183
+ decl: AnonQuantumFunctionDeclaration,
184
+ param_idx: Optional[int] = None,
185
+ index_: Optional[Union[int, CParamScalar]] = None,
186
+ ) -> None:
187
+ self._decl = self._override_decl_name(decl, param_idx)
164
188
  self._index = index_
165
189
 
190
+ @staticmethod
191
+ def _override_decl_name(
192
+ decl: AnonQuantumFunctionDeclaration, param_idx: Optional[int]
193
+ ) -> QuantumFunctionDeclaration:
194
+ if (
195
+ not isinstance(QCallable.CURRENT_EXPANDABLE, QLambdaFunction)
196
+ or param_idx is None
197
+ ):
198
+ return decl.rename(decl.get_name())
199
+ rename_params = QCallable.CURRENT_EXPANDABLE.infer_rename_params()
200
+ return decl.rename(new_name=rename_params[param_idx])
201
+
166
202
  @property
167
203
  def is_list(self) -> bool:
168
- return isinstance(self._decl, QuantumOperandDeclaration) and self._decl.is_list
204
+ return (
205
+ isinstance(self._decl, AnonQuantumOperandDeclaration) and self._decl.is_list
206
+ )
169
207
 
170
208
  def __getitem__(self, key: Union[slice, int, CInt]) -> "QTerminalCallable":
171
209
  if not self.is_list:
@@ -174,7 +212,7 @@ class QTerminalCallable(QCallable):
174
212
  raise NotImplementedError("Operand lists don't support slicing")
175
213
  if isinstance(key, CParam) and not isinstance(key, CParamScalar):
176
214
  raise ClassiqValueError("Non-classical parameter for slicing")
177
- return QTerminalCallable(self._decl, key)
215
+ return QTerminalCallable(self._decl, index_=key)
178
216
 
179
217
  def __len__(self) -> int:
180
218
  raise ClassiqValueError(
@@ -212,43 +250,54 @@ class QTerminalCallable(QCallable):
212
250
 
213
251
  @overload
214
252
  def prepare_arg(
215
- arg_decl: PositionalArg, val: Union[QCallable, Callable[..., None]], func_name: str
253
+ arg_decl: AnonPositionalArg,
254
+ val: Union[QCallable, Callable[..., None]],
255
+ func_name: Optional[str],
256
+ param_name: str,
216
257
  ) -> QuantumLambdaFunction: ...
217
258
 
218
259
 
219
260
  @overload
220
- def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue: ...
261
+ def prepare_arg(
262
+ arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
263
+ ) -> ArgValue: ...
221
264
 
222
265
 
223
- def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue:
266
+ def prepare_arg(
267
+ arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
268
+ ) -> ArgValue:
224
269
  if isinstance(val, QConstant):
225
270
  val.add_to_model()
226
271
  return Expression(expr=str(val.name))
227
- if isinstance(arg_decl, ClassicalParameterDeclaration):
272
+ if isinstance(arg_decl, AnonClassicalParameterDeclaration):
228
273
  _validate_classical_arg(val, arg_decl, func_name)
229
274
  return Expression(expr=qmod_val_to_expr_str(val))
230
- elif isinstance(arg_decl, PortDeclaration):
275
+ elif isinstance(arg_decl, AnonPortDeclaration):
231
276
  if not isinstance(val, QVar):
277
+ func_name_message = (
278
+ "" if func_name is None else f" of function {func_name!r}"
279
+ )
232
280
  raise ClassiqValueError(
233
- f"Argument {str(val)!r} to parameter {arg_decl.name!r} of function "
234
- f"{func_name!r} has incompatible type; expected quantum variable"
281
+ f"Argument {str(val)!r} to parameter {param_name!r}{func_name_message} "
282
+ f"has incompatible type; expected quantum variable"
235
283
  )
236
284
  return val.get_handle_binding()
237
285
  else:
238
286
  if isinstance(val, list):
239
287
  if not all(isinstance(v, QCallable) or callable(v) for v in val):
240
288
  raise ClassiqValueError(
241
- f"Quantum operand {arg_decl.name!r} cannot be initialized with a "
289
+ f"Quantum operand {param_name!r} cannot be initialized with a "
242
290
  f"list of non-callables"
243
291
  )
244
292
  val = cast(List[Union[QCallable, Callable[[Any], None]]], val)
245
- return [prepare_arg(arg_decl, v, func_name) for v in val]
293
+ return [prepare_arg(arg_decl, v, func_name, param_name) for v in val]
246
294
 
247
295
  if not isinstance(val, QCallable):
248
- val = QLambdaFunction(arg_decl, val)
296
+ new_arg_decl = decl_without_type_attributes(arg_decl)
297
+ val = QLambdaFunction(new_arg_decl, val)
249
298
  val.expand()
250
299
  return QuantumLambdaFunction(
251
- rename_params=val.infer_rename_params(),
300
+ pos_rename_params=val.infer_rename_params(),
252
301
  body=val.body,
253
302
  )
254
303
 
@@ -258,7 +307,7 @@ def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue:
258
307
 
259
308
 
260
309
  def _validate_classical_arg(
261
- arg: Any, arg_decl: ClassicalParameterDeclaration, func_name: str
310
+ arg: Any, arg_decl: AnonClassicalParameterDeclaration, func_name: Optional[str]
262
311
  ) -> None:
263
312
  if (
264
313
  not isinstance(
@@ -268,15 +317,16 @@ def _validate_classical_arg(
268
317
  or isinstance(arg, SymbolicExpr)
269
318
  and arg.is_quantum
270
319
  ):
320
+ func_name_message = "" if func_name is None else f" of function {func_name!r}"
271
321
  raise ClassiqValueError(
272
- f"Argument {str(arg)!r} to parameter {arg_decl.name!r} of function "
273
- f"{func_name!r} has incompatible type; expected "
322
+ f"Argument {str(arg)!r} to parameter {arg_decl.name!r}{func_name_message} "
323
+ f"has incompatible type; expected "
274
324
  f"{arg_decl.classical_type.qmod_type.__name__}"
275
325
  )
276
326
 
277
327
 
278
328
  def _get_operand_hint_args(
279
- func: QuantumFunctionDeclaration, param: PositionalArg, param_value: str
329
+ func: AnonQuantumFunctionDeclaration, param: AnonPositionalArg, param_value: str
280
330
  ) -> str:
281
331
  return ", ".join(
282
332
  [
@@ -285,39 +335,43 @@ def _get_operand_hint_args(
285
335
  if decl.name == param.name
286
336
  else f"{decl.name}=..."
287
337
  )
288
- for decl in func.get_positional_arg_decls()
338
+ for decl in func.positional_arg_declarations
289
339
  ]
290
340
  )
291
341
 
292
342
 
293
- def _get_operand_hint(func: QuantumFunctionDeclaration, param: PositionalArg) -> str:
343
+ def _get_operand_hint(
344
+ func: AnonQuantumFunctionDeclaration, param: AnonPositionalArg
345
+ ) -> str:
294
346
  return (
295
- f"\nHint: To create an operand, do not call quantum gates directly "
296
- f"`{func.name}({_get_operand_hint_args(func, param, 'H(q)')})`. "
297
- f"Instead, use a lambda function "
298
- f"`{func.name}({_get_operand_hint_args(func, param, 'lambda: H(q)')})` "
299
- f"or a quantum function "
300
- f"`{func.name}({_get_operand_hint_args(func, param, 'my_func')})`"
347
+ f"\nHint: To call a function under {func.name!r} use a lambda function as in "
348
+ f"'{func.name}({_get_operand_hint_args(func, param, 'lambda: f(q)')})' "
349
+ f"or pass the quantum function directly as in "
350
+ f"'{func.name}({_get_operand_hint_args(func, param, 'f')})'."
301
351
  )
302
352
 
303
353
 
304
354
  def _prepare_args(
305
- decl: QuantumFunctionDeclaration, arg_list: List[Any], kwargs: Dict[str, Any]
355
+ decl: AnonQuantumFunctionDeclaration, arg_list: List[Any], kwargs: Dict[str, Any]
306
356
  ) -> List[ArgValue]:
307
357
  result = []
308
- for arg_decl in decl.get_positional_arg_decls():
358
+ for idx, arg_decl in enumerate(decl.positional_arg_declarations):
359
+ arg = None
309
360
  if arg_list:
310
361
  arg = arg_list.pop(0)
311
- else:
362
+ elif arg_decl.name is not None:
312
363
  arg = kwargs.pop(mangle_keyword(arg_decl.name), None)
313
364
  if arg is None:
314
- error_message = (
315
- f"{decl.name!r} is missing required argument for {arg_decl.name!r}"
316
- )
317
- if isinstance(arg_decl, QuantumOperandDeclaration):
365
+ if arg_decl.name is not None:
366
+ param_name = repr(arg_decl.name)
367
+ else:
368
+ param_name = f"#{idx + 1}"
369
+ error_message = f"Missing required argument for parameter {param_name}"
370
+ if isinstance(arg_decl, AnonQuantumOperandDeclaration):
318
371
  error_message += _get_operand_hint(decl, arg_decl)
319
372
  raise ClassiqValueError(error_message)
320
- result.append(prepare_arg(arg_decl, arg, decl.name))
373
+ param_name = arg_decl.name if arg_decl.name is not None else f"#{idx + 1}"
374
+ result.append(prepare_arg(arg_decl, arg, decl.name, param_name))
321
375
 
322
376
  return result
323
377
 
@@ -329,7 +383,7 @@ def _create_quantum_function_call(
329
383
  *args: Any,
330
384
  **kwargs: Any,
331
385
  ) -> QuantumFunctionCall:
332
- arg_decls = decl_.get_positional_arg_decls()
386
+ arg_decls = decl_.positional_arg_declarations
333
387
  arg_list = list(args)
334
388
  prepared_args = _prepare_args(decl_, arg_list, kwargs)
335
389
 
@@ -11,12 +11,14 @@ from classiq.interface.generator.model.preferences.preferences import Preference
11
11
  from classiq.interface.model.model import Model, SerializedModel
12
12
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
13
13
  from classiq.interface.model.quantum_function_declaration import (
14
+ NamedParamsQuantumFunctionDeclaration,
14
15
  QuantumFunctionDeclaration,
15
16
  )
16
17
 
17
18
  from classiq.exceptions import ClassiqError
18
19
  from classiq.qmod.classical_function import CFunc
19
20
  from classiq.qmod.declaration_inferrer import infer_func_decl
21
+ from classiq.qmod.model_state_container import QMODULE
20
22
  from classiq.qmod.qmod_constant import QConstant
21
23
  from classiq.qmod.qmod_parameter import CArray, CParam
22
24
  from classiq.qmod.qmod_variable import QVar
@@ -25,7 +27,7 @@ from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
25
27
  from classiq.qmod.utilities import mangle_keyword, unmangle_keyword
26
28
 
27
29
 
28
- def _lookup_qfunc(name: str) -> Optional[QuantumFunctionDeclaration]:
30
+ def _lookup_qfunc(name: str) -> Optional[NamedParamsQuantumFunctionDeclaration]:
29
31
  # FIXME: to be generalized to existing user-defined functions
30
32
  return QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS.get(name)
31
33
 
@@ -55,7 +57,7 @@ class QFunc(QExpandable):
55
57
  functools.update_wrapper(self, py_callable)
56
58
 
57
59
  @property
58
- def func_decl(self) -> QuantumFunctionDeclaration:
60
+ def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
59
61
  return self._qmodule.native_defs.get(
60
62
  self._py_callable.__name__,
61
63
  infer_func_decl(self._py_callable, qmodule=self._qmodule),
@@ -74,6 +76,7 @@ class QFunc(QExpandable):
74
76
  ) -> Model:
75
77
  self._qmodule.enum_decls = dict()
76
78
  self._qmodule.type_decls = dict()
79
+ self._qmodule.qstruct_decls = dict()
77
80
  self._qmodule.native_defs = dict()
78
81
  self._qmodule.constants = dict()
79
82
  QConstant.set_current_model(self._qmodule)
@@ -93,6 +96,7 @@ class QFunc(QExpandable):
93
96
  functions=list(self._qmodule.native_defs.values()),
94
97
  enums=list(self._qmodule.enum_decls.values()),
95
98
  types=list(self._qmodule.type_decls.values()),
99
+ qstructs=list(self._qmodule.qstruct_decls.values()),
96
100
  **{key: value for key, value in model_extra_settings if value},
97
101
  )
98
102
 
@@ -138,7 +142,7 @@ class ExternalQFunc(QTerminalCallable):
138
142
 
139
143
  py_callable.__annotations__.pop("return", None)
140
144
  if py_callable.__annotations__.keys() != {
141
- mangle_keyword(arg.name) for arg in decl.get_positional_arg_decls()
145
+ mangle_keyword(arg.name) for arg in decl.positional_arg_declarations
142
146
  }:
143
147
  raise ClassiqError(
144
148
  f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
@@ -147,6 +151,14 @@ class ExternalQFunc(QTerminalCallable):
147
151
  functools.update_wrapper(self, py_callable)
148
152
 
149
153
 
154
+ class GenerativeQFunc(QTerminalCallable):
155
+ func_decl: NamedParamsQuantumFunctionDeclaration
156
+
157
+ def __init__(self, py_callable: Callable) -> None:
158
+ super().__init__(infer_func_decl(py_callable, QMODULE))
159
+ self.py_callable = py_callable
160
+
161
+
150
162
  ILLEGAL_PARAM_ERROR = "Unsupported type hint '{annotation}' for argument '{name}'."
151
163
 
152
164
 
@@ -1,13 +1,12 @@
1
- from typing import List, Mapping
1
+ from typing import Mapping
2
2
 
3
3
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
4
  from classiq.interface.model.quantum_function_declaration import (
5
+ AnonQuantumOperandDeclaration,
5
6
  QuantumFunctionDeclaration,
6
7
  )
7
8
  from classiq.interface.model.quantum_lambda_function import (
8
- QuantumCallable,
9
9
  QuantumLambdaFunction,
10
- QuantumOperand,
11
10
  )
12
11
 
13
12
  from classiq.exceptions import ClassiqError
@@ -25,14 +24,14 @@ def annotate_function_call_decl(
25
24
  )
26
25
  fc.set_func_decl(func_decl)
27
26
 
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):
27
+ for arg, param in zip(fc.positional_args, fc.func_decl.positional_arg_declarations):
28
+ if not isinstance(param, AnonQuantumOperandDeclaration):
29
+ continue
30
+ args: list
31
+ if isinstance(arg, list):
32
+ args = arg
33
+ else:
34
+ args = [arg]
35
+ for qlambda in args:
31
36
  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]
37
+ qlambda.set_op_decl(param)
@@ -1,7 +1,8 @@
1
1
  from contextlib import contextmanager
2
- from typing import Iterator, List, Type
2
+ from typing import Iterator, List, Optional, Type
3
3
 
4
4
  from classiq.interface.ast_node import ASTNode
5
+ from classiq.interface.source_reference import SourceReferencedError
5
6
 
6
7
 
7
8
  class ErrorManager:
@@ -14,19 +15,28 @@ class ErrorManager:
14
15
  if hasattr(self, "_instantiated"):
15
16
  return
16
17
  self._instantiated = True
17
- self._errors: List[str] = []
18
+ self._errors: List[SourceReferencedError] = []
18
19
  self._current_nodes_stack: List[ASTNode] = []
20
+ self._call_stack: List[str] = []
21
+
22
+ @property
23
+ def annotated_errors(self) -> List[str]:
24
+ return [str(error) for error in self._errors]
19
25
 
20
26
  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
27
+ self._errors.append(
28
+ SourceReferencedError(
29
+ error=error,
30
+ source_ref=(
31
+ self._current_nodes_stack[-1].source_ref
32
+ if self._current_nodes_stack
33
+ else None
34
+ ),
35
+ function=self.current_function,
36
+ )
26
37
  )
27
- self._errors.append(source_referenced_error)
28
38
 
29
- def get_errors(self) -> List[str]:
39
+ def get_errors(self) -> List[SourceReferencedError]:
30
40
  return self._errors
31
41
 
32
42
  def clear(self) -> None:
@@ -38,12 +48,28 @@ class ErrorManager:
38
48
 
39
49
  def report_errors(self, error_type: Type[Exception]) -> None:
40
50
  if self.has_errors():
41
- errors = self._errors
51
+ errors = self.annotated_errors
42
52
  self.clear()
43
53
  raise error_type("\n\t" + "\n\t".join(errors))
44
54
 
55
+ @property
56
+ def current_function(self) -> Optional[str]:
57
+ return self._call_stack[-1] if self._call_stack else None
58
+
45
59
  @contextmanager
46
60
  def node_context(self, node: ASTNode) -> Iterator[None]:
47
61
  self._current_nodes_stack.append(node)
48
62
  yield
49
63
  self._current_nodes_stack.pop()
64
+
65
+ @contextmanager
66
+ def call(self, func_name: str) -> Iterator[None]:
67
+ self._call_stack.append(func_name)
68
+ yield
69
+ self._call_stack.pop()
70
+
71
+
72
+ def append_error(node: ASTNode, message: str) -> None:
73
+ instance = ErrorManager()
74
+ with instance.node_context(node):
75
+ instance.add_error(message)