classiq 0.69.0__py3-none-any.whl → 0.71.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 (97) hide show
  1. classiq/analyzer/analyzer.py +0 -18
  2. classiq/analyzer/url_utils.py +9 -4
  3. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/backend/quantum_backend_providers.py +6 -0
  6. classiq/interface/chemistry/operator.py +1 -21
  7. classiq/interface/debug_info/debug_info.py +4 -0
  8. classiq/interface/executor/quantum_instruction_set.py +1 -0
  9. classiq/interface/generator/arith/arithmetic.py +21 -6
  10. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  11. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  12. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  13. classiq/interface/generator/expressions/expression_constants.py +0 -3
  14. classiq/interface/generator/expressions/expression_types.py +12 -4
  15. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  16. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  17. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +79 -0
  18. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  19. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  20. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +35 -0
  21. classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
  22. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  23. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  24. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  25. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  26. classiq/interface/generator/functions/classical_type.py +24 -30
  27. classiq/interface/generator/functions/type_name.py +42 -2
  28. classiq/interface/generator/functions/type_qualifier.py +7 -0
  29. classiq/interface/generator/generated_circuit_data.py +22 -4
  30. classiq/interface/generator/model/preferences/preferences.py +1 -0
  31. classiq/interface/generator/quantum_function_call.py +8 -1
  32. classiq/interface/generator/quantum_program.py +0 -1
  33. classiq/interface/generator/synthesis_execution_parameter.py +1 -0
  34. classiq/interface/generator/types/compilation_metadata.py +1 -0
  35. classiq/interface/ide/visual_model.py +1 -0
  36. classiq/interface/interface_version.py +1 -1
  37. classiq/interface/model/allocate.py +7 -0
  38. classiq/interface/model/block.py +12 -0
  39. classiq/interface/model/classical_if.py +4 -0
  40. classiq/interface/model/inplace_binary_operation.py +4 -0
  41. classiq/interface/model/model.py +3 -1
  42. classiq/interface/model/native_function_definition.py +0 -10
  43. classiq/interface/model/phase_operation.py +4 -0
  44. classiq/interface/model/port_declaration.py +3 -0
  45. classiq/interface/model/power.py +4 -0
  46. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
  47. classiq/interface/model/quantum_function_call.py +4 -0
  48. classiq/interface/model/quantum_function_declaration.py +1 -1
  49. classiq/interface/model/quantum_statement.py +5 -0
  50. classiq/interface/model/quantum_type.py +37 -3
  51. classiq/interface/model/repeat.py +4 -0
  52. classiq/interface/model/statement_block.py +3 -0
  53. classiq/interface/model/variable_declaration_statement.py +5 -0
  54. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  55. classiq/model_expansions/capturing/captured_vars.py +156 -34
  56. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  57. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  58. classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
  59. classiq/model_expansions/evaluators/control.py +1 -1
  60. classiq/model_expansions/evaluators/parameter_types.py +72 -16
  61. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  62. classiq/model_expansions/expression_evaluator.py +3 -12
  63. classiq/model_expansions/function_builder.py +2 -8
  64. classiq/model_expansions/generative_functions.py +39 -3
  65. classiq/model_expansions/interpreters/base_interpreter.py +3 -4
  66. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  67. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  68. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  69. classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
  70. classiq/model_expansions/quantum_operations/emitter.py +41 -0
  71. classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
  72. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  73. classiq/model_expansions/scope.py +7 -14
  74. classiq/model_expansions/scope_initialization.py +32 -39
  75. classiq/model_expansions/transformers/model_renamer.py +13 -4
  76. classiq/model_expansions/visitors/variable_references.py +8 -4
  77. classiq/open_library/functions/__init__.py +2 -0
  78. classiq/open_library/functions/lookup_table.py +58 -0
  79. classiq/qmod/__init__.py +3 -1
  80. classiq/qmod/declaration_inferrer.py +55 -25
  81. classiq/qmod/native/pretty_printer.py +25 -3
  82. classiq/qmod/pretty_print/pretty_printer.py +31 -14
  83. classiq/qmod/python_classical_type.py +12 -1
  84. classiq/qmod/qfunc.py +33 -8
  85. classiq/qmod/qmod_parameter.py +8 -0
  86. classiq/qmod/qmod_variable.py +189 -151
  87. classiq/qmod/quantum_function.py +3 -4
  88. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  89. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  90. classiq/qmod/semantics/validation/main_validation.py +1 -1
  91. classiq/qmod/semantics/validation/type_hints.py +38 -0
  92. classiq/qmod/utilities.py +38 -1
  93. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/METADATA +10 -12
  94. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/RECORD +97 -82
  95. {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +1 -1
  96. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  97. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -1,5 +1,4 @@
1
1
  import ast
2
- import re
3
2
  from collections.abc import Mapping
4
3
  from enum import EnumMeta
5
4
  from typing import Any, Optional
@@ -12,12 +11,11 @@ from classiq.interface.generator.expressions.evaluated_expression import (
12
11
  EvaluatedExpression,
13
12
  )
14
13
  from classiq.interface.generator.expressions.expression import Expression
15
- from classiq.interface.generator.expressions.expression_constants import (
16
- CPARAM_EXECUTION_SUFFIX_PATTERN,
17
- )
18
14
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
19
15
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
20
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
16
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
17
+ QmodSizedProxy,
18
+ )
21
19
  from classiq.interface.generator.expressions.sympy_supported_expressions import (
22
20
  SYMPY_SUPPORTED_EXPRESSIONS,
23
21
  )
