classiq 0.39.0__py3-none-any.whl → 0.41.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 (100) hide show
  1. classiq/__init__.py +5 -2
  2. classiq/_internals/api_wrapper.py +3 -21
  3. classiq/applications/chemistry/chemistry_model_constructor.py +87 -101
  4. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +7 -26
  5. classiq/applications/combinatorial_helpers/optimization_model.py +7 -6
  6. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -55
  7. classiq/applications/combinatorial_optimization/__init__.py +4 -0
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
  9. classiq/applications/finance/finance_model_constructor.py +23 -26
  10. classiq/applications/grover/grover_model_constructor.py +37 -38
  11. classiq/applications/qsvm/qsvm.py +1 -2
  12. classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
  13. classiq/execution/__init__.py +4 -0
  14. classiq/execution/execution_session.py +151 -0
  15. classiq/execution/qnn.py +80 -0
  16. classiq/executor.py +2 -109
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/analyzer/analysis_params.py +11 -0
  19. classiq/interface/applications/qsvm.py +0 -8
  20. classiq/interface/ast_node.py +12 -2
  21. classiq/interface/backend/backend_preferences.py +30 -6
  22. classiq/interface/backend/quantum_backend_providers.py +11 -11
  23. classiq/interface/executor/execution_preferences.py +7 -67
  24. classiq/interface/executor/execution_result.py +22 -1
  25. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -4
  26. classiq/interface/generator/application_apis/finance_declarations.py +1 -1
  27. classiq/interface/generator/arith/binary_ops.py +88 -25
  28. classiq/interface/generator/arith/unary_ops.py +28 -19
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
  30. classiq/interface/generator/expressions/enums/__init__.py +10 -0
  31. classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
  32. classiq/interface/generator/expressions/expression.py +9 -2
  33. classiq/interface/generator/expressions/qmod_qarray_proxy.py +89 -0
  34. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +20 -0
  35. classiq/interface/generator/expressions/qmod_sized_proxy.py +22 -0
  36. classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
  37. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +8 -6
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +10 -4
  39. classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
  40. classiq/interface/generator/functions/builtins/open_lib_functions.py +1627 -271
  41. classiq/interface/generator/functions/classical_type.py +27 -17
  42. classiq/interface/generator/model/preferences/preferences.py +4 -2
  43. classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
  44. classiq/interface/model/bind_operation.py +3 -1
  45. classiq/interface/model/call_synthesis_data.py +2 -13
  46. classiq/interface/model/classical_if.py +3 -1
  47. classiq/interface/model/classical_parameter_declaration.py +13 -0
  48. classiq/interface/model/control.py +6 -8
  49. classiq/interface/model/inplace_binary_operation.py +3 -1
  50. classiq/interface/model/invert.py +3 -1
  51. classiq/interface/model/port_declaration.py +8 -1
  52. classiq/interface/model/power.py +3 -1
  53. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
  54. classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
  55. classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
  56. classiq/interface/model/quantum_function_call.py +4 -10
  57. classiq/interface/model/quantum_function_declaration.py +26 -4
  58. classiq/interface/model/quantum_lambda_function.py +1 -20
  59. classiq/interface/model/quantum_statement.py +9 -2
  60. classiq/interface/model/quantum_type.py +6 -5
  61. classiq/interface/model/repeat.py +3 -1
  62. classiq/interface/model/resolvers/function_call_resolver.py +0 -5
  63. classiq/interface/model/statement_block.py +19 -16
  64. classiq/interface/model/validations/handles_validator.py +8 -2
  65. classiq/interface/model/variable_declaration_statement.py +3 -1
  66. classiq/interface/model/within_apply_operation.py +3 -1
  67. classiq/interface/server/routes.py +0 -5
  68. classiq/qmod/__init__.py +5 -2
  69. classiq/qmod/builtins/classical_execution_primitives.py +22 -2
  70. classiq/qmod/builtins/classical_functions.py +30 -35
  71. classiq/qmod/builtins/functions.py +263 -153
  72. classiq/qmod/builtins/operations.py +50 -26
  73. classiq/qmod/builtins/structs.py +50 -48
  74. classiq/qmod/declaration_inferrer.py +32 -27
  75. classiq/qmod/native/__init__.py +9 -0
  76. classiq/qmod/native/expression_to_qmod.py +8 -4
  77. classiq/qmod/native/pretty_printer.py +11 -18
  78. classiq/qmod/pretty_print/__init__.py +9 -0
  79. classiq/qmod/pretty_print/expression_to_python.py +221 -0
  80. classiq/qmod/pretty_print/pretty_printer.py +421 -0
  81. classiq/qmod/qmod_constant.py +7 -7
  82. classiq/qmod/qmod_parameter.py +57 -33
  83. classiq/qmod/qmod_struct.py +2 -2
  84. classiq/qmod/qmod_variable.py +40 -29
  85. classiq/qmod/quantum_callable.py +8 -4
  86. classiq/qmod/quantum_expandable.py +22 -15
  87. classiq/qmod/quantum_function.py +15 -4
  88. classiq/qmod/symbolic.py +73 -68
  89. classiq/qmod/symbolic_expr.py +1 -1
  90. classiq/qmod/symbolic_type.py +1 -4
  91. classiq/qmod/utilities.py +29 -0
  92. classiq/synthesis.py +15 -16
  93. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
  94. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/RECORD +95 -94
  95. classiq/interface/executor/error_mitigation.py +0 -6
  96. classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
  97. classiq/interface/model/common_model_types.py +0 -23
  98. classiq/interface/model/quantum_expressions/control_state.py +0 -38
  99. classiq/interface/model/quantum_if_operation.py +0 -94
  100. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -26,21 +26,50 @@ _T = TypeVar("_T")
