classiq 0.68.0__py3-none-any.whl → 0.70.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +4 -8
  2. classiq/analyzer/analyzer.py +0 -18
  3. classiq/analyzer/url_utils.py +9 -4
  4. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/jobs.py +2 -5
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/backend/quantum_backend_providers.py +8 -3
  12. classiq/interface/chemistry/operator.py +12 -28
  13. classiq/interface/debug_info/back_ref_util.py +22 -0
  14. classiq/interface/debug_info/debug_info.py +11 -21
  15. classiq/interface/executor/optimizer_preferences.py +1 -0
  16. classiq/interface/executor/quantum_instruction_set.py +1 -0
  17. classiq/interface/generator/arith/arithmetic.py +21 -6
  18. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  19. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  20. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  21. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  22. classiq/interface/generator/expressions/expression_types.py +8 -2
  23. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  24. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  25. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  26. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  27. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  28. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  31. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  32. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  33. classiq/interface/generator/functions/classical_type.py +34 -29
  34. classiq/interface/generator/functions/type_name.py +26 -2
  35. classiq/interface/generator/generated_circuit_data.py +84 -27
  36. classiq/interface/generator/model/preferences/preferences.py +1 -0
  37. classiq/interface/generator/quantum_program.py +0 -1
  38. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  39. classiq/interface/generator/types/enum_declaration.py +12 -1
  40. classiq/interface/ide/visual_model.py +0 -2
  41. classiq/interface/model/native_function_definition.py +0 -10
  42. classiq/interface/model/quantum_statement.py +1 -1
  43. classiq/interface/model/quantum_type.py +15 -3
  44. classiq/interface/server/routes.py +0 -6
  45. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  46. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  47. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  48. classiq/model_expansions/evaluators/control.py +1 -1
  49. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  50. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  51. classiq/model_expansions/expression_evaluator.py +3 -1
  52. classiq/model_expansions/generative_functions.py +67 -7
  53. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  54. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  55. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  56. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  57. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  58. classiq/model_expansions/scope.py +7 -6
  59. classiq/model_expansions/scope_initialization.py +20 -33
  60. classiq/model_expansions/transformers/model_renamer.py +13 -4
  61. classiq/model_expansions/visitors/variable_references.py +8 -4
  62. classiq/open_library/functions/__init__.py +2 -0
  63. classiq/open_library/functions/amplitude_amplification.py +3 -7
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +4 -4
  65. classiq/open_library/functions/grover.py +2 -2
  66. classiq/open_library/functions/hea.py +3 -3
  67. classiq/open_library/functions/lookup_table.py +58 -0
  68. classiq/open_library/functions/modular_exponentiation.py +10 -20
  69. classiq/open_library/functions/qft_functions.py +2 -2
  70. classiq/open_library/functions/qsvt.py +8 -8
  71. classiq/open_library/functions/utility_functions.py +2 -2
  72. classiq/qmod/builtins/classical_functions.py +24 -7
  73. classiq/qmod/builtins/enums.py +1 -0
  74. classiq/qmod/builtins/functions/__init__.py +2 -0
  75. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  76. classiq/qmod/builtins/operations.py +26 -11
  77. classiq/qmod/cparam.py +32 -5
  78. classiq/qmod/declaration_inferrer.py +3 -1
  79. classiq/qmod/python_classical_type.py +10 -4
  80. classiq/qmod/qmod_parameter.py +8 -0
  81. classiq/qmod/qmod_variable.py +11 -14
  82. classiq/qmod/quantum_callable.py +2 -1
  83. classiq/qmod/quantum_function.py +3 -2
  84. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  85. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  86. classiq/qmod/semantics/error_manager.py +1 -1
  87. classiq/qmod/semantics/validation/main_validation.py +1 -1
  88. classiq/qmod/semantics/validation/type_hints.py +29 -0
  89. classiq/qmod/utilities.py +67 -2
  90. classiq/synthesis.py +9 -6
  91. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  92. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/RECORD +95 -84
  93. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  94. classiq/interface/execution/jobs.py +0 -31
  95. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  96. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -48,7 +48,7 @@ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QScalar, QVa