@@ -113,13 +111,6 @@ def _validate_undefined_vars(
113
111
  - uninitialized_locals
114
112
  )
115
113
 
116
- # Ignore expanded execution parameters
117
- undefined_vars = {
118
- var
119
- for var in undefined_vars
120
- if not re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, var)
121
- }
122
-
123
114
  if len(undefined_vars) == 1:
124
115
  undefined_var = undefined_vars.__iter__().__next__()
125
116
  raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
@@ -15,7 +15,6 @@ from classiq.interface.model.model import MAIN_FUNCTION_NAME
15
15
  from classiq.interface.model.native_function_definition import (
16
16
  NativeFunctionDefinition,
17
17
  )
18
- from classiq.interface.model.port_declaration import PortDeclaration
19
18
  from classiq.interface.model.quantum_function_declaration import (
20
19
  PositionalArg,
21
20
  )
@@ -210,19 +209,14 @@ class OperationBuilder:
210
209
  self._current_source_ref = previous_source_ref
211
210
 
212
211
  def create_definition(
213
- self, function_context: FunctionContext
212
+ self, function_context: FunctionContext, params: Sequence[PositionalArg]
214
213
  ) -> NativeFunctionDefinition:
215
214
  name = self._get_expanded_function_name(function_context)
216
- new_parameters: list[PortDeclaration] = [
217
- param
218
- for param in function_context.positional_arg_declarations
219
- if isinstance(param, PortDeclaration)
220
- ]
221
215
 
222
216
  return NativeFunctionDefinition(
223
217
  name=name,
224
218
  body=function_context.body,
225
- positional_arg_declarations=new_parameters,
219
+ positional_arg_declarations=params,
226
220
  )
227
221
 
228
222
  def _get_expanded_function_name(self, function_context: FunctionContext) -> str:
@@ -1,9 +1,20 @@
1
1
  from collections.abc import Mapping
2
+ from sys import exc_info
3
+ from types import TracebackType
2
4
  from typing import TYPE_CHECKING, Any
3
5
 
4
- from classiq.interface.generator.expressions.qmod_struct_instance import (
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ )
9
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
+ ClassicalProxy,
11
+ )
12
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
5
13
  QmodStructInstance,
6
14
  )