26
26
 
27
27
 
28
28
  if TYPE_CHECKING:
29
+ SymbolicSuperclass = SymbolicExpr
30
+ else:
31
+ SymbolicSuperclass = Symbolic
29
32
 
30
- class QParam(SymbolicExpr, Generic[_T]):
31
- pass
32
33
 
33
- else:
34
+ class CParam(SymbolicSuperclass):
35
+ pass
34
36
 
35
- class QParam(Symbolic, Generic[_T]):
36
- pass
37
+
38
+ class CInt(CParam):
39
+ pass
37
40
 
38
41
 
39
- class QParamScalar(QParam, SymbolicExpr):
42
+ class CReal(CParam):
40
43
  pass
41
44
 
42
45
 
43
- class QParamList(QParam):
46
+ class CBool(CParam):
47
+ pass
48
+
49
+
50
+ _P = ParamSpec("_P")
51
+
52
+
53
+ class ArrayBase(Generic[_P]):
54
+ # Support comma-separated generic args in older Python versions
55
+ if sys.version_info[0:2] < (3, 10):
56
+
57
+ def __class_getitem__(cls, args) -> _GenericAlias:
58
+ return _GenericAlias(cls, args)
59
+
60
+
61
+ class CArray(CParam, ArrayBase[_P]):
62
+ if TYPE_CHECKING:
63
+
64
+ @property
65
+ def len(self) -> int: ...
66
+
67
+
68
+ class CParamScalar(CParam, SymbolicExpr):
69
+ pass
70
+
71
+
72
+ class CParamList(CParam):
44
73
  def __init__(
45
74
  self,
46
75
  expr: str,
@@ -51,7 +80,14 @@ class QParamList(QParam):
51
80
  self._qmodule = qmodule
52
81
  self._list_type = list_type
53
82
 
54
- def __getitem__(self, key: Any) -> QParam:
83
+ def __getitem__(self, key: Any) -> CParam:
84
+ if isinstance(key, slice):
85
+ start = key.start if key.start is not None else ""
86
+ stop = key.stop if key.stop is not None else ""
87
+ if key.step is not None:
88
+ key = f"{start}:{key.step}:{stop}"
89
+ else:
90
+ key = f"{start}:{stop}"
55
91
  return create_param(
56
92
  f"({self})[{key}]",
57
93
  self._list_type.element_type,
@@ -64,11 +100,11 @@ class QParamList(QParam):
64
100
  )
65
101
 
66
102
  @property
67
- def len(self) -> QParamScalar:
68
- return QParamScalar(f"len({self})")
103
+ def len(self) -> CParamScalar:
104
+ return CParamScalar(f"get_field({self}, 'len')")
69
105
 
70
106
 
71
- class QParamStruct(QParam):
107
+ class CParamStruct(CParam):
72
108
  def __init__(
73
109
  self, expr: str, struct_type: Struct, *, qmodule: ModelStateContainer
74
110
  ) -> None:
@@ -76,8 +112,8 @@ class QParamStruct(QParam):
76
112
  self._qmodule = qmodule
77
113
  self._struct_type = struct_type
78
114
 
79
- def __getattr__(self, field_name: str) -> QParam:
80
- return QParamStruct.get_field(
115
+ def __getattr__(self, field_name: str) -> CParam:
116
+ return CParamStruct.get_field(
81
117
  self._qmodule, str(self), self._struct_type.name, field_name
82
118
  )
83
119
 
@@ -87,7 +123,7 @@ class QParamStruct(QParam):
87
123
  variable_name: str,
88
124
  struct_name: str,
89
125
  field_name: str,
90
- ) -> QParam:
126
+ ) -> CParam:
91
127
  struct_decl = StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.get(
92
128
  struct_name, qmodule.type_decls.get(struct_name)
93
129
  )
