classiq 0.75.0__py3-none-any.whl → 0.77.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 (101) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +15 -7
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +14 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +16 -2
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +45 -21
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
  26. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
  27. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  28. classiq/interface/generator/functions/classical_type.py +6 -1
  29. classiq/interface/generator/functions/type_name.py +7 -2
  30. classiq/interface/generator/functions/type_qualifier.py +15 -0
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/helpers/backward_compatibility.py +9 -0
  34. classiq/interface/helpers/datastructures.py +6 -0
  35. classiq/interface/model/handle_binding.py +8 -0
  36. classiq/interface/model/model.py +3 -6
  37. classiq/interface/model/port_declaration.py +1 -2
  38. classiq/interface/model/quantum_function_call.py +31 -1
  39. classiq/interface/model/quantum_lambda_function.py +2 -1
  40. classiq/interface/model/quantum_statement.py +14 -1
  41. classiq/interface/server/routes.py +6 -0
  42. classiq/interface/source_reference.py +7 -2
  43. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  44. classiq/model_expansions/capturing/captured_vars.py +18 -6
  45. classiq/model_expansions/closure.py +5 -0
  46. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  47. classiq/model_expansions/evaluators/argument_types.py +3 -3
  48. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  49. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  50. classiq/model_expansions/evaluators/parameter_types.py +45 -24
  51. classiq/model_expansions/expression_evaluator.py +21 -12
  52. classiq/model_expansions/function_builder.py +45 -0
  53. classiq/model_expansions/generative_functions.py +62 -35
  54. classiq/model_expansions/interpreters/base_interpreter.py +32 -7
  55. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
  56. classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
  57. classiq/model_expansions/quantum_operations/allocate.py +8 -3
  58. classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
  59. classiq/model_expansions/quantum_operations/bind.py +54 -30
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  61. classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
  62. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  63. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  64. classiq/model_expansions/quantum_operations/emitter.py +21 -9
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  66. classiq/model_expansions/scope.py +63 -10
  67. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  68. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  69. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  70. classiq/model_expansions/transformers/model_renamer.py +45 -7
  71. classiq/model_expansions/utils/handles_collector.py +1 -1
  72. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  73. classiq/model_expansions/visitors/variable_references.py +45 -9
  74. classiq/open_library/functions/lookup_table.py +1 -1
  75. classiq/open_library/functions/state_preparation.py +1 -1
  76. classiq/qmod/builtins/functions/allocation.py +2 -2
  77. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  78. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  79. classiq/qmod/create_model_function.py +21 -3
  80. classiq/qmod/declaration_inferrer.py +19 -7
  81. classiq/qmod/generative.py +9 -1
  82. classiq/qmod/global_declarative_switch.py +19 -0
  83. classiq/qmod/native/expression_to_qmod.py +4 -0
  84. classiq/qmod/native/pretty_printer.py +12 -3
  85. classiq/qmod/pretty_print/pretty_printer.py +5 -1
  86. classiq/qmod/python_classical_type.py +4 -5
  87. classiq/qmod/qfunc.py +31 -23
  88. classiq/qmod/qmod_constant.py +15 -7
  89. classiq/qmod/qmod_variable.py +7 -1
  90. classiq/qmod/quantum_expandable.py +29 -1
  91. classiq/qmod/quantum_function.py +45 -25
  92. classiq/qmod/semantics/lambdas.py +6 -2
  93. classiq/qmod/semantics/validation/main_validation.py +17 -4
  94. classiq/qmod/symbolic.py +8 -19
  95. classiq/qmod/symbolic_expr.py +26 -0
  96. classiq/qmod/write_qmod.py +36 -10
  97. classiq/synthesis.py +24 -37
  98. classiq/visualization.py +35 -0
  99. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  100. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
  101. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,9 @@
1
1
  from typing import Any, Union
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
5
+ AnyClassicalValue,
6
+ )
4
7
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
5
8
  ClassicalArrayProxy,
6
9
  )
@@ -17,6 +20,7 @@ from classiq.interface.generator.functions.classical_type import (
17
20
  )