15
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
16
+ get_proxy_type,
17
+ )
7
18
  from classiq.interface.generator.functions.type_name import Struct
8
19
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
9
20
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
@@ -22,7 +33,7 @@ from classiq.model_expansions.closure import (
22
33
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol
23
34
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
24
35
  from classiq.qmod.model_state_container import QMODULE
25
- from classiq.qmod.qmod_parameter import CParamStruct
36
+ from classiq.qmod.qmod_parameter import CParamStruct, create_param
26
37
  from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
27
38
  from classiq.qmod.quantum_expandable import (
28
39
  QTerminalCallable,
@@ -37,6 +48,23 @@ if TYPE_CHECKING:
37
48
  )
38
49
 
39
50
 
51
+ def _unwrap_traceback_frame(e: Exception) -> Exception:
52
+ fallback_error = ClassiqExpansionError(str(e))
53
+ traceback = exc_info()[2]
54
+ if traceback is None:
55
+ return fallback_error
56
+ back_frame = traceback.tb_frame.f_back
57
+ if back_frame is None:
58
+ return fallback_error
59
+ back_tb = TracebackType(
60
+ tb_next=None,
61
+ tb_frame=back_frame,
62
+ tb_lasti=back_frame.f_lasti,
63
+ tb_lineno=back_frame.f_lineno,
64
+ )
65
+ return e.with_traceback(back_tb)
66
+
67
+
40
68
  class LenList(list):
41
69
  @property
42
70
  def len(self) -> int:
@@ -45,7 +73,10 @@ class LenList(list):
45
73
  def __getitem__(self, item: Any) -> Any:
46
74
  if isinstance(item, QNum):
47
75
  return SymbolicExpr(f"{self}[{item}]", True)
48
- return super().__getitem__(item)
76
+ try:
77
+ return super().__getitem__(item)
78
+ except (IndexError, TypeError) as e:
79
+ raise _unwrap_traceback_frame(e) from None
49
80
 
50
81
  @classmethod
51
82
  def wrap(cls, obj: Any) -> Any:
@@ -90,6 +121,11 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
90
121
  struct_type=Struct(name=classical_value.struct_declaration.name),
91
122
  qmodule=QMODULE,
92
123
  )
124
+ if isinstance(classical_value, ClassicalProxy):
125
+ return create_param(
126
+ str(classical_value.handle), get_proxy_type(classical_value), QMODULE
127
+ )
128
+
93
129
  return LenList.wrap(classical_value)
94
130
 
95
131
 
@@ -52,7 +52,6 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
52
52
  from classiq.model_expansions.scope_initialization import (
53
53
  add_entry_point_params_to_scope,
54
54
  init_builtin_types,
55
- init_exec_params,
56
55
  init_top_level_scope,
57
56
  )
58
57
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
@@ -77,7 +76,6 @@ class BaseInterpreter:
77
76
 
78
77
  init_builtin_types()
79
78
  init_top_level_scope(model, self._top_level_scope)
80
- self._exec_params = init_exec_params(model, self._top_level_scope)
81
79
  self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
82
80
  self._model.functions_compilation_metadata
83
81
  )
@@ -96,7 +94,9 @@ class BaseInterpreter:
96
94
  )
97
95
  context = self._expand_operation(main_closure)
98
96
  self._expanded_functions[main_closure.closure_id] = (
99
- self._builder.create_definition(cast(FunctionContext, context))
97
+ self._builder.create_definition(
98
+ cast(FunctionContext, context), main_closure.positional_arg_declarations
99
+ )
100
100
  )
101
101
 
102
102
  def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
@@ -139,7 +139,6 @@ class BaseInterpreter:
139
139
  qstructs=list(QMODULE.qstruct_decls.values()),
140
140
  debug_info=self._model.debug_info,
141
141
  functions_compilation_metadata=self._expanded_functions_compilation_metadata,
142
- execution_parameters=self._exec_params,
143
142
  )
144
143
 
145
144
  def process_exception(self, e: Exception) -> None:
@@ -0,0 +1,60 @@
1
+ from classiq.interface.exceptions import ClassiqValueError
2
+ from classiq.interface.generator.arith.arithmetic import is_bool
3
+ from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
5
+ ArithmeticOperation,
6
+ ArithmeticOperationKind,
7
+ )
8
+ from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
9
+
10
+ from classiq.model_expansions.scope import QuantumSymbol
11
+
12
+
13
+ def validate_assignment_bool_expression(
14
+ result_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
15
+ ) -> None:
16
+ if not is_bool(expr):
17
+ return
18
+ _validate_target_type(result_symbol, expr, op_kind)
19
+
20
+
21
+ def _validate_target_type(
22
+ target_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
23
+ ) -> None:
24
+ supported_types = _supported_types()
25
+ if target_symbol.quantum_type.qmod_type_name not in supported_types:
26
+ raise ClassiqValueError(
27
+ f'The expression has been evaluated to "{expr}" which is a Boolean value. '
28
+ f"Cannot perform {op_kind.value} operation of Boolean expression to result variable '{target_symbol.handle}' of type {target_symbol.quantum_type.qmod_type_name}. "
29
+ f"Boolean expressions can only be applied on {' or '.join(supported_types)}."
30
+ )
31
+
32
+
33
+ def convert_assignment_bool_expression(op: ArithmeticOperation) -> None:
34
+ if not is_bool(op.expression.expr):
35
+ return
36
+ op.expression = op.expression.model_copy(
37
+ update=dict(expr="1" if op.expression.expr == "True" else "0")
38
+ )
39
+
40
+
41
+ def convert_inplace_op_bool_expression(
42
+ op: ArithmeticOperation, target: QuantumSymbol
43
+ ) -> None:
44
+ if not is_bool(op.expression.expr):
45
+ return
46
+ _validate_target_type(target, op.expression.expr, op.operation_kind)
47
+ op.expression = op.expression.model_copy(
48
+ update=dict(expr="1" if op.expression.expr == "True" else "0")
49
+ )
50
+
51
+
52
+ def _supported_types() -> tuple[str, ...]:
53
+ return (
54
+ QuantumBit().qmod_type_name,
55
+ QuantumNumeric(
56
+ size=Expression(expr="1"),
57
+ is_signed=Expression(expr="False"),
58
+ fraction_digits=Expression(expr="0"),
59
+ ).qmod_type_name,
60
+ )
@@ -11,6 +11,10 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
11
11
  )
12
12
 
13
13
  from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
14
+ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
15
+ convert_assignment_bool_expression,
16
+ validate_assignment_bool_expression,
17
+ )
14
18
  from classiq.model_expansions.quantum_operations.emitter import Emitter
15
19
  from classiq.model_expansions.scope import QuantumSymbol
16
20
  from classiq.model_expansions.transformers.ast_renamer import rename_variables
@@ -24,6 +28,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
24
28
  ):
25
29
  direction = PortDeclarationDirection.Output
26
30
  self._update_result_type(op)
31
+ convert_assignment_bool_expression(op)
27
32
  else:
28
33
  direction = PortDeclarationDirection.Inout
29
34
  self._capture_handle(op.result_var, direction)
@@ -47,6 +52,10 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
47
52
  self._machine_precision,
48
53
  )
49
54
  result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
55
+
56
+ validate_assignment_bool_expression(
57
+ result_symbol, op.expression.expr, op.operation_kind
58
+ ) # must be here, otherwise copy_type_information will throw a non-indicative error
50
59
  copy_type_information(
51
60
  result_type, result_symbol.quantum_type, str(op.result_var)
52
61
  )
@@ -2,17 +2,29 @@ from collections.abc import Sequence
2
2
  from itertools import chain, combinations
3
3
  from typing import (
4
4
  TYPE_CHECKING,
5
+ Any,
5
6
  Generic,
6
7
  cast,
7
8
  )