48
48
  from classiq.qmod.quantum_callable import QCallable
49
49
  from classiq.qmod.quantum_expandable import prepare_arg
50
50
  from classiq.qmod.symbolic_expr import SymbolicExpr
51
- from classiq.qmod.utilities import get_source_ref
51
+ from classiq.qmod.utilities import Statements, get_source_ref, suppress_return_value
52
52
 
53
53
  _MISSING_VALUE: Final[int] = -1
54
54
 
@@ -63,6 +63,7 @@ def allocate(out: Output[QVar]) -> None:
63
63
  pass
64
64
 
65
65
 
66
+ @suppress_return_value
66
67
  def allocate(*args: Any, **kwargs: Any) -> None:
67
68
  """
68
69
  Initialize a quantum variable to a new quantum object in the zero state:
@@ -106,6 +107,7 @@ def allocate(*args: Any, **kwargs: Any) -> None:
106
107
  )
107
108
 
108
109
 
110
+ @suppress_return_value
109
111
  def bind(
110
112
  source: Union[Input[QVar], list[Input[QVar]]],
111
113
  destination: Union[Output[QVar], list[Output[QVar]]],
@@ -125,10 +127,11 @@ def bind(
125
127
  )
126
128
 
127
129
 
130
+ @suppress_return_value
128
131
  def if_(
129
132
  condition: Union[SymbolicExpr, bool],
130
- then: Union[QCallable, Callable[[], None]],
131
- else_: Union[QCallable, Callable[[], None], int] = _MISSING_VALUE,
133
+ then: Union[QCallable, Callable[[], Statements]],
134
+ else_: Union[QCallable, Callable[[], Statements], int] = _MISSING_VALUE,
132
135
  ) -> None:
133
136
  _validate_operand(then)
134
137
  if else_ != _MISSING_VALUE:
@@ -149,10 +152,11 @@ def if_(
149
152
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(if_stmt)
150
153
 
151
154
 
155
+ @suppress_return_value
152
156
  def control(
153
157
  ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
154
- stmt_block: Union[QCallable, Callable[[], None]],
155
- else_block: Union[QCallable, Callable[[], None], None] = None,
158
+ stmt_block: Union[QCallable, Callable[[], Statements]],
159
+ else_block: Union[QCallable, Callable[[], Statements], None] = None,
156
160
  ) -> None:
157
161
  _validate_operand(stmt_block)
158
162
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -170,6 +174,7 @@ def control(
170
174
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
171
175
 
172
176
 
177
+ @suppress_return_value
173
178
  def assign(expression: SymbolicExpr, target_var: QScalar) -> None:
174
179
  """
175
180
  Initialize a scalar quantum variable using an arithmetic expression.
@@ -194,6 +199,7 @@ def assign(expression: SymbolicExpr, target_var: QScalar) -> None:
194
199
  )
195
200
 
196
201
 
202
+ @suppress_return_value
197
203
  def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
198
204
  """
199
205
  Perform an amplitude-encoding assignment operation on a quantum variable and a
@@ -216,6 +222,7 @@ def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
216
222
  )
217
223
 
218
224
 
225
+ @suppress_return_value
219
226
  def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
220
227
  """
221
228
  Add an arithmetic expression to a quantum variable.
@@ -238,6 +245,7 @@ def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
238
245
  )
239
246
 
240
247
 
248
+ @suppress_return_value
241
249
  def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
