classiq 0.65.4__py3-none-any.whl → 0.66.1__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 (62) 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/__init__.py +8 -0
  38. classiq/open_library/functions/amplitude_amplification.py +92 -0
  39. classiq/open_library/functions/grover.py +5 -5
  40. classiq/qmod/builtins/__init__.py +1 -1
  41. classiq/qmod/builtins/functions/__init__.py +0 -2
  42. classiq/qmod/builtins/functions/allocation.py +1 -26
  43. classiq/qmod/builtins/operations.py +12 -6
  44. classiq/qmod/generative.py +6 -4
  45. classiq/qmod/native/pretty_printer.py +3 -2
  46. classiq/qmod/pretty_print/pretty_printer.py +3 -1
  47. classiq/qmod/qmod_variable.py +6 -1
  48. classiq/qmod/semantics/annotation/call_annotation.py +30 -2
  49. classiq/qmod/semantics/annotation/qstruct_annotator.py +2 -2
  50. classiq/qmod/semantics/error_manager.py +20 -6
  51. classiq/qmod/semantics/static_semantics_visitor.py +3 -40
  52. classiq/qmod/semantics/validation/constants_validation.py +2 -3
  53. classiq/qmod/semantics/validation/function_name_collisions_validation.py +6 -9
  54. classiq/qmod/semantics/validation/main_validation.py +2 -3
  55. classiq/qmod/semantics/validation/model_validation.py +25 -0
  56. classiq/qmod/semantics/validation/signature_validation.py +24 -0
  57. classiq/qmod/semantics/validation/types_validation.py +45 -46
  58. classiq/qmod/utilities.py +12 -0
  59. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/METADATA +1 -1
  60. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/RECORD +61 -56
  61. classiq/model_expansions/expression_renamer.py +0 -76
  62. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/WHEEL +0 -0
@@ -67,7 +67,7 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
67
67
 
68
68
  if quantum_type.has_size_in_bits and quantum_type.size_in_bits != size:
69
69
  raise ClassiqExpansionError(
70
- f"Size mismatch for port {param_name!r} between declared size {quantum_type.size_in_bits} and assigned size {size}"
70
+ f"Size mismatch for variable {param_name!r} between declared size {quantum_type.size_in_bits} and assigned size {size}"
71
71
  )
72
72
 
73
73
  if isinstance(quantum_type, QuantumNumeric):
@@ -79,7 +79,7 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
79
79
  if quantum_type.has_length:
80
80
  if size % quantum_type.length_value != 0:
81
81
  raise ClassiqExpansionError(
82
- f"Size mismatch for port {param_name!r}. Cannot fit {size} "
82
+ f"Size mismatch for variable {param_name!r}. Cannot fit {size} "
83
83
  f"qubits into an array of {quantum_type.length_value} elements."
84
84
  )
