classiq 0.65.3__py3-none-any.whl → 0.66.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 (60) hide show
  1. classiq/_internals/api_wrapper.py +43 -0
  2. classiq/applications/qnn/qlayer.py +65 -3
  3. classiq/execution/execution_session.py +0 -2
  4. classiq/execution/iqcc.py +66 -1
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/ast_node.py +15 -1
  7. classiq/interface/backend/backend_preferences.py +0 -14
  8. classiq/interface/debug_info/debug_info.py +2 -0
  9. classiq/interface/execution/iqcc.py +25 -0
  10. classiq/interface/generator/expressions/qmod_qarray_proxy.py +1 -13
  11. classiq/interface/generator/visitor.py +7 -4
  12. classiq/interface/model/classical_if.py +4 -0
  13. classiq/interface/model/control.py +4 -0
  14. classiq/interface/model/invert.py +4 -0
  15. classiq/interface/model/model.py +3 -1
  16. classiq/interface/model/model_visitor.py +14 -0
  17. classiq/interface/model/power.py +4 -0
  18. classiq/interface/model/quantum_statement.py +3 -3
  19. classiq/interface/model/repeat.py +4 -0
  20. classiq/interface/model/within_apply_operation.py +4 -0
  21. classiq/interface/server/routes.py +6 -0
  22. classiq/model_expansions/closure.py +0 -11
  23. classiq/model_expansions/evaluators/quantum_type_utils.py +6 -6
  24. classiq/model_expansions/expression_evaluator.py +10 -1
  25. classiq/model_expansions/interpreters/base_interpreter.py +28 -18
  26. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +58 -1
  27. classiq/model_expansions/interpreters/generative_interpreter.py +7 -13
  28. classiq/model_expansions/quantum_operations/allocate.py +69 -0
  29. classiq/model_expansions/quantum_operations/call_emitter.py +7 -6
  30. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +4 -4
  31. classiq/model_expansions/quantum_operations/emitter.py +2 -15
  32. classiq/model_expansions/quantum_operations/quantum_function_call.py +22 -0
  33. classiq/model_expansions/quantum_operations/shallow_emitter.py +21 -35
  34. classiq/model_expansions/scope_initialization.py +49 -34
  35. classiq/model_expansions/transformers/model_renamer.py +98 -0
  36. classiq/model_expansions/transformers/var_splitter.py +7 -82
  37. classiq/open_library/functions/grover.py +5 -5
  38. classiq/qmod/builtins/__init__.py +1 -1
  39. classiq/qmod/builtins/functions/__init__.py +0 -2
  40. classiq/qmod/builtins/functions/allocation.py +1 -26
  41. classiq/qmod/builtins/operations.py +12 -6
  42. classiq/qmod/generative.py +6 -4
  43. classiq/qmod/native/pretty_printer.py +3 -2
  44. classiq/qmod/pretty_print/pretty_printer.py +3 -1
  45. classiq/qmod/qmod_variable.py +6 -1
  46. classiq/qmod/semantics/annotation/call_annotation.py +30 -2
  47. classiq/qmod/semantics/annotation/qstruct_annotator.py +2 -2
  48. classiq/qmod/semantics/error_manager.py +20 -6
  49. classiq/qmod/semantics/static_semantics_visitor.py +3 -40
  50. classiq/qmod/semantics/validation/constants_validation.py +2 -3
  51. classiq/qmod/semantics/validation/function_name_collisions_validation.py +6 -9
  52. classiq/qmod/semantics/validation/main_validation.py +2 -3
  53. classiq/qmod/semantics/validation/model_validation.py +25 -0
  54. classiq/qmod/semantics/validation/signature_validation.py +24 -0
  55. classiq/qmod/semantics/validation/types_validation.py +45 -46
  56. classiq/qmod/utilities.py +12 -0
  57. {classiq-0.65.3.dist-info → classiq-0.66.0.dist-info}/METADATA +1 -1
  58. {classiq-0.65.3.dist-info → classiq-0.66.0.dist-info}/RECORD +59 -55
  59. classiq/model_expansions/expression_renamer.py +0 -76
  60. {classiq-0.65.3.dist-info → classiq-0.66.0.dist-info}/WHEEL +0 -0