@@ -105,27 +141,15 @@ class QParamStruct(QParam):
105
141
  )
106
142
 
107
143
 
108
- _P = ParamSpec("_P")
109
-
110
-
111
- class ArrayBase(Generic[_P]):
112
- # Support comma-separated generic args in older Python versions
113
- if sys.version_info[0:2] < (3, 10):
114
-
115
- def __class_getitem__(cls, args) -> _GenericAlias:
116
- return _GenericAlias(cls, args)
117
-
118
-
119
- class Array(ArrayBase[_P]):
120
- pass
121
-
122
-
123
144
  def create_param(
124
145
  expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
125
- ) -> QParam:
146
+ ) -> CParam:
126
147
  if isinstance(ctype, (ClassicalList, ClassicalArray)):
127
- return QParamList(expr_str, ctype, qmodule=qmodule)
148
+ return CParamList(expr_str, ctype, qmodule=qmodule)
128
149
  elif isinstance(ctype, Struct):
129
- return QParamStruct(expr_str, ctype, qmodule=qmodule)
150
+ return CParamStruct(expr_str, ctype, qmodule=qmodule)
130
151
  else:
131
- return QParamScalar(expr_str)
152
+ return CParamScalar(expr_str)
153
+
154
+
155
+ Array = CArray
@@ -3,7 +3,7 @@ from typing import Any, Type
3
3
 
4
4
  from typing_extensions import dataclass_transform
5
5
 
6
- from classiq.interface.generator.functions.classical_type import QStructBase
6
+ from classiq.interface.generator.functions.classical_type import CStructBase
7
7
 
8
8
 
9
9
  def _qmod_val_to_expr_str(val: Any) -> str:
@@ -30,7 +30,7 @@ def struct(user_class: Type) -> Type:
30
30
 
31
31
  user_dataclass = type(
32
32
  user_class.__name__,
33
- (QStructBase, dataclasses.dataclass(user_class)),
33
+ (CStructBase, dataclasses.dataclass(user_class)),
34
34
  dict(),
35
35
  )
36
36
  user_dataclass.__repr__ = _new_repr # type:ignore[assignment]