18
21
  from classiq.interface.generator.functions.type_name import TypeName
19
22
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
23
+ from classiq.interface.helpers.backward_compatibility import zip_strict
20
24
 
21
25
 
22
26
  def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
@@ -33,7 +37,7 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
33
37
  decl = val.struct_declaration
34
38
  new_fields = {
35
39
  field_name: infer_classical_type(field_val, field_type)
36
- for (field_name, field_val), field_type in zip(
40
+ for (field_name, field_val), field_type in zip_strict(
37
41
  val.fields.items(),
38
42
  decl.variables.values(),
39
43
  strict=True,
@@ -49,13 +53,20 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
49
53
  def _infer_classical_array_type(
50
54
  val: Any, classical_type: Union[ClassicalArray, ClassicalList]
51
55
  ) -> ClassicalType:
52
- if isinstance(val, list):
53
- val_length = len(val)
54
- elif isinstance(val, ClassicalArrayProxy):
56
+ if isinstance(val, ClassicalArrayProxy):
55
57
  val_length = val.length
58
+ elif isinstance(val, list):
59
+ val_length = len(val)
60
+ elif isinstance(val, AnyClassicalValue):
61
+ return classical_type
56
62
  else:
57
63
  raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
58
- if isinstance(classical_type, ClassicalArray) and val_length != classical_type.size:
64
+ if (
65
+ isinstance(classical_type, ClassicalArray)
66
+ and isinstance(val_length, int)
67
+ and isinstance(classical_type.size, int)
68
+ and val_length != classical_type.size
69
+ ):
59
70
  raise ClassiqExpansionError(
60
71
  f"Type mismatch: Argument has {val_length} items but "
61
72
  f"{classical_type.size} expected"
@@ -63,7 +74,7 @@ def _infer_classical_array_type(
63
74
  return ClassicalArray(
64
75
  element_type=(
65
76
  infer_classical_type(val[0], classical_type.element_type)
66
- if val_length > 0
77
+ if not isinstance(val_length, int) or val_length > 0
67
78
  else classical_type.element_type
68
79
  ),
69
80
  size=val_length,
@@ -1,10 +1,15 @@
1
- from typing import TypeVar, Union
1
+ from typing import Optional, TypeVar, Union
2
+
3
+ import sympy
2
4
 
3
5
  from classiq.interface.exceptions import (
4
6
  ClassiqExpansionError,
5
7
  ClassiqInternalExpansionError,
6
8
  )
7
9
  from classiq.interface.generator.expressions.expression import Expression
10
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
11
+ AnyClassicalValue,
12
+ )
8
13
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
9
14
  from classiq.interface.generator.functions.port_declaration import (
10
15
  PortDeclarationDirection,
@@ -15,6 +20,7 @@ from classiq.interface.generator.functions.type_name import (
15
20
  from classiq.interface.model.classical_parameter_declaration import (
16
21
  ClassicalParameterDeclaration,
17
22
  )
23
+ from classiq.interface.model.handle_binding import HandleBinding
18
24
  from classiq.interface.model.port_declaration import PortDeclaration
19
25
  from classiq.interface.model.quantum_function_declaration import (
20
26
  PositionalArg,
@@ -41,7 +47,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
41
47
  set_length,
42
48
  set_size,
43
49
  )
44
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
50
+ from classiq.model_expansions.scope import (
51
+ Evaluated,
52
+ QuantumSymbol,
53
+ QuantumVariable,
54
+ Scope,
55
+ )
45
56
 
46
57
 
47
58
  def evaluate_parameter_types_from_args(
@@ -78,10 +89,10 @@ def _update_scope(
78
89
  return
79
90
  if parameter.direction is PortDeclarationDirection.Output:
80
91
  return
81
- quantum_symbol = argument.as_type(QuantumSymbol)
92
+ quantum_var = argument.as_type(QuantumVariable)
82
93
  casted_argument = _cast(
83
94
  parameter.quantum_type,
84
- quantum_symbol,
95
+ quantum_var.quantum_type,
85
96
  parameter.name,
86
97
  )
87
98
  closure.scope[parameter.name] = Evaluated(
@@ -115,12 +126,12 @@ def _update_operand_signature_environment(
115
126
 
116
127
 
117
128
  def _cast(
118
- quantum_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
129
+ parameter_type: QuantumType, argument_type: QuantumType, param_name: str
119
130
  ) -> QuantumSymbol:
120
- updated_quantum_type = quantum_type.model_copy()
121
- _inject_quantum_arg_info_to_type(updated_quantum_type, quantum_symbol, param_name)
131
+ updated_quantum_type = parameter_type.model_copy()
132
+ _inject_quantum_arg_info_to_type(updated_quantum_type, argument_type, param_name)
122
133
  return QuantumSymbol(
123
- handle=quantum_symbol.handle, quantum_type=updated_quantum_type
134
+ handle=HandleBinding(name=param_name), quantum_type=updated_quantum_type
124
135
  )
125
136
 
126
137
 
@@ -143,7 +154,9 @@ def _evaluate_type_from_arg(
143
154
  )
144
155
  if parameter.direction != PortDeclarationDirection.Output:
145
156
  updated_quantum_type = _inject_quantum_arg_info_to_type(
146
- updated_quantum_type, argument.as_type(QuantumSymbol), parameter.name
157
+ updated_quantum_type,
158
+ argument.as_type(QuantumVariable).quantum_type,
159
+ parameter.name,
147
160
  )
148
161
  return parameter.model_copy(update={"quantum_type": updated_quantum_type})
149
162
 
@@ -178,7 +191,8 @@ def _evaluate_qarray_in_quantum_symbol(
178
191
  "length",
179
192
  param_name,
180
193
  )
181
- set_length(type_to_update, new_length)
194
+ if new_length is not None:
195
+ set_length(type_to_update, new_length)
182
196
  return type_to_update
183
197
 
184
198
 
@@ -194,7 +208,8 @@ def _evaluate_qnum_in_quantum_symbol(
194
208
  "sign",
195
209
  param_name,
196
210
  )
197
- type_to_update.is_signed = Expression(expr=str(new_is_sign))
211
+ if new_is_sign is not None:
212
+ type_to_update.is_signed = Expression(expr=str(new_is_sign))
198
213
  if type_to_update.fraction_digits is not None:
199
214
  new_fraction_digits = _eval_expr(
200
215
  type_to_update.fraction_digits,
@@ -204,7 +219,8 @@ def _evaluate_qnum_in_quantum_symbol(
204
219
  "fraction digits",
205
220
  param_name,
206
221
  )
207
- type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
222
+ if new_fraction_digits is not None:
223
+ type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
208
224
  if type_to_update.size is not None:
209
225
  new_size = _eval_expr(
210
226
  type_to_update.size,
@@ -214,7 +230,8 @@ def _evaluate_qnum_in_quantum_symbol(
214
230
  "size",
215
231
  param_name,
216
232
  )
217
- set_size(type_to_update, new_size, param_name)
233
+ if new_size is not None:
234
+ set_size(type_to_update, new_size, param_name)
218
235
  return type_to_update
219
236
 
220
237
 
@@ -228,17 +245,21 @@ def _eval_expr(
228
245
  type_name: str,
229
246
  attr_name: str,
230
247
  param_name: str,
231
- ) -> _EXPR_TYPE:
248
+ ) -> Optional[_EXPR_TYPE]:
232
249
  val = evaluate_classical_expression(expression, scope).value
233
250
  if expected_type is int and isinstance(val, float):
234
251
  val = int(val)
235
- if not isinstance(val, expected_type):
236
- raise ClassiqExpansionError(
237
- f"When inferring the type of parameter {param_name!r}: "
238
- f"{type_name} {attr_name} must be {expected_type.__name__}, got "
239
- f"{str(val)!r}"
240
- )
241
- return val
252
+ if isinstance(val, expected_type):
253
+ return val
254
+ if isinstance(val, AnyClassicalValue) or (
255
+ isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
256
+ ):
257
+ return None
258
+ raise ClassiqExpansionError(
259
+ f"When inferring the type of parameter {param_name!r}: "
260
+ f"{type_name} {attr_name} must be {expected_type.__name__}, got "
261
+ f"{str(val)!r}"
262
+ )
242
263
 
243
264
 
244
265
  def _evaluate_qstruct_in_quantum_symbol(
@@ -267,11 +288,11 @@ def evaluate_types_in_quantum_symbols(
267
288
 
268
289
 
269
290
  def _inject_quantum_arg_info_to_type(
270
- parameter_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
291
+ parameter_type: QuantumType, argument_type: QuantumType, param_name: str
271
292
  ) -> QuantumType:
272
- if quantum_symbol.quantum_type.has_size_in_bits:
293
+ if argument_type.has_size_in_bits:
273
294
  copy_type_information(
274
- quantum_symbol.quantum_type,
295
+ argument_type,
275
296
  parameter_type,
276
297
  param_name,
277
298
  )
@@ -1,7 +1,7 @@
1
1
  import ast
2
2
  from collections.abc import Mapping
3
3
  from enum import EnumMeta
4
- from typing import Any, Optional
4
+ from typing import Any
5
5
 
6
6
  from sympy import SympifyError, sympify
7
7
 
@@ -50,11 +50,25 @@ def evaluate_constants_as_python(constants: list[Constant]) -> dict[str, Any]:
50
50
  }
51
51
 
52
52
 
53
+ def _quick_eval(expr: str) -> Any:
54
+ try:
55
+ return int(expr)
56
+ except ValueError:
57
+ pass
58
+ try:
59
+ return float(expr)
60
+ except ValueError:
61
+ pass
62
+ return None
63
+
64
+
53
65
  def evaluate(
54
- expr: Expression,
55
- locals_dict: Mapping[str, EvaluatedExpression],
56
- uninitialized_locals: Optional[set[str]] = None,
66
+ expr: Expression, locals_dict: Mapping[str, EvaluatedExpression]
57
67
  ) -> EvaluatedExpression:
68
+ val = _quick_eval(expr.expr)
69
+ if val is not None:
70
+ return EvaluatedExpression(value=val)
71
+
58
72
  model_locals: dict[str, ExpressionValue] = {}
59
73
  model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
60
74
  model_locals.update(
@@ -65,15 +79,14 @@ def evaluate(
65
79
  )
66
80
  # locals override builtin-functions
67
81
  model_locals.update({name: expr.value for name, expr in locals_dict.items()})
68
- uninitialized_locals = uninitialized_locals or set()
69
82
 
70
- _validate_undefined_vars(expr.expr, model_locals, uninitialized_locals)
83
+ _validate_undefined_vars(expr.expr, model_locals)
71
84
 
72
85
  sympy_expr = translate_to_sympy(expr.expr)
73
86
  try:
74
87
  sympify_result = sympify(sympy_expr, locals=model_locals)
75
88
  except (TypeError, IndexError) as e:
76
- raise ClassiqExpansionError(str(e)) from None
89
+ raise ClassiqExpansionError(str(e)) from e
77
90
  except AttributeError as e:
78
91
  if isinstance(e.obj, EnumMeta):
79
92
  raise ClassiqExpansionError(
@@ -95,11 +108,8 @@ def evaluate(
95
108
 
96
109
 
97
110
  def _validate_undefined_vars(
98
- expr: str,
99
- model_locals: dict[str, ExpressionValue],
100
- uninitialized_locals: Optional[set[str]],
111
+ expr: str, model_locals: dict[str, ExpressionValue]
101
112
  ) -> None:
102
- uninitialized_locals = uninitialized_locals or set()
103
113
  id_visitor = _VarsCollector()
104
114
  id_visitor.visit(ast.parse(expr))
105
115
  identifiers = id_visitor.vars
@@ -108,7 +118,6 @@ def _validate_undefined_vars(
108
118
  - model_locals.keys()
109
119
  - set(SYMPY_SUPPORTED_EXPRESSIONS)
110
120
  - set(symbolic.__all__)
111
- - uninitialized_locals
112
121
  )
113
122
 
114
123
  if len(undefined_vars) == 1:
@@ -11,12 +11,15 @@ from classiq.interface.generator.compiler_keywords import (
11
11
  from classiq.interface.generator.functions.builtins.internal_operators import (
12
12
  WITHIN_APPLY_NAME,
13
13
  )
14
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
14
15
  from classiq.interface.model.model import MAIN_FUNCTION_NAME
15
16
  from classiq.interface.model.native_function_definition import (
16
17
  NativeFunctionDefinition,
17
18
  )
19
+ from classiq.interface.model.port_declaration import PortDeclaration
18
20
  from classiq.interface.model.quantum_function_declaration import (
19
21
  PositionalArg,
22
+ QuantumOperandDeclaration,
20
23
  )
21
24
  from classiq.interface.model.quantum_statement import QuantumStatement
22
25
  from classiq.interface.model.variable_declaration_statement import (
@@ -212,6 +215,7 @@ class OperationBuilder:
212
215
  self, function_context: FunctionContext, params: Sequence[PositionalArg]
213
216
  ) -> NativeFunctionDefinition:
214
217
  name = self._get_expanded_function_name(function_context)
218
+ self._override_type_qualifier(function_context, params)
215
219
 
216
220
  return NativeFunctionDefinition(
217
221
  name=name,
@@ -244,3 +248,44 @@ class OperationBuilder:
244
248
  raise ClassiqInternalExpansionError("Could not allocate function name")
245
249
 
246
250
  return name
251
+
252
+ def _override_type_qualifier(
253
+ self, function_context: FunctionContext, params: Sequence[PositionalArg]
254
+ ) -> None:
255
+ """
256
+ The type qualifier can be changed according to the operand passed to the
257
+ function. For example,
258
+ apply_to_all(X, q) --> q will be QFree after expansion
259
+ apply_to_all(H, q) --> q will be Quantum after expansion
260
+ This also holds for the intermediate lambda created during the expansion.
261
+
262
+ We don't override the type qualifier if it's explicitly specified (QFree or
263
+ Const), neither in the function declaration nor in the operand declaration.
264
+ """
265
+
266
+ if function_context.is_lambda:
267
+ self._update_type_qualifiers(params)
268
+ return
269
+
270
+ orig_name = function_context.name
271
+ if orig_name == MAIN_FUNCTION_NAME or orig_name not in self.current_scope:
272
+ return
273
+
274
+ orig_func = self.current_scope[orig_name].value
275
+ if not any(
276
+ isinstance(param_decl, QuantumOperandDeclaration)
277
+ for param_decl in orig_func.positional_arg_declarations
278
+ ):
279
+ return
280
+
281
+ self._update_type_qualifiers(params)
282
+
283
+ @staticmethod
284
+ def _update_type_qualifiers(params: Sequence[PositionalArg]) -> None:
285
+ # only override the qualifier if it's unspecified (not QFree or Const)
286
+ for param in params:
287
+ if (
288
+ isinstance(param, PortDeclaration)
289
+ and param.type_qualifier is TypeQualifier.Quantum
290
+ ):
291
+ param.type_qualifier = TypeQualifier.Inferred
@@ -16,7 +16,7 @@ from classiq.interface.generator.expressions.proxies.classical.utils import (
16
16
  get_proxy_type,
17
17
  )
18
18
  from classiq.interface.generator.functions.type_name import Struct
19
- from classiq.interface.helpers.datastructures import get_sdk_compatible_python_object
19
+ from classiq.interface.helpers.datastructures import LenList
20
20
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
21
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
22
22
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -31,16 +31,18 @@ from classiq.model_expansions.closure import (
31
31
  FunctionClosure,
32
32
  GenerativeClosure,
33
33
  )
34
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol
34
+ from classiq.model_expansions.scope import Evaluated
35
35
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
36
36
  from classiq.qmod.model_state_container import QMODULE
37
37
  from classiq.qmod.qmod_parameter import CParamStruct, create_param
38
- from classiq.qmod.qmod_variable import _create_qvar_for_qtype
38
+ from classiq.qmod.qmod_variable import QScalar, _create_qvar_for_qtype
39
39
  from classiq.qmod.quantum_expandable import (
40
40
  QTerminalCallable,
41
41
  )
42
42
  from classiq.qmod.quantum_function import QFunc
43
43
  from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
44
+ from classiq.qmod.symbolic_expr import SymbolicExpr, SymbolicSubscriptAndField
45
+ from classiq.qmod.utilities import qmod_val_to_expr_str
44
46
 
45
47
  if TYPE_CHECKING:
46
48
  from classiq.model_expansions.interpreters.generative_interpreter import (
@@ -65,36 +67,61 @@ def _unwrap_traceback_frame(e: Exception) -> Exception:
65
67
  return e.with_traceback(back_tb)
66
68
 
67
69
 
68
- def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated) -> Any:
69
- if isinstance(param, PortDeclaration):
70
- quantum_symbol = evaluated.as_type(QuantumSymbol)
71
- return _create_qvar_for_qtype(
72
- quantum_symbol.quantum_type, quantum_symbol.handle
70
+ class SymbolicList(LenList):
71
+ def __getitem__(self, index: Any) -> Any:
72
+ if isinstance(index, (QScalar, SymbolicExpr)) or (
73
+ isinstance(index, slice)
74
+ and any(
75
+ isinstance(slice_part, (QScalar, SymbolicExpr))
76
+ for slice_part in (index.start, index.stop, index.step)
77
+ )
78
+ ):
79
+ return SymbolicSubscriptAndField(
80
+ qmod_val_to_expr_str(self), is_quantum=False
81
+ )[index]
82
+ try:
83
+ return super().__getitem__(index)
84
+ except (IndexError, TypeError) as e:
85
+ raise _unwrap_traceback_frame(e) from None
86
+
87
+
88
+ def translate_classical_ast_arg_to_python_qmod(value: Any) -> Any:
89
+ if isinstance(value, QmodStructInstance):
90
+ return QmodStructInstance(
91
+ value.struct_declaration,
92
+ {
93
+ field_name: translate_classical_ast_arg_to_python_qmod(field_value)
94
+ for field_name, field_value in value.fields.items()
95
+ },
73
96
  )
97
+ if isinstance(value, list):
98
+ return SymbolicList(
99
+ [translate_classical_ast_arg_to_python_qmod(item) for item in value]
100
+ )
101
+ if isinstance(value, ClassicalProxy):
102
+ return create_param(str(value.handle), get_proxy_type(value), QMODULE)
103
+
104
+ return value
105
+
106
+
107
+ def translate_ast_arg_to_python_qmod(param: PositionalArg, value: Any) -> Any:
108
+ if isinstance(param, PortDeclaration):
109
+ return _create_qvar_for_qtype(value.quantum_type, value.handle)
74
110
  if isinstance(param, QuantumOperandDeclaration):
75
111
  if not param.is_list or not param.is_generative:
76
112
  return QTerminalCallable(param)
77
113
  inner_decl = param.model_copy(update={"is_list": False})
78
- func_list: list[FunctionClosure] = evaluated.as_type(list)
79
- return [
80
- QTerminalCallable(inner_decl, index_=idx) for idx in range(len(func_list))
81
- ]
82
- classical_value = evaluated.value
83
- if isinstance(classical_value, QmodStructInstance):
84
- if param.classical_type.is_purely_declarative:
85
- return CParamStruct(
86
- expr=param.name,
87
- struct_type=Struct(name=classical_value.struct_declaration.name),
88
- qmodule=QMODULE,
89
- )
90
- else:
91
- return get_sdk_compatible_python_object(dict(classical_value.fields))
92
- if isinstance(classical_value, ClassicalProxy):
93
- return create_param(
94
- str(classical_value.handle), get_proxy_type(classical_value), QMODULE
114
+ return [QTerminalCallable(inner_decl, index_=idx) for idx in range(len(value))]
115
+ if (
116
+ isinstance(value, QmodStructInstance)
117
+ and param.classical_type.is_purely_declarative
118
+ ):
119
+ classical_type = Struct(name=value.struct_declaration.name)
120
+ classical_type.set_classical_struct_decl(value.struct_declaration)
121
+ return CParamStruct(
122
+ expr=param.name, struct_type=classical_type, qmodule=QMODULE
95
123
  )
96
-
97
- return get_sdk_compatible_python_object(classical_value)
124
+ return translate_classical_ast_arg_to_python_qmod(value)
98
125
 
99
126
 
100
127
  class _InterpreterExpandable(QFunc):
@@ -107,7 +134,7 @@ class _InterpreterExpandable(QFunc):
107
134
  dummy_function = NativeFunctionDefinition(
108
135
  name=current_operation.name,
109
136
  positional_arg_declarations=current_operation.positional_arg_declarations,
110
- body=self._interpreter._builder._current_statements + [stmt],
137
+ body=[stmt],
111
138
  )
112
139
  declarative_functions = {
113
140
  name: func
@@ -140,14 +167,14 @@ class _InterpreterExpandable(QFunc):
140
167
  name=name,
141
168
  positional_arg_declarations=value.positional_arg_declarations,
142
169
  )
143
- elif (
144
- isinstance(value, list)
145
- and len(value) > 0
146
- and isinstance(value[0], FunctionClosure)
147
- ):
170
+ continue
171
+ op_param = self._interpreter._builder.current_function.parameters_dict.get(
172
+ name
173
+ )
174
+ if isinstance(op_param, QuantumOperandDeclaration):
148
175
  scope_func_decls[name] = QuantumFunctionDeclaration(
149
176
  name=name,
150
- positional_arg_declarations=value[0].positional_arg_declarations,
177
+ positional_arg_declarations=op_param.positional_arg_declarations,
151
178
  )
152
179
  return (
153
180
  nameables_to_dict(self._interpreter._get_function_declarations())
@@ -161,7 +188,7 @@ def emit_generative_statements(
161
188
  args: list[Evaluated],
162
189
  ) -> None:
163
190
  python_qmod_args = [
164
- translate_ast_arg_to_python_qmod(param, arg)
191
+ translate_ast_arg_to_python_qmod(param, arg.value)
165
192
  for param, arg in zip(operation.positional_arg_declarations, args)
166
193
  ]
167
194
  with _InterpreterExpandable(interpreter):
@@ -6,7 +6,6 @@ from contextlib import nullcontext
6
6
  from functools import singledispatchmethod
7
7
  from typing import Any, cast
8
8
 
9
- import sympy
10
9
  from pydantic import ValidationError
11
10
 
12
11
  from classiq.interface.debug_info.debug_info import (
@@ -17,11 +16,15 @@ from classiq.interface.exceptions import (
17
16
  ClassiqExpansionError,
18
17
  ClassiqInternalExpansionError,
19
18
  )
19
+ from classiq.interface.generator.expressions.atomic_expression_functions import (
20
+ CLASSICAL_ATTRIBUTES,
21
+ )
20
22
  from classiq.interface.generator.expressions.expression import Expression
21
23
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
22
24
  from classiq.interface.model.handle_binding import (
23
25
  FieldHandleBinding,
24
26
  HandleBinding,
27
+ HandlesList,
25
28
  SlicedHandleBinding,
26
29
  SubscriptHandleBinding,
27
30
  )
@@ -49,7 +52,12 @@ from classiq.model_expansions.function_builder import (
49
52
  OperationBuilder,
50
53
  OperationContext,
51
54
  )
52
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
55
+ from classiq.model_expansions.scope import (
56
+ Evaluated,
57
+ QuantumSymbol,
58
+ QuantumSymbolList,
59
+ Scope,
60
+ )
53
61
  from classiq.model_expansions.scope_initialization import (
54
62
  add_entry_point_params_to_scope,
55
63
  init_builtin_types,
@@ -57,11 +65,13 @@ from classiq.model_expansions.scope_initialization import (
57
65
  )
58
66
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
59
67
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
68
+ from classiq.qmod.builtins.constants import __all__ as builtin_constants
60
69
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
61
70
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
62
71
  from classiq.qmod.model_state_container import QMODULE
63
72
  from classiq.qmod.semantics.error_manager import ErrorManager
64
73
  from classiq.qmod.semantics.validation.model_validation import validate_model
74
+ from classiq.qmod.utilities import qmod_val_to_expr_str
65
75
 
66
76
 
67
77
  class BaseInterpreter:
@@ -126,7 +136,11 @@ class BaseInterpreter:
126
136
  classical_execution_code=self._model.classical_execution_code,
127
137
  execution_preferences=self._model.execution_preferences,
128
138
  functions=list(self._expanded_functions.values()),
129
- constants=self._model.constants,
139
+ constants=[
140
+ const
141
+ for name, const in QMODULE.constants.items()
142
+ if name not in builtin_constants
143
+ ],
130
144
  enums=[
131
145
  enum_decl
132
146
  for name, enum_decl in QMODULE.enum_decls.items()
@@ -157,14 +171,17 @@ class BaseInterpreter:
157
171
  @evaluate.register
158
172
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
159
173
  expr = evaluate_classical_expression(expression, self._builder.current_scope)
160
- if not isinstance(expr.value, sympy.Basic):
161
- return expr
162
174
  vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
163
- vrc.visit(ast.parse(str(expr.value)))
175
+ vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
164
176
  for handle in vrc.var_handles:
165
177
  if handle.name in self._builder.current_scope and isinstance(
166
- self._builder.current_scope[handle.name], QuantumSymbol
178
+ self._builder.current_scope[handle.name].value, QuantumSymbol
167
179
  ):
180
+ if (
181
+ isinstance(handle, FieldHandleBinding)
182
+ and handle.field in CLASSICAL_ATTRIBUTES
183
+ ):
184
+ handle = handle.base_handle
168
185
  self.evaluate(handle)
169
186
  return expr
170
187
 
@@ -222,6 +239,14 @@ class BaseInterpreter:
222
239
  )
223
240
  return Evaluated(value=fields[field_name])
224
241
 
242
+ @evaluate.register
243
+ def evaluate_handles_list(self, handles_list: HandlesList) -> Evaluated:
244
+ return Evaluated(
245
+ value=QuantumSymbolList.from_symbols(
246
+ [self.evaluate(handle).value for handle in handles_list.handles]
247
+ )
248
+ )
249
+
225
250
  @abstractmethod
226
251
  def emit(self, statement: QuantumStatement) -> None:
227
252
  pass
@@ -1,10 +1,12 @@
1
1
  import inspect
2
2
  import os
3
+ from typing import Optional
3
4
 
4
5
  from pydantic import ValidationError
5
6
 
6
7
  from classiq.interface.exceptions import ClassiqError
7
8
  from classiq.interface.model.allocate import Allocate
9
+ from classiq.interface.model.bind_operation import BindOperation
8
10
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
9
11
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
12
  from classiq.interface.model.quantum_function_declaration import (
@@ -16,6 +18,7 @@ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunction
16
18
  from classiq.model_expansions.interpreters.generative_interpreter import (
17
19
  GenerativeInterpreter,
18
20
  )
21
+ from classiq.model_expansions.quantum_operations import BindEmitter
19
22
  from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
20
23
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
21
24
  DeclarativeQuantumFunctionCallEmitter,
@@ -31,15 +34,18 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
31
34
  def infer_symbolic_parameters(
32
35
  self,
33
36
  functions: list[NativeFunctionDefinition],
34
- additional_signatures: (
35
- list[NamedParamsQuantumFunctionDeclaration] | None
36
- ) = None,
37
+ additional_signatures: Optional[
38
+ list[NamedParamsQuantumFunctionDeclaration]
39
+ ] = None,
37
40
  ) -> None:
38
41
  SymbolicParamInference(functions, additional_signatures).infer()
39
42
 
40
43
  def emit_allocate(self, allocate: Allocate) -> None:
41
44
  AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
42
45
 
46
+ def emit_bind(self, bind: BindOperation) -> None:
47
+ BindEmitter(self, allow_symbolic_size=True).emit(bind)
48
+
43
49
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
44
50
  DeclarativeQuantumFunctionCallEmitter(self).emit(call)
45
51