@@ -1,22 +1,19 @@
1
1
  from collections.abc import Sequence
2
- from typing import TYPE_CHECKING
2
+ from typing import Any
3
3
 
4
4
  from classiq.interface.exceptions import ClassiqError
5
5
  from classiq.interface.generator.constant import Constant
6
6
  from classiq.interface.generator.expressions.expression_constants import (
7
7
  CPARAM_EXECUTION_SUFFIX,
8
+ RESERVED_EXPRESSIONS,
8
9
  )
9
- from classiq.interface.model.classical_parameter_declaration import (
10
- ClassicalParameterDeclaration,
11
- )
10
+ from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
12
11
  from classiq.interface.model.handle_binding import HandleBinding
13
12
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
14
13
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
15
14
  from classiq.interface.model.port_declaration import PortDeclaration
16
15
  from classiq.interface.model.quantum_function_declaration import (
17
- NamedParamsQuantumFunctionDeclaration,
18
16
  PositionalArg,
19
- QuantumFunctionDeclaration,
20
17
  )
21
18
 
22
19
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
@@ -26,7 +23,6 @@ from classiq.model_expansions.evaluators.classical_expression import (
26
23
  from classiq.model_expansions.evaluators.parameter_types import (
27
24
  evaluate_type_in_quantum_symbol,
28
25
  )
29
- from classiq.model_expansions.expression_renamer import ExpressionRenamer
30
26
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
31
27
  from classiq.qmod.builtins import BUILTIN_CONSTANTS
32
28
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
@@ -39,19 +35,6 @@ from classiq.qmod.model_state_container import QMODULE
39
35
  from classiq.qmod.quantum_function import GenerativeQFunc
40
36
 
41
37
 
42
- def get_main_renamer(
43
- func_decls: Sequence[QuantumFunctionDeclaration],
44
- ) -> ExpressionRenamer:
45
- for func_decl in func_decls:
46
- if func_decl.name == MAIN_FUNCTION_NAME:
47
- if TYPE_CHECKING:
48
- assert isinstance(func_decl, NamedParamsQuantumFunctionDeclaration)
49
- return ExpressionRenamer.from_positional_arg_declarations(
50
- func_decl.positional_arg_declarations, CPARAM_EXECUTION_SUFFIX
51
- )
52
- return ExpressionRenamer(var_mapping={})
53
-
54
-
55
38
  def add_constants_to_scope(constants: list[Constant], scope: Scope) -> None:
56
39
  for constant in constants:
57
40
  scope[constant.name] = Evaluated(
@@ -122,21 +105,17 @@ def add_entry_point_params_to_scope(
122
105
  parameters: Sequence[PositionalArg], main_closure: FunctionClosure
123
106
  ) -> None:
124
107
  for parameter in parameters:
125
- if isinstance(parameter, PortDeclaration):
126
- main_closure.scope[parameter.name] = Evaluated(
127
- value=QuantumSymbol(
128
- handle=HandleBinding(name=parameter.name),
129
- quantum_type=evaluate_type_in_quantum_symbol(
130
- parameter.quantum_type, main_closure.scope, parameter.name
131
- ),
108
+ if not isinstance(parameter, PortDeclaration):
109
+ continue
110
+ main_closure.scope[parameter.name] = Evaluated(
111
+ value=QuantumSymbol(
112
+ handle=HandleBinding(name=parameter.name),
113
+ quantum_type=evaluate_type_in_quantum_symbol(
114
+ parameter.quantum_type, main_closure.scope, parameter.name
132
115
  ),
133
- defining_function=main_closure,
134
- )
135
- elif isinstance(parameter, ClassicalParameterDeclaration):
136
- main_closure.scope[parameter.name] = Evaluated(
137
- value=parameter.classical_type.as_symbolic(parameter.name),
138
- defining_function=main_closure,
139
- )
116
+ ),
117
+ defining_function=main_closure,
118
+ )
140
119
 
141
120
 
142
121
  def init_top_level_scope(model: Model, scope: Scope) -> None:
@@ -148,3 +127,39 @@ def init_top_level_scope(model: Model, scope: Scope) -> None:
148
127
  def init_builtin_types() -> None:
149
128
  QMODULE.enum_decls |= BUILTIN_ENUM_DECLARATIONS
150
129
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
130
+
131
+
132
+ def _rename_exec_param(param_name: str) -> str:
133
+ if param_name in RESERVED_EXPRESSIONS:
134
+ return param_name
135
+ return param_name + CPARAM_EXECUTION_SUFFIX
136
+
137
+
138
+ def _add_exec_param_parts_to_scope(param_val: Any, scope: Scope) -> None:
139
+ if not isinstance(param_val, list):
140
+ scope[str(param_val)] = Evaluated(value=param_val)
141
+ return
142
+ for param_part in param_val:
143
+ _add_exec_param_parts_to_scope(param_part, scope)
144
+
145
+
146
+ def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
147
+ if model.execution_parameters is not None:
148
+ exec_params = {
149
+ param_name: (param_name, param_type)
150
+ for param_name, param_type in model.execution_parameters.items()
151
+ }
152
+ else:
153
+ exec_params = {
154
+ param.name: (_rename_exec_param(param.name), param.classical_type)
155
+ for param in model.function_dict.get(
156
+ "_dec_main", model.main_func
157
+ ).param_decls
158
+ }
159
+ for param_name, (param_rename, param_type) in exec_params.items():
160
+ param_val = param_type.as_symbolic(param_rename)
161
+ scope[param_name] = Evaluated(value=param_val)
162
+ scope[param_rename] = Evaluated(value=param_val)
163
+ if isinstance(param_val, list):
164
+ _add_exec_param_parts_to_scope(param_val, scope)
165
+ return dict(exec_params.values())
@@ -0,0 +1,98 @@
1
+ import ast
2
+ from collections.abc import Mapping, Sequence
3
+ from dataclasses import dataclass
4
+ from typing import TypeVar, cast
5
+
6
+ from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.visitor import NodeType
8
+ from classiq.interface.model.handle_binding import HandleBinding
9
+ from classiq.interface.model.model_visitor import ModelTransformer
10
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
11
+ QuantumExpressionOperation,
12
+ )
13
+
14
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
15
+
16
+ AST_NODE = TypeVar("AST_NODE", bound=NodeType)
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class HandleRenaming:
21
+ source_handle: HandleBinding
22
+ target_var_name: str
23
+
24
+ @property
25
+ def target_var_handle(self) -> HandleBinding:
26
+ return HandleBinding(name=self.target_var_name)
27
+
28
+
29
+ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
30
+
31
+
32
+ class ModelRenamer:
33
+ def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolRenaming) -> AST_NODE:
34
+ if len(symbol_mapping) == 0:
35
+ return subject
36
+ handle_replacements = {
37
+ part.source_handle: part.target_var_handle
38
+ for parts in symbol_mapping.values()
39
+ for part in parts
40
+ }
41
+
42
+ class ReplaceSplitVars(ModelTransformer):
43
+ @staticmethod
44
+ def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
45
+ handle = handle.collapse()
46
+ for handle_to_replace, replacement in handle_replacements.items():
47
+ handle = handle.replace_prefix(handle_to_replace, replacement)
48
+ return handle
49
+
50
+ @staticmethod
51
+ def visit_Expression(expr: Expression) -> Expression:
52
+ return self._rewrite_expression(symbol_mapping, expr)
53
+
54
+ def visit_QuantumExpressionOperation(
55
+ self, op: QuantumExpressionOperation
56
+ ) -> QuantumExpressionOperation:
57
+ op = cast(QuantumExpressionOperation, self.generic_visit(op))
58
+ previous_var_handles = list(op._var_handles)
59
+ op._var_handles = self.visit(op._var_handles)
60
+ op._var_types = {
61
+ new_handle.name: op._var_types.get(
62
+ new_handle.name, op._var_types[previous_handle.name]
63
+ )
64
+ for previous_handle, new_handle in zip(
65
+ previous_var_handles, op._var_handles
66
+ )
67
+ }
68
+ return op
69
+
70
+ return ReplaceSplitVars().visit(subject)
71
+
72
+ def _rewrite_expression(
73
+ self,
74
+ symbol_mapping: SymbolRenaming,
75
+ expression: Expression,
76
+ ) -> Expression:
77
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
78
+ vrc.visit(ast.parse(expression.expr))
79
+
80
+ handle_names = {
81
+ part.source_handle: part.target_var_handle
82
+ for parts in symbol_mapping.values()
83
+ for part in parts
84
+ }
85
+ new_expr_str = expression.expr
86
+ for handle in vrc.var_handles:
87
+ new_handle = handle.collapse()
88
+ for handle_to_replace, replacement in handle_names.items():
89
+ new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
90
+ new_expr_str = new_expr_str.replace(str(handle), str(new_handle))
91
+ if handle.qmod_expr != str(handle):
92
+ new_expr_str = new_expr_str.replace(
93
+ handle.qmod_expr, new_handle.qmod_expr
94
+ )
95
+
96
+ new_expr = Expression(expr=new_expr_str)
97
+ new_expr._evaluated_expr = expression._evaluated_expr
98
+ return new_expr
@@ -1,14 +1,13 @@
1
1
  import ast
2
2
  from dataclasses import dataclass
3
3
  from itertools import chain
4
- from typing import TYPE_CHECKING, Callable, Optional, TypeVar, cast
4
+ from typing import TYPE_CHECKING, Callable, Optional
5
5
 
6
6
  from classiq.interface.exceptions import (
7
7
  ClassiqExpansionError,
8
8
  ClassiqInternalExpansionError,
9
9
  )
10
10
  from classiq.interface.generator.expressions.expression import Expression
11
- from classiq.interface.generator.visitor import NodeType, Transformer
12
11
  from classiq.interface.model.bind_operation import BindOperation
13
12
  from classiq.interface.model.handle_binding import (
14
13
  HandleBinding,
@@ -16,9 +15,6 @@ from classiq.interface.model.handle_binding import (
16
15
  SlicedHandleBinding,
17
16
  SubscriptHandleBinding,
18
17
  )
19
- from classiq.interface.model.quantum_expressions.quantum_expression import (
20
- QuantumExpressionOperation,
21
- )
22
18
  from classiq.interface.model.quantum_type import (
23
19
  QuantumBitvector,
24
20
  QuantumScalar,
@@ -29,27 +25,23 @@ from classiq.interface.model.variable_declaration_statement import (
29
25
  )
30
26
 
31
27
  from classiq.model_expansions.scope import QuantumSymbol, Scope
28
+ from classiq.model_expansions.transformers.model_renamer import (
29
+ HandleRenaming,
30
+ ModelRenamer,
31
+ )
32
32
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
33
33
 
34
- AST_NODE = TypeVar("AST_NODE", bound=NodeType)
35
-
36
34
 
37
35
  @dataclass(frozen=True)
38
- class SymbolPart:
39
- source_handle: HandleBinding
40
- target_var_name: str
36
+ class SymbolPart(HandleRenaming):
41
37
  target_var_type: QuantumType
42
38
 
43
- @property
44
- def target_var_handle(self) -> HandleBinding:
45
- return HandleBinding(name=self.target_var_name)
46
-
47
39
 
48
40
  SymbolParts = dict[HandleBinding, list[SymbolPart]]
49
41
  PartNamer = Callable[[str], str]
50
42
 
51
43
 
52
- class VarSplitter:
44
+ class VarSplitter(ModelRenamer):
53
45
  def __init__(self, scope: Scope):
54
46
  self._scope = scope
55
47
 
@@ -230,70 +222,3 @@ class VarSplitter:
230
222
  )
231
223
  for part in chain.from_iterable(symbol_parts.values())
232
224
  ]
233
-
234
- def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolParts) -> AST_NODE:
235
- if len(symbol_mapping) == 0:
236
- return subject
237
- handle_replacements = {
238
- part.source_handle: part.target_var_handle
239
- for parts in symbol_mapping.values()
240
- for part in parts
241
- }
242
-
243
- class ReplaceSplitVars(Transformer):
244
- @staticmethod
245
- def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
246
- handle = handle.collapse()
247
- for handle_to_replace, replacement in handle_replacements.items():
248
- handle = handle.replace_prefix(handle_to_replace, replacement)
249
- return handle
250
-
251
- @staticmethod
252
- def visit_Expression(expr: Expression) -> Expression:
253
- return self._rewrite_expression(symbol_mapping, expr)
254
-
255
- def visit_QuantumExpressionOperation(
256
- self, op: QuantumExpressionOperation
257
- ) -> QuantumExpressionOperation:
258
- op = cast(QuantumExpressionOperation, self.generic_visit(op))
259
- previous_var_handles = list(op._var_handles)
260
- op._var_handles = self.visit(op._var_handles)
261
- op._var_types = {
262
- new_handle.name: op._var_types.get(
263
- new_handle.name, op._var_types[previous_handle.name]
264
- )
265
- for previous_handle, new_handle in zip(
266
- previous_var_handles, op._var_handles
267
- )
268
- }
269
- return op
270
-
271
- return ReplaceSplitVars().visit(subject)
272
-
273
- def _rewrite_expression(
274
- self,
275
- symbol_mapping: SymbolParts,
276
- expression: Expression,
277
- ) -> Expression:
278
- vrc = VarRefCollector(ignore_duplicated_handles=True)
279
- vrc.visit(ast.parse(expression.expr))
280
-
281
- handle_names = {
282
- part.source_handle: part.target_var_handle
283
- for parts in symbol_mapping.values()
284
- for part in parts
285
- }
286
- new_expr_str = expression.expr
287
- for handle in vrc.var_handles:
288
- new_handle = handle.collapse()
289
- for handle_to_replace, replacement in handle_names.items():
290
- new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
291
- new_expr_str = new_expr_str.replace(str(handle), str(new_handle))
292
- if handle.qmod_expr != str(handle):
293
- new_expr_str = new_expr_str.replace(
294
- handle.qmod_expr, new_handle.qmod_expr
295
- )
296
-
297
- new_expr = Expression(expr=new_expr_str)
298
- new_expr._evaluated_expr = expression._evaluated_expr
299
- return new_expr
@@ -25,14 +25,14 @@ def phase_oracle(
25
25
  Creates a phase oracle operator based on a predicate function.
26
26
 
27
27
  Applies a predicate function and marks "good" and "bad" states with a phase flip.
28
- If the predicate is marked as $\\chi$, and the oracle is marked as $S_\\chi$, then:
28
+ If the predicate is marked as $\\chi$, and the oracle is marked as $S_{\\chi}$, then:
29
29
 
30
30
 
31
31
  $$
32
- S_\\chi\\lvert x \rangle =
33
- \begin{cases}
34
- -\\lvert x \rangle & \text{if } \\chi(x) = 1 \\
35
- \\phantom{-} \\lvert x \rangle & \text{if } \\chi(x) = 0
32
+ S_{\\chi}\\lvert x \\rangle =
33
+ \\begin{cases}
34
+ -\\lvert x \\rangle & \\text{if } \\chi(x) = 1 \\\\
35
+ \\phantom{-} \\lvert x \\rangle & \\text{if } \\chi(x) = 0
36
36
  \\end{cases}
37
37
  $$
38
38
 
@@ -12,7 +12,7 @@ from .enums import * # noqa: F403
12
12
  from .enums import __all__ as _builtin_enums
13
13
  from .functions import * # noqa: F403
14
14
  from .functions import __all__ as _builtin_functions
15
- from .operations import * # type:ignore[assignment] # noqa: F403
15
+ from .operations import * # noqa: F403
16
16
  from .operations import __all__ as _builtin_operations
17
17
  from .structs import * # noqa: F403
18
18
  from .structs import __all__ as _builtin_structs
@@ -58,7 +58,6 @@ CORE_LIB_DECLS = [
58
58
  real_xor_constant,
59
59
  U,
60
60
  CCX,
61
- allocate,
62
61
  free,
63
62
  randomized_benchmarking,
64
63
  inplace_prepare_state,
@@ -103,7 +102,6 @@ __all__ = [ # noqa: RUF022
103
102
  "Y",
104
103
  "Z",
105
104
  "add",
106
- "allocate",
107
105
  "apply",
108
106
  "bloch_sphere_feature_map",
109
107
  "exponentiation_with_depth_constraint",
@@ -1,35 +1,10 @@
1
1
  from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
- from classiq.qmod.qmod_parameter import CArray, CInt, CReal
4
+ from classiq.qmod.qmod_parameter import CArray, CReal
5
5
  from classiq.qmod.qmod_variable import Input, Output, QArray, QBit
6
6
 
7
7
 
8
- @qfunc(external=True)
9
- def allocate(
10
- num_qubits: CInt, out: Output[QArray[QBit, Literal["num_qubits"]]]
11
- ) -> None:
12
- """
13
- [Qmod core-library function]
14
-
15
- Allocates the specified number of qubits to a given quantum variable and initializes
16
- them in the zero state:
17
-
18
- $$
19
- \\left|\\text{out}\\right\\rangle = \\left|0\\right\\rangle^{\\otimes \\text{num_qubits}}
20
- $$
21
-
22
- Args:
23
- num_qubits: The number of qubits to allocate. Must be a positive integer.
24
- out: The quantum variable that will receive the allocated qubits. Must be uninitialized before allocation.
25
-
26
- Notes:
27
- 1. If the output variable has been declared with a specific number of qubits, the number of qubits allocated must match the declared number.
28
- 2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
29
- """
30
- pass
31
-
32
-
33
8
  @qfunc(external=True)
34
9
  def free(in_: Input[QArray[QBit]]) -> None:
35
10
  """
@@ -65,15 +65,21 @@ def allocate(out: Output[QVar]) -> None:
65
65
 
66
66
  def allocate(*args: Any, **kwargs: Any) -> None:
67
67
  """
68
- Initialize a quantum variable.
68
+ Initialize a quantum variable to a new quantum object in the zero state:
69
69
 
70
- If 'num_qubits' is specified, 'num_qubits' qubits will be allocated for variable
71
- 'out'. Otherwise, the number of qubits will be inferred according to the type of
72
- 'out'.
70
+ $$
71
+ \\left|\\text{out}\\right\\rangle = \\left|0\\right\\rangle^{\\otimes \\text{num_qubits}}
72
+ $$
73
+
74
+ If 'num_qubits' is not specified, it will be inferred according to the type of 'out'.
73
75
 
74
76
  Args:
75
- size: The number of qubits to be allocated (optional)
76
- out: The target variable
77
+ num_qubits: The number of qubits to allocate (positive integer, optional).
78
+ out: The quantum variable that will receive the allocated qubits. Must be uninitialized before allocation.
79
+
80
+ Notes:
81
+ 1. If the output variable has been declared with a specific number of qubits, the number of qubits allocated must match the declared number.
82
+ 2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
77
83
  """
78
84
  assert QCallable.CURRENT_EXPANDABLE is not None
79
85
  source_ref = get_source_ref(sys._getframe(1))
@@ -6,10 +6,12 @@ from classiq.interface.exceptions import ClassiqError
6
6
  from classiq.interface.generator.expressions.expression import Expression
7
7
 
8
8
  if TYPE_CHECKING:
9
- from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
9
+ from classiq.model_expansions.interpreters.generative_interpreter import (
10
+ GenerativeInterpreter,
11
+ )
10
12
 
11
13
  _GENERATIVE_MODE: bool = False
12
- _FRONTEND_INTERPRETER: Optional["BaseInterpreter"] = None
14
+ _FRONTEND_INTERPRETER: Optional["GenerativeInterpreter"] = None
13
15
 
14
16
 
15
17
  def is_generative_mode() -> bool:
@@ -27,12 +29,12 @@ def generative_mode_context(generative: bool) -> Iterator[None]:
27
29
  _GENERATIVE_MODE = previous
28
30
 
29
31
 
30
- def set_frontend_interpreter(interpreter: "BaseInterpreter") -> None:
32
+ def set_frontend_interpreter(interpreter: "GenerativeInterpreter") -> None:
31
33
  global _FRONTEND_INTERPRETER
32
34
  _FRONTEND_INTERPRETER = interpreter
33
35
 
34
36
 
35
- def get_frontend_interpreter() -> "BaseInterpreter":
37
+ def get_frontend_interpreter() -> "GenerativeInterpreter":
36
38
  if _FRONTEND_INTERPRETER is None:
37
39
  raise ClassiqError("Interpreter was not set")
38
40
  return _FRONTEND_INTERPRETER
@@ -21,7 +21,7 @@ from classiq.interface.generator.functions.type_name import TypeName
21
21
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
22
22
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
23
23
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
24
- from classiq.interface.generator.visitor import NodeType, Visitor
24
+ from classiq.interface.generator.visitor import NodeType
25
25
  from classiq.interface.model.allocate import Allocate
26
26
  from classiq.interface.model.bind_operation import BindOperation
27
27
  from classiq.interface.model.classical_if import ClassicalIf
@@ -38,6 +38,7 @@ from classiq.interface.model.handle_binding import (
38
38
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
39
39
  from classiq.interface.model.invert import Invert
40
40
  from classiq.interface.model.model import Model
41
+ from classiq.interface.model.model_visitor import ModelVisitor
41
42
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
42
43
  from classiq.interface.model.phase_operation import PhaseOperation
43
44
  from classiq.interface.model.port_declaration import (
@@ -85,7 +86,7 @@ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_c
85
86
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
86
87
 
87
88
 
88
- class DSLPrettyPrinter(Visitor):
89
+ class DSLPrettyPrinter(ModelVisitor):
89
90
  def __init__(
90
91
  self,
91
92
  decimal_precision: Optional[int] = DEFAULT_DECIMAL_PRECISION,
@@ -41,6 +41,7 @@ from classiq.interface.model.handle_binding import (
41
41
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
42
42
  from classiq.interface.model.invert import Invert
43
43
  from classiq.interface.model.model import Model
44
+ from classiq.interface.model.model_visitor import ModelVisitor
44
45
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
45
46
  from classiq.interface.model.phase_operation import PhaseOperation
46
47
  from classiq.interface.model.port_declaration import AnonPortDeclaration
@@ -120,7 +121,7 @@ class VariableDeclarationAssignment(Visitor):
120
121
  return qtype.name, []
121
122
 
122
123
 
123
- class PythonPrettyPrinter(Visitor):
124
+ class PythonPrettyPrinter(ModelVisitor):
124
125
  def __init__(self, decimal_precision: int = DEFAULT_DECIMAL_PRECISION) -> None:
125
126
  self._level = 0
126
127
  self._decimal_precision = decimal_precision
@@ -407,6 +408,7 @@ class PythonPrettyPrinter(Visitor):
407
408
  return ", ".join(self.visit(arg) for arg in func_call.positional_args)
408
409
 
409
410
  def visit_Allocate(self, allocate: Allocate) -> str:
411
+ self._imports["allocate"] = 1
410
412
  if allocate.size is not None:
411
413
  size = f"{self.visit(allocate.size)}, "
412
414
  else:
@@ -22,6 +22,7 @@ from typing_extensions import ParamSpec, Self, _AnnotatedAlias
22
22
 
23
23
  from classiq.interface.exceptions import ClassiqValueError
24
24
  from classiq.interface.generator.expressions.expression import Expression
25
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
25
26
  from classiq.interface.generator.expressions.qmod_qarray_proxy import (
26
27
  ILLEGAL_SLICE_MSG,
27
28
  ILLEGAL_SLICING_STEP_MSG,
@@ -185,6 +186,10 @@ class QVar(Symbolic):
185
186
  return interpret_expression(str(self.size))
186
187
  return CParamScalar(f"get_field({self}, 'size')")
187
188
 
189
+ @property
190
+ def type_name(self) -> str:
191
+ return self.get_qmod_type().type_name
192
+
188
193
 
189
194
  _Q = TypeVar("_Q", bound=QVar)
190
195
  Output = Annotated[_Q, PortDeclarationDirection.Output]
@@ -414,7 +419,7 @@ class QNum(Generic[_P], QScalar):
414
419
  return _GenericAlias(cls, args)
415
420
 
416
421
 
417
- class QArray(ArrayBase[_P], QVar):
422
+ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
418
423
  CONSTRUCTOR_DEPTH: int = 3
419
424
 
420
425
  # TODO [CAD-18620]: improve type hints
@@ -3,8 +3,17 @@ from contextlib import contextmanager
3
3
  from typing import Any
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
- from classiq.interface.generator.visitor import Visitor
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
+ from classiq.interface.model.model_visitor import ModelVisitor
7
15
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
16
+ from classiq.interface.model.port_declaration import PortDeclaration
8
17
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
9
18
  from classiq.interface.model.quantum_function_declaration import (
10
19
  AnonQuantumOperandDeclaration,
@@ -14,17 +23,36 @@ from classiq.interface.model.quantum_function_declaration import (
14
23
  from classiq.interface.model.quantum_lambda_function import (
15
24
  QuantumLambdaFunction,
16
25
  )
26
+ from classiq.interface.model.quantum_type import QuantumBitvector
17
27
 
18
28
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
19
29
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
20
30
  from classiq.qmod.semantics.error_manager import ErrorManager
21
31
  from classiq.qmod.semantics.lambdas import get_renamed_parameters
22
32
 
33
+ ALLOCATE_DECL_FOR_COMPATIBILITY = QuantumFunctionDeclaration(
34
+ name="allocate",
35
+ positional_arg_declarations=[
36
+ ClassicalParameterDeclaration(
37
+ name="num_qubits",
38
+ classical_type=Integer(),
39
+ ),
40
+ PortDeclaration(
41
+ name="out",
42
+ quantum_type=QuantumBitvector(length=Expression(expr="num_qubits")),
43
+ direction=PortDeclarationDirection.Output,
44
+ ),
45
+ ],
46
+ )
47
+
23
48
 
24
49
  def _annotate_function_call_decl(
25
50
  fc: QuantumFunctionCall,
26
51
  function_dict: Mapping[str, QuantumFunctionDeclaration],
27
52
  ) -> None:
53
+ if fc.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
54
+ fc.set_func_decl(ALLOCATE_DECL_FOR_COMPATIBILITY)
55
+ return
28
56
  if fc._func_decl is None:
29
57
  func_decl = function_dict.get(fc.func_name)
30
58
  if func_decl is None:
@@ -46,7 +74,7 @@ def _annotate_function_call_decl(
46
74
  qlambda.set_op_decl(param)
47
75
 
48
76
 
49
- class _CallLambdaAnnotator(Visitor):
77
+ class _CallLambdaAnnotator(ModelVisitor):
50
78
  def __init__(
51
79
  self, quantum_functions: Mapping[str, QuantumFunctionDeclaration]
52
80
  ) -> None:
@@ -1,10 +1,10 @@
1
1
  from classiq.interface.generator.functions.type_name import TypeName
2
- from classiq.interface.generator.visitor import Visitor
2
+ from classiq.interface.model.model_visitor import ModelVisitor
3
3
 
4
4
  from classiq.qmod.model_state_container import QMODULE
5
5
 
6
6
 
7
- class QStructAnnotator(Visitor):
7
+ class QStructAnnotator(ModelVisitor):
8
8
  def __init__(self) -> None:
9
9
  self._visited: set[TypeName] = set()
10
10