@@ -21,6 +21,7 @@ from typing import ( # type: ignore[attr-defined]
21
21
 
22
22
  from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
23
23
 
24
+ from classiq.interface.ast_node import SourceReference
24
25
  from classiq.interface.generator.expressions.expression import Expression
25
26
  from classiq.interface.generator.functions.port_declaration import (
26
27
  PortDeclarationDirection,
@@ -45,11 +46,11 @@ from classiq.interface.model.quantum_type import (
45
46
  )
46
47
 
47
48
  from classiq.exceptions import ClassiqValueError
48
- from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
49
+ from classiq.qmod.qmod_parameter import ArrayBase, CBool, CInt, CParam, CParamScalar
49
50
  from classiq.qmod.quantum_callable import QCallable
50
51
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
51
52
  from classiq.qmod.symbolic_type import SymbolicTypes
52
- from classiq.qmod.utilities import version_portable_get_args
53
+ from classiq.qmod.utilities import get_source_ref, version_portable_get_args
53
54
 
54
55
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
55
56
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
@@ -82,8 +83,9 @@ def _no_current_expandable() -> Iterator[None]:
82
83
  QCallable.CURRENT_EXPANDABLE = current_expandable
83
84
 
84
85
 
85
- class QVar:
86
+ class QVar(Symbolic):
86
87
  def __init__(self, name: str) -> None:
88
+ super().__init__(name)
87
89
  self._name = name
88
90
  if QCallable.CURRENT_EXPANDABLE is not None:
89
91
  QCallable.CURRENT_EXPANDABLE.add_local_handle(
@@ -120,6 +122,9 @@ class QVar:
120
122
  assert type_hint == cls or get_origin(type_hint) == cls
121
123
  return PortDeclarationDirection.Inout
122
124
 
125
+ def __str__(self) -> str:
126
+ return str(self.get_handle_binding())
127
+
123
128
 
124
129
  _Q = TypeVar("_Q", bound=QVar)
125
130
  Output = Annotated[_Q, PortDeclarationDirection.Output]
@@ -131,7 +136,9 @@ class QScalar(QVar, SymbolicExpr):
131
136
  QVar.__init__(self, name)
132
137
  SymbolicExpr.__init__(self, name)
133
138
 
134
- def _insert_arith_operation(self, expr: SymbolicTypes, inplace: bool) -> None:
139
+ def _insert_arith_operation(
140
+ self, expr: SymbolicTypes, inplace: bool, source_ref: SourceReference
141
+ ) -> None:
135
142
  # Fixme: Arithmetic operations are not yet supported on slices (see CAD-12670)
136
143
  if TYPE_CHECKING:
137
144
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -140,16 +147,20 @@ class QScalar(QVar, SymbolicExpr):
140
147
  expression=Expression(expr=str(expr)),
141
148
  result_var=self.get_handle_binding(),
142
149
  inplace_result=inplace,
150
+ source_ref=source_ref,
143
151
  )
144
152
  )
145
153
 
146
- def _insert_amplitude_loading(self, expr: SymbolicTypes) -> None:
154
+ def _insert_amplitude_loading(
155
+ self, expr: SymbolicTypes, source_ref: SourceReference
156
+ ) -> None:
147
157
  if TYPE_CHECKING:
148
158
  assert QCallable.CURRENT_EXPANDABLE is not None
149
159
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
150
160
  AmplitudeLoadingOperation(
151
161
  expression=Expression(expr=str(expr)),
152
162
  result_var=self.get_handle_binding(),
163
+ source_ref=source_ref,
153
164
  )
154
165
  )
155
166
 
@@ -162,7 +173,7 @@ class QScalar(QVar, SymbolicExpr):
162
173
  f"Invalid argument {other!r} for out-of-place arithmetic operation"
163
174
  )
164
175
 
165
- self._insert_arith_operation(other, False)
176
+ self._insert_arith_operation(other, False, get_source_ref(sys._getframe(1)))
166
177
  return self
167
178
 
168
179
  def __ixor__(self, other: Any) -> Self:
@@ -171,7 +182,7 @@ class QScalar(QVar, SymbolicExpr):
171
182
  f"Invalid argument {other!r} for in-place arithmetic operation"
172
183
  )
173
184
 
174
- self._insert_arith_operation(other, True)
185
+ self._insert_arith_operation(other, True, get_source_ref(sys._getframe(1)))
175
186
  return self
176
187
 
177
188
  def __imul__(self, other: Any) -> Self:
@@ -180,7 +191,7 @@ class QScalar(QVar, SymbolicExpr):
180
191
  f"Invalid argument {other!r} for out of ampltiude encoding operation"
181
192
  )
182
193
 
183
- self._insert_amplitude_loading(other)
194
+ self._insert_amplitude_loading(other, get_source_ref(sys._getframe(1)))
184
195
  return self
185
196
 
186
197
 
@@ -207,18 +218,18 @@ class QNum(Generic[_P], QScalar):
207
218
  def __init__(
208
219
  self,
209
220
  name: str,
210
- size: Union[int, QParam[int]],
211
- is_signed: Union[bool, QParam[bool]],
212
- fraction_digits: Union[int, QParam[int]],
221
+ size: Union[int, CInt],
222
+ is_signed: Union[bool, CBool],
223
+ fraction_digits: Union[int, CInt],
213
224
  ):
214
225
  pass
215
226
 
216
227
  def __init__(
217
228
  self,
218
229
  name: str,
219
- size: Union[int, QParam[int], None] = None,
220
- is_signed: Union[bool, QParam[bool], None] = None,
221
- fraction_digits: Union[int, QParam[int], None] = None,
230
+ size: Union[int, CInt, None] = None,
231
+ is_signed: Union[bool, CBool, None] = None,
232
+ fraction_digits: Union[int, CInt, None] = None,
222
233
  ):
223
234
  if (
224
235
  size is None
@@ -244,8 +255,8 @@ class QNum(Generic[_P], QScalar):
244
255
  type_args = type_args[0]
245
256
  if len(type_args) != 3:
246
257
  raise ClassiqValueError(
247
- "QNum receives three type arguments: QNum[size: int | QParam[int], "
248
- "is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
258
+ "QNum receives three type arguments: QNum[size: int | CInt, "
259
+ "is_signed: bool | CBool, fraction_digits: int | CInt]"
249
260
  )
250
261
  return cls.QMOD_TYPE(
251
262
  size=Expression(expr=get_type_hint_expr(type_args[0])),
@@ -261,16 +272,16 @@ class QNum(Generic[_P], QScalar):
261
272
  )
262
273
 
263
274
  @property
264
- def size(self) -> QParamScalar:
265
- return QParamScalar(f"len({self._name})")
275
+ def size(self) -> CParamScalar:
276
+ return CParamScalar(f"get_field({self._name}, 'size')")
266
277
 
267
278
  @property
268
- def fraction_digits(self) -> QParamScalar:
269
- return QParamScalar(f"fraction_digits({self._name})")
279
+ def fraction_digits(self) -> CParamScalar:
280
+ return CParamScalar(f"get_field({self._name}, 'fraction_digits')")
270
281
 
271
282
  @property
272
- def is_signed(self) -> QParamScalar:
273
- return QParamScalar(f"is_signed({self._name})")
283
+ def is_signed(self) -> CParamScalar:
284
+ return CParamScalar(f"get_field({self._name}, 'is_signed')")
274
285
 
275
286
  # Support comma-separated generic args in older Python versions
276
287
  if sys.version_info[0:2] < (3, 10):
@@ -284,10 +295,10 @@ class QArray(ArrayBase[_P], QVar):
284
295
  self,
285
296
  name: str,
286
297
  element_type: _GenericAlias = QBit,
287
- length: Optional[Union[int, QParam[int]]] = None,
298
+ length: Optional[Union[int, CInt]] = None,
288
299
  # TODO [CAD-18620]: improve type hints
289
300
  slice_: Optional[Tuple[int, int]] = None,
290
- index_: Optional[Union[int, QParam[int]]] = None,
301
+ index_: Optional[Union[int, CInt]] = None,
291
302
  ) -> None:
292
303
  if element_type is not QBit:
293
304
  raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
@@ -313,7 +324,7 @@ class QArray(ArrayBase[_P], QVar):
313
324
 
314
325
  return HandleBinding(name=self._name)
315
326
 
316
- def __getitem__(self, key: Union[slice, int, QParam]) -> "QArray":
327
+ def __getitem__(self, key: Union[slice, int, CInt]) -> "QArray":
317
328
  if self._index is not None:
318
329
  raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
319
330
 
@@ -326,7 +337,7 @@ class QArray(ArrayBase[_P], QVar):
326
337
  new_slice = self._get_new_slice(key.start, key.stop)
327
338
 
328
339
  else:
329
- if isinstance(key, QParam) and not isinstance(key, QParamScalar):
340
+ if isinstance(key, CParam) and not isinstance(key, CParamScalar):
330
341
  raise ClassiqValueError("Non-classical parameter for slicing")
331
342
  new_slice = self._get_new_slice(key, key + 1)
332
343
  new_index = new_slice[0]
@@ -368,10 +379,10 @@ class QArray(ArrayBase[_P], QVar):
368
379
  else:
369
380
 
370
381
  @property
371
- def len(self) -> QParamScalar:
382
+ def len(self) -> CParamScalar:
372
383
  if self._length is not None:
373
- return QParamScalar(f"{self._length}")
374
- return QParamScalar(f"len({self._name})")
384
+ return CParamScalar(f"{self._length}")
385
+ return CParamScalar(f"get_field({self._name}, 'len')")
375
386
 
376
387
  @classmethod
377
388
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
@@ -12,6 +12,7 @@ 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
15
16
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
16
17
  from classiq.interface.model.quantum_function_declaration import (
17
18
  QuantumFunctionDeclaration,
@@ -19,7 +20,8 @@ from classiq.interface.model.quantum_function_declaration import (
19
20
  from classiq.interface.model.quantum_statement import QuantumStatement
20
21
  from classiq.interface.model.quantum_type import QuantumType
21
22
 
22
- from classiq.qmod.qmod_parameter import QParam
23
+ from classiq.qmod.qmod_parameter import CInt
24
+ from classiq.qmod.utilities import get_source_ref
23
25
 
24
26
  if TYPE_CHECKING:
25
27
  from classiq.qmod.quantum_expandable import QTerminalCallable
@@ -39,11 +41,13 @@ class QExpandableInterface(ABC):
39
41
 
40
42
  class QCallable(Generic[P], ABC):
41
43
  CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
44
+ FRAME_DEPTH = 1
42
45
 
43
46
  def __call__(self, *args: Any, **kwargs: Any) -> None:
44
47
  assert QCallable.CURRENT_EXPANDABLE is not None
48
+ source_ref = get_source_ref(sys._getframe(self.FRAME_DEPTH))
45
49
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
46
- self.create_quantum_function_call(*args, **kwargs)
50
+ self.create_quantum_function_call(source_ref, *args, **kwargs)
47
51
  )
48
52
  return
49
53
 
@@ -60,7 +64,7 @@ class QCallable(Generic[P], ABC):
60
64
 
61
65
  @abstractmethod
62
66
  def create_quantum_function_call(
63
- self, *args: Any, **kwargs: Any
67
+ self, source_ref_: SourceReference, *args: Any, **kwargs: Any
64
68
  ) -> QuantumFunctionCall:
65
69
  raise NotImplementedError()
66
70
 
@@ -71,5 +75,5 @@ class QCallableList(QCallable, Generic[P], ABC):
71
75
  @property
72
76
  def len(self) -> int: ...
73
77
 
74
- def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
78
+ def __getitem__(self, key: Union[slice, int, CInt]) -> "QTerminalCallable":
75
79
  raise NotImplementedError()
@@ -17,6 +17,7 @@ from typing import (
17
17
 
18
18
  from typing_extensions import Self
19
19
 
20
+ from classiq.interface.ast_node import SourceReference
20
21
  from classiq.interface.generator.expressions.expression import Expression
21
22
  from classiq.interface.model.classical_parameter_declaration import (
22
23
  ClassicalParameterDeclaration,
@@ -42,12 +43,12 @@ from classiq.interface.model.variable_declaration_statement import (
42
43
  from classiq.exceptions import ClassiqValueError
43
44
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
44
45
  from classiq.qmod.qmod_constant import QConstant
45
- from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
46
+ from classiq.qmod.qmod_parameter import CInt, CParam, CParamScalar, create_param
46
47
  from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
47
48
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
48
49
  from classiq.qmod.utilities import mangle_keyword
49
50
 
50
- ArgType = Union[QParam, QVar, QCallable]
51
+ ArgType = Union[CParam, QVar, QCallable]
51
52
 
52
53
 
53
54
  class QExpandable(QCallable, QExpandableInterface, ABC):
@@ -80,8 +81,9 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
80
81
  )
81
82
 
82
83
  def expand(self) -> None:
83
- with self:
84
- self._py_callable(*self._get_positional_args())
84
+ if self not in QExpandable.STACK:
85
+ with self:
86
+ self._py_callable(*self._get_positional_args())
85
87
 
86
88
  def infer_rename_params(self) -> Dict[str, str]:
87
89
  return {}
@@ -109,9 +111,11 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
109
111
  return result
110
112
 
111
113
  def create_quantum_function_call(
112
- self, *args: Any, **kwargs: Any
114
+ self, source_ref_: SourceReference, *args: Any, **kwargs: Any
113
115
  ) -> QuantumFunctionCall:
114
- return _create_quantum_function_call(self.func_decl, None, *args, **kwargs)
116
+ return _create_quantum_function_call(
117
+ self.func_decl, None, source_ref_, *args, **kwargs
118
+ )
115
119
 
116
120
 
117
121
  class QLambdaFunction(QExpandable):
@@ -138,7 +142,7 @@ class QTerminalCallable(QCallable):
138
142
  def __init__(
139
143
  self,
140
144
  decl: QuantumFunctionDeclaration,
141
- index_: Optional[Union[int, QParamScalar]] = None,
145
+ index_: Optional[Union[int, CParamScalar]] = None,
142
146
  ) -> None:
143
147
  self._decl = decl
144
148
  self._index = index_
@@ -147,12 +151,12 @@ class QTerminalCallable(QCallable):
147
151
  def is_list(self) -> bool:
148
152
  return isinstance(self._decl, QuantumOperandDeclaration) and self._decl.is_list
149
153
 
150
- def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
154
+ def __getitem__(self, key: Union[slice, int, CInt]) -> "QTerminalCallable":
151
155
  if not self.is_list:
152
156
  raise ClassiqValueError("Cannot index a non-list operand")
153
157
  if isinstance(key, slice):
154
158
  raise NotImplementedError("Operand lists don't support slicing")
155
- if isinstance(key, QParam) and not isinstance(key, QParamScalar):
159
+ if isinstance(key, CParam) and not isinstance(key, CParamScalar):
156
160
  raise ClassiqValueError("Non-classical parameter for slicing")
157
161
  return QTerminalCallable(self._decl, key)
158
162
 
@@ -169,24 +173,24 @@ class QTerminalCallable(QCallable):
169
173
  else:
170
174
 
171
175
  @property
172
- def len(self) -> QParamScalar:
176
+ def len(self) -> CParamScalar:
173
177
  if not self.is_list:
174
178
  raise ClassiqValueError("Cannot get length of a non-list operand")
175
- return QParamScalar(f"len({self.func_decl.name})")
179
+ return CParamScalar(f"get_field({self.func_decl.name}, 'len')")
176
180
 
177
181
  @property
178
182
  def func_decl(self) -> QuantumFunctionDeclaration:
179
183
  return self._decl
180
184
 
181
185
  def create_quantum_function_call(
182
- self, *args: Any, **kwargs: Any
186
+ self, source_ref_: SourceReference, *args: Any, **kwargs: Any
183
187
  ) -> QuantumFunctionCall:
184
188
  if self.is_list and self._index is None:
185
189
  raise ClassiqValueError(
186
190
  f"Quantum operand {self.func_decl.name!r} is a list and must be indexed"
187
191
  )
188
192
  return _create_quantum_function_call(
189
- self.func_decl, self._index, *args, **kwargs
193
+ self.func_decl, self._index, source_ref_, *args, **kwargs
190
194
  )
191
195
 
192
196
 
@@ -278,7 +282,8 @@ def _prepare_args(
278
282
 
279
283
  def _create_quantum_function_call(
280
284
  decl_: QuantumFunctionDeclaration,
281
- index_: Optional[Union[QParamScalar, int]] = None,
285
+ index_: Optional[Union[CParamScalar, int]] = None,
286
+ source_ref_: Optional[SourceReference] = None,
282
287
  *args: Any,
283
288
  **kwargs: Any,
284
289
  ) -> QuantumFunctionCall:
@@ -306,4 +311,6 @@ def _create_quantum_function_call(
306
311
  index=Expression(expr=str(index_)), name=function_ident
307
312
  )
308
313
 
309
- return QuantumFunctionCall(function=function_ident, positional_args=prepared_args)
314
+ return QuantumFunctionCall(
315
+ function=function_ident, positional_args=prepared_args, source_ref=source_ref_
316
+ )
@@ -1,8 +1,10 @@
1
1
  import ast
2
2
  import functools
3
+ from inspect import isclass
3
4
  from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
4
5
 
5
6
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
7
+ from classiq.interface.generator.functions.classical_type import CStructBase
6
8
  from classiq.interface.generator.model.constraints import Constraints
7
9
  from classiq.interface.generator.model.preferences.preferences import Preferences
8
10
  from classiq.interface.model.model import Model, SerializedModel
@@ -15,7 +17,7 @@ from classiq.exceptions import ClassiqError
15
17
  from classiq.qmod.classical_function import CFunc
16
18
  from classiq.qmod.declaration_inferrer import infer_func_decl
17
19
  from classiq.qmod.qmod_constant import QConstant
18
- from classiq.qmod.qmod_parameter import QParam
20
+ from classiq.qmod.qmod_parameter import CArray, CParam
19
21
  from classiq.qmod.qmod_variable import QVar
20
22
  from classiq.qmod.quantum_callable import QCallable, QCallableList
21
23
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
@@ -44,6 +46,8 @@ def create_model(
44
46
 
45
47
 
46
48
  class QFunc(QExpandable):
49
+ FRAME_DEPTH = 2
50
+
47
51
  def __init__(self, py_callable: Callable) -> None:
48
52
  _validate_no_gen_params(py_callable.__annotations__)
49
53
  super().__init__(py_callable)
@@ -94,7 +98,7 @@ class QFunc(QExpandable):
94
98
  return
95
99
  self.expand()
96
100
  self._qmodule.native_defs[self.func_decl.name] = NativeFunctionDefinition(
97
- **self.func_decl.__dict__, body=self.body
101
+ **{**self.func_decl.dict(), **{"body": self.body}}
98
102
  )
99
103
 
100
104
  def _add_constants_from_classical_code(
@@ -144,7 +148,10 @@ ILLEGAL_PARAM_ERROR = "Unsupported type hint '{annotation}' for argument '{name}
144
148
 
145
149
 
146
150
  class IllegalParamsError(ClassiqError):
147
- _HINT = "\nNote - QMOD functions can declare classical parameters using the type hint 'QParam'."
151
+ _HINT = (
152
+ "\nNote - QMOD functions can declare classical parameters using the type hints "
153
+ "'CInt', 'CReal', 'CBool', and 'CArray'."
154
+ )
148
155
 
149
156
  def __init__(self, message: str) -> None:
150
157
  super().__init__(message + self._HINT)
@@ -156,7 +163,11 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
156
163
  for name, annotation in annotations.items()
157
164
  if not (
158
165
  name == "return"
159
- or get_origin(annotation) is QParam
166
+ or isclass(annotation)
167
+ and issubclass(annotation, CParam)
168
+ or isclass(annotation)
169
+ and issubclass(annotation, CStructBase)
170
+ or get_origin(annotation) is CArray
160
171
  or (get_origin(annotation) or annotation) is QCallable
161
172
  or (get_origin(annotation) or annotation) is QCallableList
162
173
  or QVar.from_type_hint(annotation) is not None