8
9
  from uuid import UUID
9
10
 
11
+ import sympy
12
+
10
13
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
14
  from classiq.interface.exceptions import ClassiqExpansionError
15
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
16
+ ClassicalProxy,
17
+ )
18
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
19
+ QmodStructInstance,
20
+ )
12
21
  from classiq.interface.generator.functions.port_declaration import (
13
22
  PortDeclarationDirection,
14
23
  )
15
24
  from classiq.interface.generator.generated_circuit_data import OperationLevel
25
+ from classiq.interface.model.classical_parameter_declaration import (
26
+ ClassicalParameterDeclaration,
27
+ )
16
28
  from classiq.interface.model.handle_binding import HandleBinding
17
29
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
18
30
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -75,6 +87,16 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
75
87
  )
76
88
 
77
89
 
90
+ def _is_symbolic(arg: Any) -> bool:
91
+ if isinstance(arg, list):
92
+ return any(_is_symbolic(item) for item in arg)
93
+ if isinstance(arg, QmodStructInstance):
94
+ return any(_is_symbolic(item) for item in arg.fields.values())
95
+ if isinstance(arg, sympy.Basic):
96
+ return len(arg.free_symbols) > 0
97
+ return isinstance(arg, ClassicalProxy)
98
+
99
+
78
100
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
79
101
  def __init__(self, interpreter: "BaseInterpreter") -> None:
80
102
  Emitter.__init__(self, interpreter)
@@ -197,7 +219,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
197
219
  ].occurrences_number += 1
198
220
  return function_def
199
221
 
200
- function_def = self._create_function_definition(function_context)
222
+ function_def = self._create_function_definition(function_context, args)
201
223
  self._expanded_functions[closure_id] = function_def
202
224
  self._top_level_scope[function_def.name] = Evaluated(
203
225
  value=function_context.closure.with_new_declaration(function_def)
@@ -210,14 +232,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
210
232
  return function_def
211
233
 
212
234
  def _create_function_definition(
213
- self, function_context: FunctionContext
235
+ self, function_context: FunctionContext, args: list[Evaluated]
214
236
  ) -> NativeFunctionDefinition:
215
- func_def = self._builder.create_definition(function_context)
237
+ params = [
238
+ param
239
+ for arg, param in zip(args, function_context.positional_arg_declarations)
240
+ if isinstance(param, PortDeclaration)
241
+ or (
242
+ isinstance(param, ClassicalParameterDeclaration)
243
+ and _is_symbolic(arg.value)
244
+ )
245
+ ]
246
+ func_def = self._builder.create_definition(function_context, params)
216
247
 
217
248
  captured_vars = function_context.closure.captured_vars.filter_vars(
218
249
  function_context.closure
219
250
  )
220
- captured_ports = captured_vars.get_captured_ports()
251
+ captured_ports = captured_vars.get_captured_parameters()
221
252
  if len(captured_ports) == 0:
222
253
  return func_def
223
254
  func_def.positional_arg_declarations = list(
@@ -239,15 +270,22 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
239
270
  closure: FunctionClosure,
240
271
  ) -> None:
241
272
  for parameter, argument in zip(parameters, arguments):
273
+ param_handle = HandleBinding(name=parameter.name)
242
274
  if isinstance(argument.value, QuantumSymbol):
243
275
  assert isinstance(parameter, PortDeclaration)
244
276
  closure.scope[parameter.name] = Evaluated(
245
277
  QuantumSymbol(
246
- handle=HandleBinding(name=parameter.name),
278
+ handle=param_handle,
247
279
  quantum_type=parameter.quantum_type,
248
280
  ),
249
281
  defining_function=closure,
250
282
  )
283
+ elif _is_symbolic(argument.value):
284
+ assert isinstance(parameter, ClassicalParameterDeclaration)
285
+ closure.scope[parameter.name] = Evaluated(
286
+ value=parameter.classical_type.get_classical_proxy(param_handle),
287
+ defining_function=closure,
288
+ )
251
289
  else:
252
290
  closure.scope[parameter.name] = argument
253
291
 
@@ -264,7 +302,9 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
264
302
  return [arg.emit() for arg in evaluated_args]
265
303
 
266
304
  positional_args = [
267
- arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
305
+ arg.emit()
306
+ for arg in evaluated_args
307
+ if isinstance(arg.value, QuantumSymbol) or _is_symbolic(arg.value)
268
308
  ]
269
309
 
270
310
  return positional_args
@@ -19,6 +19,13 @@ from classiq.interface.generator.expressions.evaluated_expression import (
19
19
  EvaluatedExpression,
20
20
  )
21
21
  from classiq.interface.generator.expressions.expression import Expression
22
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
23
+ ClassicalProxy,
24
+ )
25
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
26
+ get_proxy_type,
27
+ )
28
+ from classiq.interface.generator.functions.classical_type import ClassicalType
22
29
  from classiq.interface.generator.functions.port_declaration import (
23
30
  PortDeclarationDirection,
24
31
  )