85
85
  set_size(
@@ -123,7 +123,7 @@ def set_fraction_digits(
123
123
  and quantum_numeric.fraction_digits_value != fraction_digits
124
124
  ):
125
125
  raise ClassiqExpansionError(
126
- f"Fraction digits mismatch for port {param_name!r} between declared "
126
+ f"Fraction digits mismatch for variable {param_name!r} between declared "
127
127
  f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
128
128
  f"digits {fraction_digits!r}"
129
129
  )
@@ -145,7 +145,7 @@ def set_is_signed(
145
145
  and quantum_numeric.sign_value != is_signed
146
146
  ):
147
147
  raise ClassiqExpansionError(
148
- f"Sign mismatch for port {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
148
+ f"Sign mismatch for variable {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
149
149
  )
150
150
 
151
151
  if not (
@@ -175,7 +175,7 @@ def set_length_by_size(
175
175
 
176
176
  if quantum_array.has_size_in_bits and quantum_array.size_in_bits != size:
177
177
  raise ClassiqExpansionError(
178
- f"Size mismatch for port {param_name!r} between declared size "
178
+ f"Size mismatch for variable {param_name!r} between declared size "
179
179
  f"{quantum_array.size_in_bits} ({quantum_array.length_value} elements of "
180
180
  f"size {quantum_array.element_type.size_in_bits}) and assigned size {size}."
181
181
  )
@@ -188,7 +188,7 @@ def set_length_by_size(
188
188
 
189
189
  if size % element_size != 0:
190
190
  raise ClassiqExpansionError(
191
- f"Size mismatch for port {param_name!r}. Cannot fit elements of type "
191
+ f"Size mismatch for variable {param_name!r}. Cannot fit elements of type "
192
192
  f"{quantum_array.element_type.qmod_type_name} (size {element_size}) into "
193
193
  f"{size} qubits."
194
194
  )
@@ -4,7 +4,7 @@ from collections.abc import Mapping
4
4
  from enum import EnumMeta
5
5
  from typing import Any, Optional
6
6
 
7
- from sympy import sympify
7
+ from sympy import SympifyError, sympify
8
8
 
9
9
  from classiq.interface.exceptions import ClassiqExpansionError
10
10
  from classiq.interface.generator.constant import Constant
@@ -16,6 +16,8 @@ from classiq.interface.generator.expressions.expression_constants import (
16
16
  CPARAM_EXECUTION_SUFFIX_PATTERN,
17
17
  )
18
18
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
19
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
20
+ from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
19
21
  from classiq.interface.generator.expressions.sympy_supported_expressions import (
20
22
  SYMPY_SUPPORTED_EXPRESSIONS,
21
23
  )
@@ -81,6 +83,13 @@ def evaluate(
81
83
  f"{', '.join(e.obj.__members__)}"
82
84
  ) from e
83
85
  raise
86
+ except SympifyError as e:
87
+ expr = e.expr
88
+ if isinstance(expr, QmodSizedProxy) and isinstance(expr, NonSymbolicExpr):
89
+ raise ClassiqExpansionError(
90
+ f"{expr.type_name} {str(expr)!r} does not support arithmetic operations"
91
+ ) from e
92
+ raise
84
93
 
85
94
  return EvaluatedExpression(
86
95
  value=sympy_to_python(sympify_result, locals=model_locals)
@@ -4,18 +4,19 @@ from collections import defaultdict
4
4
  from collections.abc import Sequence
5
5
  from contextlib import nullcontext
6
6
  from functools import singledispatchmethod
7
- from typing import Any, Optional, cast
7
+ from typing import Any, cast
8
8
 
9
9
  import sympy
10
10
  from pydantic import ValidationError
11
11
 
12
+ from classiq.interface.debug_info.debug_info import FunctionDebugInfo
12
13
  from classiq.interface.exceptions import (
13
14
  ClassiqError,
14
15
  ClassiqExpansionError,
15
16
  ClassiqInternalExpansionError,
16
17
  )
17
- from classiq.interface.generator.constant import Constant
18
18
  from classiq.interface.generator.expressions.expression import Expression
19
+ from classiq.interface.generator.generated_circuit_data import OperationLevel
19
20
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
20
21
  from classiq.interface.model.handle_binding import (
21
22
  FieldHandleBinding,
@@ -42,7 +43,6 @@ from classiq.model_expansions.debug_flag import debug_mode
42
43
  from classiq.model_expansions.evaluators.classical_expression import (
43
44
  evaluate_classical_expression,
44
45
  )
45
- from classiq.model_expansions.expression_renamer import ExpressionRenamer
46
46
  from classiq.model_expansions.function_builder import (
47
47
  FunctionContext,
48
48
  OperationBuilder,
@@ -50,10 +50,9 @@ from classiq.model_expansions.function_builder import (
50
50
  )
51
51
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
52
52
  from classiq.model_expansions.scope_initialization import (
53
- add_constants_to_scope,
54
53
  add_entry_point_params_to_scope,
55
- get_main_renamer,
56
54
  init_builtin_types,
55
+ init_exec_params,
57
56
  init_top_level_scope,
58
57
  )
59
58
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
@@ -62,10 +61,12 @@ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
62
61
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
63
62
  from classiq.qmod.model_state_container import QMODULE
64
63
  from classiq.qmod.semantics.error_manager import ErrorManager
64
+ from classiq.qmod.semantics.validation.model_validation import validate_model
65
65
 
66
66
 
67
67
  class BaseInterpreter:
68
68
  def __init__(self, model: Model) -> None:
69
+ validate_model(model)
69
70
  self._model = model
70
71
  self._top_level_scope = Scope()
71
72
  self._counted_name_allocator = CountedNameAllocator()
@@ -76,6 +77,7 @@ class BaseInterpreter:
76
77
 
77
78
  init_builtin_types()
78
79
  init_top_level_scope(model, self._top_level_scope)
80
+ self._exec_params = init_exec_params(model, self._top_level_scope)
79
81
  self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
80
82
  self._model.functions_compilation_metadata
81
83
  )
@@ -85,9 +87,6 @@ class BaseInterpreter:
85
87
  self._counted_name_allocator = CountedNameAllocator()
86
88
  self._error_manager: ErrorManager = ErrorManager()
87
89
 
88
- def get_main_renamer(self) -> Optional[ExpressionRenamer]:
89
- return get_main_renamer(self._get_function_declarations())
90
-
91
90
  def _expand_main_func(self) -> None:
92
91
  main_closure = self._get_main_closure(
93
92
  self._top_level_scope[MAIN_FUNCTION_NAME].value
@@ -105,7 +104,6 @@ class BaseInterpreter:
105
104
  name=main_func.name,
106
105
  positional_arg_declarations=main_func.positional_arg_declarations,
107
106
  scope=Scope(parent=self._top_level_scope),
108
- expr_renamer=self.get_main_renamer(),
109
107
  _depth=0,
110
108
  body=main_func.body,
111
109
  )
@@ -117,12 +115,7 @@ class BaseInterpreter:
117
115
  except Exception as e:
118
116
  if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
119
117
  raise e
120
- if not isinstance(e, (ClassiqError, ValidationError)):
121
- raise ClassiqInternalExpansionError(str(e)) from None
122
- prefix = ""
123
- if not isinstance(e, ClassiqExpansionError):
124
- prefix = f"{type(e).__name__}: "
125
- self._error_manager.add_error(f"{prefix}{e}")
118
+ self.process_exception(e)
126
119
  finally:
127
120
  self._error_manager.report_errors(ClassiqExpansionError)
128
121
 
@@ -146,8 +139,17 @@ class BaseInterpreter:
146
139
  qstructs=list(QMODULE.qstruct_decls.values()),
147
140
  debug_info=self._model.debug_info,
148
141
  functions_compilation_metadata=self._expanded_functions_compilation_metadata,
142
+ execution_parameters=self._exec_params,
149
143
  )
150
144
 
145
+ def process_exception(self, e: Exception) -> None:
146
+ if not isinstance(e, (ClassiqError, ValidationError)):
147
+ raise ClassiqInternalExpansionError(str(e)) from None
148
+ prefix = ""
149
+ if not isinstance(e, ClassiqExpansionError):
150
+ prefix = f"{type(e).__name__}: "
151
+ self._error_manager.add_error(f"{prefix}{e}")
152
+
151
153
  @singledispatchmethod
152
154
  def evaluate(self, expression: Any) -> Evaluated:
153
155
  raise NotImplementedError(f"Cannot evaluate {expression!r}")
@@ -229,6 +231,17 @@ class BaseInterpreter:
229
231
  if source_ref is not None
230
232
  else nullcontext()
231
233
  )
234
+ if statement.uuid not in self._model.debug_info:
235
+ self._model.debug_info[statement.uuid] = FunctionDebugInfo(
236
+ name="",
237
+ parameters={},
238
+ level=OperationLevel.QMOD_STATEMENT,
239
+ statement_type=None,
240
+ is_allocate_or_free=False,
241
+ is_inverse=False,
242
+ port_to_passed_variable_map={},
243
+ node=statement._as_back_ref(),
244
+ )
232
245
  with error_context, self._builder.source_ref_context(source_ref):
233
246
  self.emit(statement)
234
247
 
@@ -258,6 +271,3 @@ class BaseInterpreter:
258
271
  for func in self._top_level_scope.values()
259
272
  if isinstance(func_closure := func.value, FunctionClosure)
260
273
  ]
261
-
262
- def add_constant(self, constant: Constant) -> None:
263
- add_constants_to_scope([constant], self._top_level_scope)
@@ -1,4 +1,11 @@
1
+ import inspect
2
+ import os
3
+
4
+ from pydantic import ValidationError
5
+
6
+ from classiq.interface.exceptions import ClassiqError
1
7
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
8
+ from classiq.interface.source_reference import SourceReference
2
9
 
3
10
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
4
11
  from classiq.model_expansions.interpreters.generative_interpreter import (
@@ -8,6 +15,7 @@ from classiq.model_expansions.quantum_operations.quantum_function_call import (
8
15
  DeclarativeQuantumFunctionCallEmitter,
9
16
  )
10
17
  from classiq.model_expansions.scope import Scope
18
+ from classiq.qmod.model_state_container import QMODULE
11
19
 
12
20
 
13
21
  class FrontendGenerativeInterpreter(GenerativeInterpreter):
@@ -20,9 +28,58 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
20
28
  name=main_func.name,
21
29
  positional_arg_declarations=main_func.positional_arg_declarations,
22
30
  scope=Scope(parent=self._top_level_scope),
23
- expr_renamer=self.get_main_renamer(),
24
31
  _depth=0,
25
32
  generative_blocks={"body": main_func.generative_blocks["body"]},
26
33
  )
27
34
 
28
35
  return super()._get_main_closure(main_func)
36
+
37
+ def process_exception(self, e: Exception) -> None:
38
+ if not isinstance(e, (ClassiqError, ValidationError)):
39
+ frame = inspect.trace()[-1]
40
+ module = inspect.getmodule(frame[0])
41
+ if module is None or not module.__name__.startswith("classiq."):
42
+ file_name = os.path.split(frame.filename)[-1]
43
+ if (
44
+ frame.positions is not None
45
+ and frame.positions.lineno is not None
46
+ and frame.positions.col_offset is not None
47
+ and frame.positions.end_lineno is not None
48
+ and frame.positions.end_col_offset is not None
49
+ ):
50
+ source_ref = SourceReference(
51
+ start_line=frame.positions.lineno - 1,
52
+ start_column=frame.positions.col_offset - 1,
53
+ end_line=frame.positions.end_lineno - 1,
54
+ end_column=frame.positions.end_col_offset - 1,
55
+ file_name=file_name,
56
+ )
57
+ else:
58
+ source_ref = SourceReference(
59
+ start_line=frame.lineno - 1,
60
+ start_column=frame.lineno - 1,
61
+ end_line=-1,
62
+ end_column=-1,
63
+ file_name=file_name,
64
+ )
65
+ e_str = f": {e}" if str(e) else ""
66
+ self._error_manager.add_error(
67
+ f"{type(e).__name__}{e_str}",
68
+ source_ref=source_ref,
69
+ function=frame.function,
70
+ )
71
+ return
72
+
73
+ super().process_exception(e)
74
+
75
+ def add_purely_declarative_function(self, function: FunctionClosure) -> None:
76
+ functions_to_add = [function.name] + QMODULE.function_dependencies[
77
+ function.name
78
+ ]
79
+ for func in functions_to_add:
80
+ if func not in self._expanded_functions and func in QMODULE.native_defs:
81
+ self._expanded_functions[func] = QMODULE.native_defs[func]
82
+ if func in QMODULE.functions_compilation_metadata:
83
+ self._expanded_functions_compilation_metadata[func] = (
84
+ QMODULE.functions_compilation_metadata[func]
85
+ )
@@ -4,6 +4,7 @@ from typing import Any
4
4
  import numpy as np
5
5
  from numpy.random import permutation
6
6
 
7
+ from classiq.interface.generator.constant import Constant
7
8
  from classiq.interface.generator.expressions.expression import Expression
8
9
  from classiq.interface.generator.functions.builtins.internal_operators import (
9
10
  CONTROL_OPERATOR_NAME,
@@ -53,14 +54,16 @@ from classiq.model_expansions.quantum_operations import (
53
54
  RepeatEmitter,
54
55
  VariableDeclarationStatementEmitter,
55
56
  )
57
+ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
56
58
  from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
57
59
  from classiq.model_expansions.scope import Evaluated, Scope
58
60
  from classiq.model_expansions.scope_initialization import (
61
+ add_constants_to_scope,
59
62
  add_functions_to_scope,
60
63
  add_generative_functions_to_scope,
61
64
  )
62
65
  from classiq.qmod.builtins.functions import permute
63
- from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
66
+ from classiq.qmod.model_state_container import ModelStateContainer
64
67
  from classiq.qmod.quantum_function import GenerativeQFunc
65
68
 
66
69
 
@@ -123,7 +126,7 @@ class GenerativeInterpreter(BaseInterpreter):
123
126
 
124
127
  @emit.register
125
128
  def emit_allocate(self, allocate: Allocate) -> None:
126
- ShallowEmitter(self, "allocate", components=["size", "target"]).emit(allocate)
129
+ AllocateEmitter(self).emit(allocate)
127
130
 
128
131
  @emit.register
129
132
  def emit_bind(self, bind: BindOperation) -> None:
@@ -236,14 +239,5 @@ class GenerativeInterpreter(BaseInterpreter):
236
239
  qmodule.functions_compilation_metadata[dec_func_name]
237
240
  )
238
241
 
239
- def add_purely_declarative_function(self, function: FunctionClosure) -> None:
240
- functions_to_add = [function.name] + QMODULE.function_dependencies[
241
- function.name
242
- ]
243
- for func in functions_to_add:
244
- if func not in self._expanded_functions and func in QMODULE.native_defs:
245
- self._expanded_functions[func] = QMODULE.native_defs[func]
246
- if func in QMODULE.functions_compilation_metadata:
247
- self._expanded_functions_compilation_metadata[func] = (
248
- QMODULE.functions_compilation_metadata[func]
249
- )
242
+ def add_constant(self, constant: Constant) -> None:
243
+ add_constants_to_scope([constant], self._top_level_scope)
@@ -0,0 +1,69 @@
1
+ from classiq.interface.debug_info.debug_info import FunctionDebugInfo
2
+ from classiq.interface.exceptions import ClassiqValueError
3
+ from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.generator.generated_circuit_data import OperationLevel
5
+ from classiq.interface.model.allocate import Allocate
6
+ from classiq.interface.model.handle_binding import NestedHandleBinding
7
+ from classiq.interface.model.quantum_type import QuantumBitvector
8
+
9
+ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
10
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
11
+ from classiq.model_expansions.scope import QuantumSymbol
12
+
13
+
14
+ class AllocateEmitter(Emitter[Allocate]):
15
+ def emit(self, allocate: Allocate, /) -> None:
16
+ self._register_debug_info(allocate)
17
+ target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
18
+ QuantumSymbol
19
+ )
20
+
21
+ if isinstance(target.handle, NestedHandleBinding):
22
+ raise ClassiqValueError(
23
+ f"Cannot allocate partial quantum variable {str(target.handle)!r}"
24
+ )
25
+
26
+ size = self._get_var_size(target, allocate.size)
27
+ allocate = allocate.model_copy(
28
+ update=dict(size=Expression(expr=str(size)), target=target.handle)
29
+ )
30
+ self.emit_statement(allocate)
31
+
32
+ def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
33
+ if size is None:
34
+ if not target.quantum_type.is_evaluated:
35
+ raise ClassiqValueError(
36
+ f"Could not infer the size of variable {str(target.handle)!r}"
37
+ )
38
+ return target.quantum_type.size_in_bits
39
+
40
+ size_value = self._interpreter.evaluate(size).value
41
+ if not isinstance(size_value, (int, float)):
42
+ raise ClassiqValueError(
43
+ f"The number of allocated qubits must be an integer. Got "
44
+ f"{str(size_value)!r}"
45
+ )
46
+ size_value = int(size_value)
47
+ copy_type_information(
48
+ QuantumBitvector(length=Expression(expr=str(size_value))),
49
+ target.quantum_type,
50
+ str(target.handle),
51
+ )
52
+ return size_value
53
+
54
+ def _register_debug_info(self, allocate: Allocate) -> None:
55
+ if (
56
+ allocate.uuid in self._debug_info
57
+ and self._debug_info[allocate.uuid].name != ""
58
+ ):
59
+ return
60
+ parameters: dict[str, str] = {}
61
+ if allocate.size is not None:
62
+ parameters["num_qubits"] = allocate.size.expr
63
+ self._debug_info[allocate.uuid] = FunctionDebugInfo(
64
+ name="allocate",
65
+ parameters=parameters,
66
+ level=OperationLevel.QMOD_STATEMENT,
67
+ is_allocate_or_free=True,
68
+ port_to_passed_variable_map={"ARG": str(allocate.target)},
69
+ )
@@ -44,7 +44,10 @@ from classiq.model_expansions.quantum_operations.emitter import (
44
44
  )
45
45
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
46
46
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
47
- from classiq.qmod.builtins.functions import allocate, free
47
+ from classiq.qmod.builtins.functions import free
48
+ from classiq.qmod.semantics.validation.signature_validation import (
49
+ validate_function_signature,
50
+ )
48
51
 
49
52
  if TYPE_CHECKING:
50
53
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
@@ -119,10 +122,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
119
122
  new_call = QuantumFunctionCall(
120
123
  function=new_declaration.name, positional_args=new_positional_args
121
124
  )
122
- is_allocate_or_free = (
123
- new_call.func_name == allocate.func_decl.name
124
- or new_call.func_name == free.func_decl.name
125
- )
125
+ is_allocate_or_free = new_call.func_name == free.func_decl.name
126
126
  parameters = {
127
127
  arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
128
128
  for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
@@ -139,7 +139,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
139
139
  level=OperationLevel.QMOD_FUNCTION_CALL,
140
140
  parameters=(
141
141
  parameters
142
- if propagated_debug_info is None
142
+ if propagated_debug_info is None or propagated_debug_info.name == ""
143
143
  else propagated_debug_info.parameters
144
144
  ),
145
145
  is_allocate_or_free=is_allocate_or_free,
@@ -255,6 +255,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
255
255
  different from the call scope. For example, the former uses r,s and the latter
256
256
  uses p, q.
257
257
  """
258
+ validate_function_signature(function.positional_arg_declarations)
258
259
  # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
259
260
  return NamedParamsQuantumFunctionDeclaration(
260
261
  name=function.name,
@@ -13,17 +13,17 @@ from classiq.model_expansions.scope import Evaluated
13
13
  from classiq.qmod.model_state_container import QMODULE
14
14
 
15
15
  if TYPE_CHECKING:
16
- from classiq.model_expansions.interpreters.generative_interpreter import (
17
- GenerativeInterpreter,
16
+ from classiq.model_expansions.interpreters.frontend_generative_interpreter import (
17
+ FrontendGenerativeInterpreter,
18
18
  )
19
19
 
20
20
 
21
21
  class DeclarativeCallEmitter(
22
22
  Generic[QuantumStatementT], CallEmitter[QuantumStatementT]
23
23
  ):
24
- _interpreter: "GenerativeInterpreter"
24
+ _interpreter: "FrontendGenerativeInterpreter"
25
25
 
26
- def __init__(self, interpreter: "GenerativeInterpreter") -> None:
26
+ def __init__(self, interpreter: "FrontendGenerativeInterpreter") -> None:
27
27
  super().__init__(interpreter)
28
28
 
29
29
  def should_expand_function(
@@ -1,5 +1,4 @@
1
- import ast
2
- from abc import abstractmethod
1
+ from abc import ABC, abstractmethod
3
2
  from typing import (
4
3
  TYPE_CHECKING,
5
4
  Generic,
@@ -37,7 +36,6 @@ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
37
36
  translate_sympy_quantum_expression,
38
37
  )
39
38
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
40
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
41
39
  from classiq.qmod.quantum_function import GenerativeQFunc
42
40
 
43
41
  if TYPE_CHECKING:
@@ -46,7 +44,7 @@ if TYPE_CHECKING:
46
44
  QuantumStatementT = TypeVar("QuantumStatementT", bound=QuantumStatement)
47
45
 
48
46
 
49
- class Emitter(Generic[QuantumStatementT]):
47
+ class Emitter(Generic[QuantumStatementT], ABC):
50
48
  def __init__(self, interpreter: "BaseInterpreter") -> None:
51
49
  self._interpreter = interpreter
52
50
 
@@ -168,14 +166,3 @@ class Emitter(Generic[QuantumStatementT]):
168
166
  defining_function=defining_function,
169
167
  direction=direction,
170
168
  )
171
-
172
- def capture_handles_in_expression(self, expr: Expression) -> None:
173
- vrc = VarRefCollector(ignore_duplicated_handles=True)
174
- vrc.visit(ast.parse(expr.expr))
175
- handles = dict.fromkeys(
176
- handle
177
- for handle in vrc.var_handles
178
- if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
179
- )
180
- for handle in handles:
181
- self._capture_handle(handle, PortDeclarationDirection.Inout)
@@ -1,5 +1,9 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ from classiq.interface.exceptions import ClassiqValueError
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.model.allocate import Allocate
6
+ from classiq.interface.model.handle_binding import HandleBinding
3
7
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
8
 
5
9
  from classiq.model_expansions.closure import FunctionClosure
@@ -13,12 +17,21 @@ if TYPE_CHECKING:
13
17
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
14
18
 
15
19
 
20
+ ALLOCATE_COMPATIBILITY_ERROR_MESSAGE = (
21
+ "'allocate' expects two argument: The number of qubits to allocate (integer) and "
22
+ "the variable to be allocated (quantum variable)"
23
+ )
24
+
25
+
16
26
  class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
17
27
  def __init__(self, interpreter: "BaseInterpreter") -> None:
18
28
  super().__init__(interpreter)
19
29
  self._model = self._interpreter._model
20
30
 
21
31
  def emit(self, call: QuantumFunctionCall, /) -> None:
32
+ if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
33
+ self._allocate_compatibility(call)
34
+ return
22
35
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
23
36
  args = call.positional_args
24
37
  with ErrorManager().call(function.name):
@@ -26,6 +39,15 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
26
39
  function, args, self._debug_info.get(call.uuid)
27
40
  )
28
41
 
42
+ def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
43
+ if len(call.positional_args) != 2:
44
+ raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
45
+ size, target = call.positional_args
46
+ if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
47
+ raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
48
+ allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
49
+ self._interpreter.emit_statement(allocate)
50
+
29
51
 
30
52
  class DeclarativeQuantumFunctionCallEmitter(
31
53
  QuantumFunctionCallEmitter, DeclarativeCallEmitter
@@ -6,7 +6,6 @@ from classiq.interface.generator.expressions.expression import Expression
6
6
  from classiq.interface.generator.functions.port_declaration import (
7
7
  PortDeclarationDirection,
8
8
  )
9
- from classiq.interface.model.allocate import Allocate
10
9
  from classiq.interface.model.handle_binding import HandleBinding
11
10
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
12
11
  ArithmeticOperation,
@@ -18,10 +17,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
18
17
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
19
18
 
20
19
  from classiq.model_expansions.closure import Closure
21
- from classiq.model_expansions.evaluators.quantum_type_utils import (
22
- copy_type_information,
23
- set_size,
24
- )
20
+ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
25
21
  from classiq.model_expansions.quantum_operations.emitter import Emitter
26
22
  from classiq.model_expansions.scope import QuantumSymbol, Scope
27
23
  from classiq.model_expansions.transformers.ast_renamer import rename_variables
@@ -65,7 +61,8 @@ class ShallowEmitter(Emitter[QuantumOperation]):
65
61
  for expression_name in expressions:
66
62
  expression = getattr(op, expression_name)
67
63
  expression = self._evaluate_expression(expression, preserve_bool_ops=True)
68
- self.capture_handles_in_expression(expression)
64
+ for symbol in self._get_symbols_in_expression(expression):
65
+ self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
69
66
  expanded_components[expression_name] = expression
70
67
 
71
68
  for handle_name in handles:
@@ -77,8 +74,6 @@ class ShallowEmitter(Emitter[QuantumOperation]):
77
74
  op = op.model_copy(update=expanded_components)
78
75
  if isinstance(op, QuantumAssignmentOperation):
79
76
  self._post_process_assignment(op)
80
- if isinstance(op, Allocate):
81
- self._post_process_allocate(op)
82
77
  self._builder.emit_statement(op)
83
78
 
84
79
  def _post_process_assignment(self, op: QuantumAssignmentOperation) -> None:
@@ -92,19 +87,6 @@ class ShallowEmitter(Emitter[QuantumOperation]):
92
87
  direction = PortDeclarationDirection.Inout
93
88
  self._capture_handle(op.result_var, direction)
94
89
 
95
- def _post_process_allocate(self, allocate: Allocate) -> None:
96
- target_symbol = self._interpreter.evaluate(allocate.target).value
97
- if not isinstance(target_symbol, QuantumSymbol):
98
- return
99
- self._capture_handle(target_symbol.handle, PortDeclarationDirection.Output)
100
- if allocate.size is None or not allocate.size.is_evaluated():
101
- return
102
- set_size(
103
- target_symbol.quantum_type,
104
- allocate.size.to_int_value(),
105
- str(target_symbol.handle),
106
- )
107
-
108
90
  def _split_components(
109
91
  self, op: QuantumOperation
110
92
  ) -> tuple[list[str], list[str], list[str]]:
@@ -152,25 +134,19 @@ class ShallowEmitter(Emitter[QuantumOperation]):
152
134
  }
153
135
 
154
136
  def _update_result_type(self, op: ArithmeticOperation) -> None:
155
- expr = str(self._evaluate_expression(op.expression))
156
- vrc = VarRefCollector(ignore_duplicated_handles=True)
157
- vrc.visit(ast.parse(expr))
158
- symbols: list[QuantumSymbol] = [
159
- symbol
160
- for handle in vrc.var_handles
161
- if isinstance(
162
- symbol := self._interpreter.evaluate(handle).value, QuantumSymbol
163
- )
164
- ]
165
- expr = rename_variables(
166
- expr,
137
+ expr = self._evaluate_expression(op.expression)
138
+ symbols = self._get_symbols_in_expression(expr)
139
+ expr_str = rename_variables(
140
+ expr.expr,
167
141
  {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
168
142
  | {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
169
143
  )
170
144
  for symbol in symbols:
171
- expr = expr.replace(symbol.handle.qmod_expr, symbol.handle.identifier)
145
+ expr_str = expr_str.replace(
146
+ symbol.handle.qmod_expr, symbol.handle.identifier
147
+ )
172
148
  result_type = compute_arithmetic_result_type(
173
- expr,
149
+ expr_str,
174
150
  {symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
175
151
  self._machine_precision,
176
152
  )
@@ -178,3 +154,13 @@ class ShallowEmitter(Emitter[QuantumOperation]):
178
154
  copy_type_information(
179
155
  result_type, result_symbol.quantum_type, str(op.result_var)
180
156
  )
157
+
158
+ def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
159
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
160
+ vrc.visit(ast.parse(expr.expr))
161
+ handles = dict.fromkeys(
162
+ handle
163
+ for handle in vrc.var_handles
164
+ if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
165
+ )
166
+ return [self._interpreter.evaluate(handle).value for handle in handles]