242
250
  """
243
251
  Bitwise-XOR a quantum variable with an arithmetic expression.
@@ -260,9 +268,10 @@ def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
260
268
  )
261
269
 
262
270
 
271
+ @suppress_return_value
263
272
  def within_apply(
264
- within: Callable[[], None],
265
- apply: Callable[[], None],
273
+ within: Callable[[], Statements],
274
+ apply: Callable[[], Statements],
266
275
  ) -> None:
267
276
  _validate_operand(within)
268
277
  _validate_operand(apply)
@@ -279,7 +288,10 @@ def within_apply(
279
288
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(within_apply_stmt)
280
289
 
281
290
 
282
- def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) -> None:
291
+ @suppress_return_value
292
+ def repeat(
293
+ count: Union[SymbolicExpr, int], iteration: Callable[[int], Statements]
294
+ ) -> None:
283
295
  _validate_operand(iteration)
284
296
  assert QCallable.CURRENT_EXPANDABLE is not None
285
297
  source_ref = get_source_ref(sys._getframe(1))
@@ -310,9 +322,10 @@ def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) ->
310
322
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(repeat_stmt)
311
323
 
312
324
 
325
+ @suppress_return_value
313
326
  def power(
314
327
  exponent: Union[SymbolicExpr, int],
315
- stmt_block: Union[QCallable, Callable[[], None]],
328
+ stmt_block: Union[QCallable, Callable[[], Statements]],
316
329
  ) -> None:
317
330
  _validate_operand(stmt_block)
318
331
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -327,7 +340,8 @@ def power(
327
340
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(power_stmt)
328
341
 
329
342
 
330
- def invert(stmt_block: Union[QCallable, Callable[[], None]]) -> None:
343
+ @suppress_return_value
344
+ def invert(stmt_block: Union[QCallable, Callable[[], Statements]]) -> None:
331
345
  _validate_operand(stmt_block)
332
346
  assert QCallable.CURRENT_EXPANDABLE is not None
333
347
  source_ref = get_source_ref(sys._getframe(1))
@@ -339,6 +353,7 @@ def invert(stmt_block: Union[QCallable, Callable[[], None]]) -> None:
339
353
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(invert_stmt)
340
354
 
341
355
 
356
+ @suppress_return_value
342
357
  def phase(expr: SymbolicExpr, theta: float = 1.0) -> None:
343
358
  assert QCallable.CURRENT_EXPANDABLE is not None
344
359
  source_ref = get_source_ref(sys._getframe(1))
@@ -402,7 +417,7 @@ def _get_operand_hint(
402
417
 
403
418
 
404
419
  def _operand_to_body(
405
- callable_: Union[QCallable, Callable[[], None]], param_name: str
420
+ callable_: Union[QCallable, Callable[[], Statements]], param_name: str
406
421
  ) -> StatementBlock:
407
422
  op_name = sys._getframe(1).f_code.co_name
408
423
  if (
classiq/qmod/cparam.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import sys
2
+ from abc import ABC, abstractmethod
2
3
  from typing import ( # type: ignore[attr-defined]
3
4
  TYPE_CHECKING,
4
5
  Any,
@@ -9,6 +10,8 @@ from typing import ( # type: ignore[attr-defined]
9
10
 
10
11
  from typing_extensions import ParamSpec
11
12
 
13
+ from classiq.interface.exceptions import ClassiqValueError
14
+
12
15
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
13
16
 
14
17
  if TYPE_CHECKING:
@@ -21,18 +24,34 @@ else:
21
24
 
22
25
  class CParam(SymbolicSuperclass):
23
26
  def __init__(self, expr: str) -> None:
24
- super().__init__(expr, False)
27
+ super().__init__(expr, is_quantum=False)
28
+
29
+
30
+ class CParamAbstract(ABC, CParam):
31
+
32
+ def __new__(cls, *args: Any, **kwargs: Any) -> "CParamAbstract":
33
+ raise ClassiqValueError(
34
+ f"{cls.__name__} is a Qmod type hint for a classical parameter and it cannot be instantiated. "
35
+ f"Use regular Pythonic values as arguments instead. "
36
+ f"Example:\n\n"
37
+ f"def foo(val: {cls.__name__}) -> None: ...\n\n"
38
+ f"foo({_EXAMPLE_VALUES[cls.__name__]}) # Correct\n"
39
+ )
25
40
 
41
+ @abstractmethod
42
+ def __init__(self) -> None:
43
+ pass
26
44
 
27
- class CInt(CParam):
45
+
46
+ class CInt(CParamAbstract):
28
47
  pass
29
48
 
30
49
 
31
- class CReal(CParam):
50
+ class CReal(CParamAbstract):
32
51
  pass
33
52
 
34
53
 
35
- class CBool(CParam):
54
+ class CBool(CParamAbstract):
36
55
  pass
37
56
 
38
57
 
@@ -47,7 +66,7 @@ class ArrayBase(Generic[_P]):
47
66
  return _GenericAlias(cls, args)
48
67
 
49
68
 
50
- class CArray(CParam, ArrayBase[_P]):
69
+ class CArray(CParamAbstract, ArrayBase[_P]):
51
70
  if TYPE_CHECKING:
52
71
 
53
72
  @property
@@ -62,3 +81,11 @@ Array = CArray
62
81
  class CParamScalar(CParam, SymbolicExpr):
63
82
  def __hash__(self) -> int:
64
83
  return hash(str(self))
84
+
85
+
86
+ _EXAMPLE_VALUES: dict[str, Any] = {
87
+ CInt.__name__: 1,
88
+ CReal.__name__: 1.0,
89
+ CBool.__name__: True,
90
+ CArray.__name__: [1, 2],
91
+ }
@@ -38,6 +38,7 @@ from classiq.qmod.model_state_container import ModelStateContainer
38
38
  from classiq.qmod.python_classical_type import PythonClassicalType
39
39
  from classiq.qmod.qmod_variable import QVar
40
40
  from classiq.qmod.quantum_callable import QCallableList
41
+ from classiq.qmod.semantics.validation.type_hints import validate_annotation
41
42
  from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
42
43
 
43
44
  if sys.version_info[0:2] >= (3, 9):
@@ -123,7 +124,7 @@ def _extract_operand_decl(
123
124
 
124
125
 
125
126
  def _extract_operand_param(py_type: Any) -> tuple[Optional[str], Any]:
126
- if sys.version_info[0:2] < (3, 9) or get_origin(py_type) is not Annotated:
127
+ if get_origin(py_type) is not Annotated:
127
128
  return None, py_type
128
129
  args = get_args(py_type)
129
130
  if len(args) == 2:
@@ -163,6 +164,7 @@ def _extract_positional_args(
163
164
  ) -> Sequence[AnonPositionalArg]:
164
165
  result: list[AnonPositionalArg] = []
165
166
  for name, py_type in args:
167
+ validate_annotation(py_type)
166
168
  if name == "return":
167
169
  continue
168
170
  name = unmangle_keyword(name)
@@ -30,16 +30,22 @@ CARRAY_ERROR_MESSAGE = (
30
30
 
31
31
  class PythonClassicalType:
32
32
  def convert(self, py_type: type) -> Optional[ConcreteClassicalType]:
33
- if py_type is int or py_type is CInt:
33
+ if py_type is int:
34
+ return Integer().set_generative()
35
+ elif py_type is CInt:
34
36
  return Integer()
35
- elif py_type in (float, complex) or py_type is CReal:
37
+ elif py_type in (float, complex):
38
+ return Real().set_generative()
39
+ elif py_type is CReal:
36
40
  return Real()
37
- elif py_type is bool or py_type is CBool:
41
+ elif py_type is bool:
42
+ return Bool().set_generative()
43
+ elif py_type is CBool:
38
44
  return Bool()
39
45
  elif get_origin(py_type) is list:
40
46
  element_type = self.convert(get_args(py_type)[0])
41
47
  if element_type is not None:
42
- return ClassicalList(element_type=element_type)
48
+ return ClassicalList(element_type=element_type).set_generative()
43
49
  elif get_origin(py_type) is CArray:
44
50
  array_args = version_portable_get_args(py_type)
45
51
  if len(array_args) == 1:
@@ -22,6 +22,11 @@ from classiq.qmod.cparam import ( # noqa: F401
22
22
  CParamScalar,
23
23
  CReal,
24
24
  )
25
+ from classiq.qmod.generative import (
26
+ generative_mode_context,
27
+ interpret_expression,
28
+ is_generative_mode,
29
+ )
25
30
  from classiq.qmod.model_state_container import ModelStateContainer
26
31
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
27
32
 
@@ -66,6 +71,9 @@ class CParamList(CParam):
66
71
 
67
72
  @property
68
73
  def len(self) -> CParamScalar:
74
+ if is_generative_mode():
75
+ with generative_mode_context(False):
76
+ return interpret_expression(str(self.len))
69
77
  return CParamScalar(f"get_field({self}, 'len')")
70
78
 
71
79
 
@@ -23,7 +23,7 @@ from typing_extensions import ParamSpec, Self, _AnnotatedAlias
23
23
  from classiq.interface.exceptions import ClassiqValueError
24
24
  from classiq.interface.generator.expressions.expression import Expression
25
25
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
26
- from classiq.interface.generator.expressions.qmod_qarray_proxy import (
26
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
27
27
  ILLEGAL_SLICE_MSG,
28
28
  ILLEGAL_SLICING_STEP_MSG,
29
29
  )
@@ -72,12 +72,6 @@ from classiq.qmod.utilities import (
72
72
  )
73
73
 
74
74
 
75
- def _is_input_output_typehint(type_hint: Any) -> bool:
76
- return isinstance(type_hint, _AnnotatedAlias) and isinstance(
77
- type_hint.__metadata__[0], PortDeclarationDirection
78
- )
79
-
80
-
81
75
  def get_type_hint_expr(type_hint: Any) -> str:
82
76
  if isinstance(type_hint, ForwardRef): # expression in string literal
83
77
  return str(type_hint.__forward_arg__)
@@ -143,9 +137,12 @@ class QVar(Symbolic):
143
137
 
144
138
  @staticmethod
145
139
  def from_type_hint(type_hint: Any) -> Optional[type["QVar"]]:
146
- if _is_input_output_typehint(type_hint):
147
- return QVar.from_type_hint(type_hint.__args__[0])
148
- type_ = get_origin(type_hint) or type_hint
140
+ non_annotated_type = (
141
+ type_hint.__origin__
142
+ if isinstance(type_hint, _AnnotatedAlias)
143
+ else type_hint
144
+ )
145
+ type_ = get_origin(non_annotated_type) or non_annotated_type
149
146
  if issubclass(type_, QVar):
150
147
  if issubclass(type_, QStruct):
151
148
  with _no_current_expandable():
@@ -170,10 +167,10 @@ class QVar(Symbolic):
170
167
 
171
168
  @classmethod
172
169
  def port_direction(cls, type_hint: Any) -> PortDeclarationDirection:
173
- if _is_input_output_typehint(type_hint):
174
- assert len(type_hint.__metadata__) >= 1
175
- return type_hint.__metadata__[0]
176
- assert type_hint == cls or get_origin(type_hint) == cls
170
+ if isinstance(type_hint, _AnnotatedAlias):
171
+ for metadata in type_hint.__metadata__:
172
+ if isinstance(metadata, PortDeclarationDirection):
173
+ return metadata
177
174
  return PortDeclarationDirection.Inout
178
175
 
179
176
  def __str__(self) -> str:
@@ -21,7 +21,7 @@ from classiq.interface.model.quantum_type import QuantumType
21
21
  from classiq.interface.source_reference import SourceReference
22
22
 
23
23
  from classiq.qmod.cparam import CInt
24
- from classiq.qmod.utilities import get_source_ref
24
+ from classiq.qmod.utilities import get_source_ref, suppress_return_value
25
25
 
26
26
  if TYPE_CHECKING:
27
27
  from classiq.qmod.quantum_expandable import QTerminalCallable
@@ -48,6 +48,7 @@ class QCallable(Generic[P], ABC):
48
48
  CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
49
49
  FRAME_DEPTH = 1
50
50
 
51
+ @suppress_return_value
51
52
  def __call__(self, *args: Any, **kwargs: Any) -> None:
52
53
  assert QCallable.CURRENT_EXPANDABLE is not None
53
54
  source_ref = get_source_ref(sys._getframe(self.FRAME_DEPTH))
@@ -24,10 +24,11 @@ from classiq.interface.model.quantum_function_declaration import (
24
24
  )
25
25
 
26
26
  from classiq.qmod.classical_function import CFunc
27
+ from classiq.qmod.cparam import CParamAbstract
27
28
  from classiq.qmod.declaration_inferrer import infer_func_decl
28
29
  from classiq.qmod.generative import set_frontend_interpreter
29
30
  from classiq.qmod.qmod_constant import QConstant
30
- from classiq.qmod.qmod_parameter import CArray, CParam
31
+ from classiq.qmod.qmod_parameter import CArray
31
32
  from classiq.qmod.qmod_variable import QVar
32
33
  from classiq.qmod.quantum_callable import QCallable, QCallableList
33
34
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
@@ -308,7 +309,7 @@ def _validate_no_gen_params(annotations: dict[str, Any]) -> None:
308
309
  for name, annotation in annotations.items()
309
310
  if not (
310
311
  name == "return"
311
- or (isclass(annotation) and issubclass(annotation, CParam))
312
+ or (isclass(annotation) and issubclass(annotation, CParamAbstract))
312
313
  or (isclass(annotation) and is_dataclass(annotation))
313
314
  or (isclass(annotation) and isinstance(annotation, EnumMeta))
314
315
  or get_origin(annotation) is CArray
@@ -3,18 +3,9 @@ from contextlib import contextmanager
3
3
  from typing import Any, Optional
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
- from classiq.interface.generator.expressions.expression import Expression
7
- from classiq.interface.generator.functions.classical_type import Integer
8
- from classiq.interface.generator.functions.port_declaration import (
9
- PortDeclarationDirection,
10
- )
11
- from classiq.interface.model.classical_parameter_declaration import (
12
- ClassicalParameterDeclaration,
13
- )
14
6
  from classiq.interface.model.model import Model
15
7
  from classiq.interface.model.model_visitor import ModelVisitor
16
8
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
17
- from classiq.interface.model.port_declaration import PortDeclaration
18
9
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
19
10
  from classiq.interface.model.quantum_function_declaration import (
20
11
  AnonQuantumOperandDeclaration,
@@ -24,36 +15,17 @@ from classiq.interface.model.quantum_function_declaration import (
24
15
  from classiq.interface.model.quantum_lambda_function import (
25
16
  QuantumLambdaFunction,
26
17
  )
27
- from classiq.interface.model.quantum_type import QuantumBitvector
28
18
 
29
19
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
30
20
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
31
21
  from classiq.qmod.semantics.error_manager import ErrorManager
32
22
  from classiq.qmod.semantics.lambdas import get_renamed_parameters
33
23
 
34
- ALLOCATE_DECL_FOR_COMPATIBILITY = QuantumFunctionDeclaration(
35
- name="allocate",
36
- positional_arg_declarations=[
37
- ClassicalParameterDeclaration(
38
- name="num_qubits",
39
- classical_type=Integer(),
40
- ),
41
- PortDeclaration(
42
- name="out",
43
- quantum_type=QuantumBitvector(length=Expression(expr="num_qubits")),
44
- direction=PortDeclarationDirection.Output,
45
- ),
46
- ],
47
- )
48
-
49
24
 
50
25
  def _annotate_function_call_decl(
51
26
  fc: QuantumFunctionCall,
52
27
  function_dict: Mapping[str, QuantumFunctionDeclaration],
53
28
  ) -> None:
54
- if fc.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
55
- fc.set_func_decl(ALLOCATE_DECL_FOR_COMPATIBILITY)
56
- return
57
29
  if fc._func_decl is None:
58
30
  func_decl = function_dict.get(fc.func_name)
59
31
  if func_decl is None:
@@ -9,8 +9,18 @@ class QStructAnnotator(ModelVisitor):
9
9
  self._visited: set[TypeName] = set()
10
10
 
11
11
  def visit_TypeName(self, type_name: TypeName) -> None:
12
+ self._annotate_quantum_struct(type_name)
13
+ self._annotate_classical_struct(type_name)
14
+
15
+ def _annotate_quantum_struct(self, type_name: TypeName) -> None:
16
+ if (
17
+ type_name.has_classical_struct_decl
18
+ or type_name.has_fields
19
+ or type_name in self._visited
20
+ ):
21
+ return
12
22
  decl = QMODULE.qstruct_decls.get(type_name.name)
13
- if decl is None or type_name.has_fields or (type_name in self._visited):
23
+ if decl is None:
14
24
  return
15
25
  self._visited.add(type_name)
16
26
  new_fields = {
@@ -21,3 +31,13 @@ class QStructAnnotator(ModelVisitor):
21
31
  # qstructs
22
32
  self.visit(new_fields)
23
33
  type_name.set_fields(new_fields)
34
+
35
+ def _annotate_classical_struct(self, type_name: TypeName) -> None:
36
+ if type_name.has_classical_struct_decl or type_name.has_fields:
37
+ return
38
+ decl = QMODULE.type_decls.get(type_name.name)
39
+ if decl is None:
40
+ return
41
+ type_name.set_classical_struct_decl(decl)
42
+ for field_type in decl.variables.values():
43
+ self.visit(field_type)
@@ -46,7 +46,7 @@ class ErrorManager:
46
46
  error: str,
47
47
  *,
48
48
  source_ref: Optional[SourceReference] = None,
49
- function: Optional[str] = None
49
+ function: Optional[str] = None,
50
50
  ) -> None:
51
51
  if not self._ignore_errors:
52
52
  self._errors.append(
@@ -25,7 +25,7 @@ def _validate_main_classical_param_type(
25
25
  ) -> None:
26
26
  if isinstance(param, ClassicalList):
27
27
  raise ClassiqExpansionError(
28
- f"Classical array parameter {param_name!r} of function 'main' must must "
28
+ f"Classical array parameter {param_name!r} of function 'main' must "
29
29
  f"specify array length",
30
30
  )
31
31
  if isinstance(param, ClassicalArray):
@@ -0,0 +1,29 @@
1
+ from typing import Any
2
+
3
+ from typing_extensions import _AnnotatedAlias
4
+
5
+ from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.generator.functions.port_declaration import (
7
+ PortDeclarationDirection,
8
+ )
9
+
10
+ annotation_map: dict[PortDeclarationDirection, str] = {
11
+ PortDeclarationDirection.Input: PortDeclarationDirection.Input.name,
12
+ PortDeclarationDirection.Output: PortDeclarationDirection.Output.name,
13
+ }
14
+
15
+
16
+ def validate_annotation(type_hint: Any) -> None:
17
+ if not isinstance(type_hint, _AnnotatedAlias):
18
+ return
19
+ directions: list[PortDeclarationDirection] = [
20
+ direction
21
+ for direction in type_hint.__metadata__
22
+ if isinstance(direction, PortDeclarationDirection)
23
+ ]
24
+ if len(directions) <= 1:
25
+ return
26
+ raise ClassiqValueError(
27
+ f"Multiple directions are not allowed in a single type hint: "
28
+ f"[{', '.join(annotation_map[direction] for direction in reversed(directions))}]\n"
29
+ )
classiq/qmod/utilities.py CHANGED
@@ -3,16 +3,33 @@ import inspect
3
3
  import itertools
4
4
  import keyword
5
5
  import sys
6
+ from collections import Counter
6
7
  from collections.abc import Iterable
7
8
  from enum import Enum as PythonEnum
8
9
  from types import FrameType
9
- from typing import Any, ForwardRef, Literal, Optional, get_args, get_origin, overload
10
+ from typing import (
11
+ TYPE_CHECKING,
12
+ Any,
13
+ Callable,
14
+ ForwardRef,
15
+ Literal,
16
+ Optional,
17
+ Union,
18
+ get_args,
19
+ get_origin,
20
+ overload,
21
+ )
22
+
23
+ from typing_extensions import ParamSpec
10
24
 
11
- from classiq.interface.generator.expressions.qmod_struct_instance import (
25
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
12
26
  QmodStructInstance,
13
27
  )
14
28
  from classiq.interface.source_reference import SourceReference
15
29
 
30
+ if TYPE_CHECKING:
31
+ from classiq.qmod.qmod_variable import QVar
32
+
16
33
  DEFAULT_DECIMAL_PRECISION = 4
17
34
 
18
35
 
@@ -132,3 +149,51 @@ def varname(depth: int) -> Optional[str]:
132
149
  if not var_name.isidentifier():
133
150
  return None
134
151
  return var_name
152
+
153
+
154
+ Params = ParamSpec("Params")
155
+
156
+
157
+ def suppress_return_value(func: Callable[Params, None]) -> Callable[Params, None]:
158
+ # An empty decorator suppresses mypy's func-returns-value error when assigning the
159
+ # return value of a None-returning function
160
+ return func
161
+
162
+
163
+ Statements = Union[None, list[Union[None, "QVar"]], tuple[Union[None, "QVar"], ...]]
164
+
165
+
166
+ def _eval_qnum(val: int, size: int, is_signed: bool, fraction_digits: int) -> float:
167
+ if val < 0 or val >= 2**size:
168
+ raise ValueError
169
+ if size == 1 and is_signed and fraction_digits == 1:
170
+ return -0.5 if val == 1 else 0
171
+ if is_signed and val & (1 << (size - 1)) > 0:
172
+ val ^= 1 << (size - 1)
173
+ val -= 1 << (size - 1)
174
+ return val * 2**-fraction_digits
175
+
176
+
177
+ def qnum_values(size: int, is_signed: bool, fraction_digits: int) -> list[float]:
178
+ return [_eval_qnum(i, size, is_signed, fraction_digits) for i in range(2**size)]
179
+
180
+
181
+ def qnum_attributes(max_size: int) -> list[tuple[int, bool, int]]:
182
+ return [(1, True, 1)] + [
183
+ (size, is_signed, fraction_digits)
184
+ for size in range(1, max_size + 1)
185
+ for is_signed in (False, True)
186
+ for fraction_digits in range(size - int(is_signed) + 1)
187
+ ]
188
+
189
+
190
+ _VAR_NAME_COUNTER: Counter[str] = Counter()
191
+
192
+
193
+ def get_temp_var_name(var_name: str = "temp") -> str:
194
+ n = _VAR_NAME_COUNTER[var_name]
195
+ _VAR_NAME_COUNTER[var_name] += 1
196
+ return f"{var_name}_{n}"
197
+
198
+
199
+ RealFunction = Callable[Params, float]
classiq/synthesis.py CHANGED
@@ -3,16 +3,15 @@ from typing import Any, NewType, Optional, Union
3
3
  import pydantic
4
4
 
5
5
  from classiq.interface.analyzer.result import QasmCode
6
- from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.exceptions import ClassiqError, ClassiqValueError
7
7
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
8
8
  from classiq.interface.generator.model.constraints import Constraints
9
9
  from classiq.interface.generator.model.preferences.preferences import Preferences
10
- from classiq.interface.model.model import Model, SerializedModel
10
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model, SerializedModel
11
11
 
12
12
  from classiq import QuantumProgram
13
13
  from classiq._internals import async_utils
14
14
  from classiq._internals.api_wrapper import ApiWrapper
15
- from classiq.qmod.create_model_function import create_model
16
15
  from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
17
16
 
18
17
  SerializedQuantumProgram = NewType("SerializedQuantumProgram", str)
@@ -90,9 +89,13 @@ def synthesize(
90
89
  SerializedQuantumProgram: Quantum program serialized as a string. (See: QuantumProgram)
91
90
  """
92
91
  if isinstance(model, (QFunc, GenerativeQFunc)):
93
- serialized_model = create_model(
94
- model, constraints=constraints, preferences=preferences
95
- )
92
+ func_name = model._py_callable.__name__
93
+ if func_name != MAIN_FUNCTION_NAME:
94
+ raise ClassiqError(
95
+ f"The entry point function must be named 'main', got {func_name!r}"
96
+ )
97
+ model_obj = model.create_model(constraints=constraints, preferences=preferences)
98
+ serialized_model = model_obj.get_model()
96
99
  else:
97
100
  serialized_model = model
98
101
  result = async_utils.run(synthesize_async(serialized_model))