@@ -145,6 +152,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
145
152
  return new_expression
146
153
 
147
154
  def emit_statement(self, statement: QuantumStatement) -> None:
155
+ self._update_captured_classical_vars(statement)
148
156
  if isinstance(statement, QuantumOperation):
149
157
  self._update_captured_vars(statement)
150
158
  if statement.uuid not in self._interpreter._model.debug_info:
@@ -153,6 +161,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
153
161
  )
154
162
  self._builder.emit_statement(statement)
155
163
 
164
+ def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
165
+ for expr in stmt.expressions:
166
+ for var_name, var_type in self._get_classical_vars_in_expression(expr):
167
+ self._capture_classical_var(var_name, var_type)
168
+
156
169
  def _update_captured_vars(self, op: QuantumOperation) -> None:
157
170
  handles = (
158
171
  [(handle, PortDeclarationDirection.Input) for handle in op.inputs]
@@ -178,6 +191,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
178
191
  direction=direction,
179
192
  )
180
193
 
194
+ def _capture_classical_var(self, var_name: str, var_type: ClassicalType) -> None:
195
+ if var_name not in self._current_scope:
196
+ return
197
+ defining_function = self._current_scope[var_name].defining_function
198
+ if defining_function is None:
199
+ raise ClassiqInternalExpansionError
200
+ self._builder.current_block.captured_vars.capture_classical_var(
201
+ var_name=var_name,
202
+ var_type=var_type,
203
+ defining_function=defining_function,
204
+ )
205
+
181
206
  def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
182
207
  vrc = VarRefCollector(ignore_duplicated_handles=True)
183
208
  vrc.visit(ast.parse(expr.expr))
@@ -187,3 +212,19 @@ class Emitter(Generic[QuantumStatementT], ABC):
187
212
  if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
188
213
  )
189
214
  return [self._interpreter.evaluate(handle).value for handle in handles]
215
+
216
+ def _get_classical_vars_in_expression(
217
+ self, expr: Expression
218
+ ) -> list[tuple[str, ClassicalType]]:
219
+ vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
220
+ vrc.visit(ast.parse(expr.expr))
221
+ return list(
222
+ {
223
+ handle.name: get_proxy_type(proxy)
224
+ for handle in vrc.var_handles
225
+ if handle.name in self._current_scope
226
+ and isinstance(
227
+ proxy := self._current_scope[handle.name].value, ClassicalProxy
228
+ )
229
+ }.items()
230
+ )
@@ -26,6 +26,10 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
26
26
  )
27
27
  for symbol in self._get_symbols_in_expression(evaluated_expression):
28
28
  self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
29
+ for var_name, var_type in self._get_classical_vars_in_expression(
30
+ evaluated_expression
31
+ ):
32
+ self._capture_classical_var(var_name, var_type)
29
33
  op = op.model_copy(
30
34
  update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
31
35
  )
@@ -1,9 +1,5 @@
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
7
3
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
8
4
 
9
5
  from classiq.model_expansions.closure import FunctionClosure
@@ -17,21 +13,12 @@ if TYPE_CHECKING:
17
13
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
18
14
 
19
15
 
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
-
26
16
  class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
27
17
  def __init__(self, interpreter: "BaseInterpreter") -> None:
28
18
  super().__init__(interpreter)
29
19
  self._model = self._interpreter._model
30
20
 
31
21
  def emit(self, call: QuantumFunctionCall, /) -> bool:
32
- if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
33
- self._allocate_compatibility(call)
34
- return True
35
22
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
36
23
  args = call.positional_args
37
24
  with ErrorManager().call(function.name):
@@ -40,15 +27,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
40
27
  )
41
28
  return True
42
29
 
43
- def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
44
- if len(call.positional_args) != 2:
45
- raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
46
- size, target = call.positional_args
47
- if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
48
- raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
49
- allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
50
- self._interpreter.emit_statement(allocate)
51
-
52
30
 
53
31
  class DeclarativeQuantumFunctionCallEmitter(
54
32
  QuantumFunctionCallEmitter, DeclarativeCallEmitter
@@ -1,13 +1,10 @@
1
1
  import itertools
2
- import re
3
2
  from collections import UserDict
4
3
  from collections.abc import Iterator
5
4
  from dataclasses import dataclass
6
5
  from functools import singledispatch
7
6
  from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
8
7
 
9
- from sympy import Symbol
10
-
11
8
  from classiq.interface.exceptions import (
12
9
  ClassiqExpansionError,
13
10
  ClassiqInternalExpansionError,
@@ -16,10 +13,7 @@ from classiq.interface.generator.expressions.evaluated_expression import (
16
13
  EvaluatedExpression,
17
14
  )
18
15
  from classiq.interface.generator.expressions.expression import Expression
19
- from classiq.interface.generator.expressions.expression_constants import (
20
- CPARAM_EXECUTION_SUFFIX_PATTERN,
21
- )
22
- from classiq.interface.generator.expressions.qmod_struct_instance import (
16
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
23
17
  QmodStructInstance,
24
18
  )
25
19
  from classiq.interface.generator.functions.type_name import TypeName
@@ -72,9 +66,9 @@ class QuantumSymbol:
72
66
  array_length = self.quantum_type.length_value
73
67
  if start < 0 or end > array_length:
74
68
  raise ClassiqExpansionError(
75
- f"Slice [{start}:{end}] is out of bounds of "
76
- f"{self.quantum_type.type_name} {str(self.handle)!r} of length "
77
- f"{array_length}"
69
+ f"Slice [{start}:{end}] is out of bounds for "
70
+ f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
71
+ f"length {array_length})"
78
72
  )
79
73
  return QuantumSymbol(
80
74
  handle=SlicedHandleBinding(
@@ -96,8 +90,9 @@ class QuantumSymbol:
96
90
  array_length = self.quantum_type.length_value
97
91
  if index < 0 or index >= array_length:
98
92
  raise ClassiqExpansionError(
99
- f"Subscript {index} is out of bounds of {self.quantum_type.type_name} "
100
- f"{str(self.handle)!r} of length {array_length}"
93
+ f"Index {index} is out of bounds for "
94
+ f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
95
+ f"length {array_length})"
101
96
  )
102
97
  return QuantumSymbol(
103
98
  handle=SubscriptHandleBinding(
@@ -203,8 +198,6 @@ class Scope(EvaluatedUserDict):
203
198
  return self.data[name]
204
199
  if self._parent is not None:
205
200
  return self._parent[name]
206
- if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
207
- return Evaluated(value=Symbol(name))
208
201
  raise ClassiqExpansionError(f"Variable {name!r} is undefined")
209
202
 
210
203
  def __contains__(self, item: Any) -> bool:
@@ -1,16 +1,20 @@
1
1
  from collections.abc import Sequence
2
- from typing import Any
3
2
 
4
- from classiq.interface.exceptions import ClassiqError
3
+ from classiq.interface.exceptions import ClassiqError, ClassiqInternalExpansionError
5
4
  from classiq.interface.generator.constant import Constant
5
+ from classiq.interface.generator.functions.classical_type import (
6
+ ClassicalArray,
7
+ ClassicalList,
8
+ )
6
9
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
10
+ from classiq.interface.model.classical_parameter_declaration import (
11
+ ClassicalParameterDeclaration,
12
+ )
7
13
  from classiq.interface.model.handle_binding import HandleBinding
8
14
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
9
15
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
10
16
  from classiq.interface.model.port_declaration import PortDeclaration
11
- from classiq.interface.model.quantum_function_declaration import (
12
- PositionalArg,
13
- )
17
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
14
18
 
15
19
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
16
20
  from classiq.model_expansions.evaluators.classical_expression import (
@@ -101,17 +105,24 @@ def add_entry_point_params_to_scope(
101
105
  parameters: Sequence[PositionalArg], main_closure: FunctionClosure
102
106
  ) -> None:
103
107
  for parameter in parameters:
104
- if not isinstance(parameter, PortDeclaration):
105
- continue
106
- main_closure.scope[parameter.name] = Evaluated(
107
- value=QuantumSymbol(
108
- handle=HandleBinding(name=parameter.name),
109
- quantum_type=evaluate_type_in_quantum_symbol(
110
- parameter.quantum_type, main_closure.scope, parameter.name
108
+ if isinstance(parameter, PortDeclaration):
109
+ main_closure.scope[parameter.name] = Evaluated(
110
+ value=QuantumSymbol(
111
+ handle=HandleBinding(name=parameter.name),
112
+ quantum_type=evaluate_type_in_quantum_symbol(
113
+ parameter.quantum_type, main_closure.scope, parameter.name
114
+ ),
111
115
  ),
112
- ),
113
- defining_function=main_closure,
114
- )
116
+ defining_function=main_closure,
117
+ )
118
+ elif isinstance(parameter, ClassicalParameterDeclaration):
119
+ param_val = parameter.classical_type.get_classical_proxy(
120
+ handle=HandleBinding(name=parameter.name)
121
+ )
122
+ main_closure.scope[parameter.name] = Evaluated(
123
+ value=param_val,
124
+ defining_function=main_closure,
125
+ )
115
126
 
116
127
 
117
128
  def init_top_level_scope(model: Model, scope: Scope) -> None:
@@ -125,27 +136,9 @@ def init_builtin_types() -> None:
125
136
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
126
137
 
127
138
 
128
- def _add_exec_param_parts_to_scope(param_val: Any, scope: Scope) -> None:
129
- if not isinstance(param_val, list):
130
- scope[str(param_val)] = Evaluated(value=param_val)
131
- return
132
- for param_part in param_val:
133
- _add_exec_param_parts_to_scope(param_part, scope)
134
-
135
-
136
- def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
137
- if model.execution_parameters is not None:
138
- exec_params = model.execution_parameters
139
- else:
140
- exec_params = {
141
- param.name: param.classical_type
142
- for param in model.function_dict.get(
143
- "_dec_main", model.main_func
144
- ).param_decls
145
- }
146
- for param_name, param_type in exec_params.items():
147
- param_val = param_type.as_symbolic(param_name)
148
- scope[param_name] = Evaluated(value=param_val)
149
- if isinstance(param_val, list):
150
- _add_exec_param_parts_to_scope(param_val, scope)
151
- return exec_params
139
+ def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
140
+ if isinstance(classical_type, ClassicalList):
141
+ raise ClassiqInternalExpansionError("Unexpected classical list")
142
+ if isinstance(classical_type, ClassicalArray):
143
+ return classical_type.size, *_get_shape(classical_type.element_type)
144
+ return ()