classiq 0.75.0__py3-none-any.whl → 0.76.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 (83) 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 +8 -1
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
  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_qscalar_proxy.py +9 -2
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
  26. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  27. classiq/interface/generator/functions/classical_type.py +6 -1
  28. classiq/interface/generator/functions/type_name.py +7 -2
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/model/preferences/preferences.py +7 -0
  31. classiq/interface/generator/quantum_program.py +5 -19
  32. classiq/interface/helpers/backward_compatibility.py +9 -0
  33. classiq/interface/helpers/datastructures.py +6 -0
  34. classiq/interface/model/port_declaration.py +1 -2
  35. classiq/interface/model/quantum_lambda_function.py +2 -1
  36. classiq/interface/server/routes.py +6 -0
  37. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  38. classiq/model_expansions/capturing/captured_vars.py +2 -0
  39. classiq/model_expansions/closure.py +5 -0
  40. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  41. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  42. classiq/model_expansions/expression_evaluator.py +1 -1
  43. classiq/model_expansions/generative_functions.py +61 -34
  44. classiq/model_expansions/interpreters/base_interpreter.py +17 -6
  45. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +5 -0
  46. classiq/model_expansions/interpreters/generative_interpreter.py +13 -1
  47. classiq/model_expansions/quantum_operations/allocate.py +6 -1
  48. classiq/model_expansions/quantum_operations/assignment_result_processor.py +219 -20
  49. classiq/model_expansions/quantum_operations/bind.py +54 -30
  50. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  51. classiq/model_expansions/quantum_operations/call_emitter.py +14 -7
  52. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  53. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  54. classiq/model_expansions/quantum_operations/emitter.py +20 -3
  55. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  56. classiq/model_expansions/scope.py +10 -7
  57. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  58. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  59. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  60. classiq/model_expansions/transformers/model_renamer.py +45 -7
  61. classiq/model_expansions/utils/handles_collector.py +1 -1
  62. classiq/model_expansions/visitors/variable_references.py +45 -9
  63. classiq/qmod/builtins/functions/allocation.py +2 -2
  64. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  65. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  66. classiq/qmod/declaration_inferrer.py +19 -7
  67. classiq/qmod/generative.py +9 -1
  68. classiq/qmod/native/expression_to_qmod.py +4 -0
  69. classiq/qmod/native/pretty_printer.py +8 -3
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +4 -5
  72. classiq/qmod/qmod_constant.py +15 -7
  73. classiq/qmod/qmod_variable.py +7 -1
  74. classiq/qmod/quantum_function.py +19 -6
  75. classiq/qmod/semantics/lambdas.py +6 -2
  76. classiq/qmod/semantics/validation/main_validation.py +17 -4
  77. classiq/qmod/symbolic.py +8 -19
  78. classiq/qmod/symbolic_expr.py +26 -0
  79. classiq/synthesis.py +17 -31
  80. classiq/visualization.py +35 -0
  81. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  82. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/RECORD +83 -79
  83. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +0 -0
@@ -5,6 +5,7 @@ import pydantic
5
5
  from classiq.interface.ast_node import ASTNode
6
6
  from classiq.interface.exceptions import ClassiqError
7
7
  from classiq.interface.generator.expressions.expression import Expression
8
+ from classiq.interface.helpers.backward_compatibility import zip_strict
8
9
  from classiq.interface.model.quantum_function_declaration import (
9
10
  AnonQuantumOperandDeclaration,
10
11
  )
@@ -56,7 +57,7 @@ class QuantumLambdaFunction(ASTNode):
56
57
  def named_func_decl(self) -> AnonQuantumOperandDeclaration:
57
58
  named_params = [
58
59
  param.rename(rename)
59
- for param, rename in zip(
60
+ for param, rename in zip_strict(
60
61
  self.func_decl.positional_arg_declarations,
61
62
  self.pos_rename_params,
62
63
  strict=False, # strict=False enables lambda keyword args
@@ -6,6 +6,8 @@ CONVERSION_PREFIX = "/conversion"
6
6
  PROVIDERS_PREFIX = "/providers"
7
7
 
8
8
  IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
9
+ USER_BUDGETS_PREFIX = "/user_budgets"
10
+
9
11
 
10
12
  ANALYZER_CIRCUIT_PAGE = "circuit"
11
13
  DEFAULT_IDE_FE_APP = "https://platform.classiq.io/"
@@ -50,6 +52,7 @@ TASK_TEST_SUFFIX = TASKS_SUFFIX + "/test"
50
52
  TASK_PREDICT_SUFFIX = TASKS_SUFFIX + "/predict"
51
53
  TASK_RB_SUFFIX = TASKS_SUFFIX + RB
52
54
  TASKS_GENERATE_FULL_PATH = TASKS_GENERATE_SUFFIX
55
+ TASKS_VISUAL_MODEL_FULL_PATH = ANALYZER_PREFIX + TASKS_VISUAL_MODEL_SUFFIX
53
56
 
54
57
  EXECUTION_JOBS_SUFFIX = "/jobs"
55
58
  EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
@@ -80,3 +83,6 @@ IQCC_LIST_AUTH_METHODS_SUFFIX = "/auth_methods"
80
83
  IQCC_LIST_AUTH_METHODS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_METHODS_SUFFIX
81
84
  IQCC_LIST_AUTH_TARGETS_SUFFIX = "/auth_targets"
82
85
  IQCC_LIST_AUTH_TARGETS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_TARGETS_SUFFIX
86
+
87
+ USER_BUDGETS_SUFFIX = "/all"
88
+ USER_BUDGETS_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGETS_SUFFIX
@@ -2,6 +2,7 @@ from collections.abc import Mapping
2
2
  from enum import Enum
3
3
  from typing import Any, Callable, Union, get_args
4
4
 
5
+ import sympy
5
6
  from sympy import Eq, Expr, Piecewise, Symbol
6
7
 
7
8
  from classiq.interface.exceptions import (
@@ -56,6 +57,8 @@ from classiq.model_expansions.sympy_conversion.arithmetics import (
56
57
  BitwiseOr,
57
58
  BitwiseXor,
58
59
  LogicalXor,
60
+ LShift,
61
+ RShift,
59
62
  )
60
63
  from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
61
64
  MISSING_SLICE_VALUE_PLACEHOLDER,
@@ -63,11 +66,12 @@ from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
63
66
  from classiq.model_expansions.sympy_conversion.sympy_to_python import (
64
67
  sympy_to_python,
65
68
  )
66
- from classiq.model_expansions.utils.sympy_utils import (
67
- is_constant_subscript,
68
- unwrap_sympy_numeric,
69
+ from classiq.model_expansions.utils.sympy_utils import is_constant_subscript
70
+ from classiq.qmod.builtins.classical_functions import (
71
+ __all__ as qmod_classical_functions,
69
72
  )
70
73
  from classiq.qmod.model_state_container import QMODULE
74
+ from classiq.qmod.utilities import qmod_val_to_expr_str
71
75
 
72
76
 
73
77
  def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
@@ -169,19 +173,14 @@ def get_field(
169
173
  ],
170
174
  field: str,
171
175
  ) -> ExpressionValue:
172
- if isinstance(proxy, AnyClassicalValue):
173
- return AnyClassicalValue(f"({proxy}).{field}")
174
- if isinstance(proxy, type) and issubclass(proxy, Enum):
175
- return getattr(proxy, field)
176
- if (
176
+ if isinstance(proxy, AnyClassicalValue) or (
177
177
  isinstance(proxy, Symbol)
178
178
  and not isinstance(proxy, QmodSizedProxy)
179
179
  and not isinstance(proxy, ClassicalProxy)
180
180
  ):
181
- raise ClassiqExpansionError(
182
- f"Cannot evaluate '{proxy}.{field}': Variable {str(proxy)!r} is not "
183
- f"initialized"
184
- )
181
+ return AnyClassicalValue(f"get_field({qmod_val_to_expr_str(proxy)}, '{field}')")
182
+ if isinstance(proxy, type) and issubclass(proxy, Enum):
183
+ return getattr(proxy, field)
185
184
  if isinstance(proxy, list):
186
185
  if field != "len":
187
186
  raise ClassiqExpansionError(
@@ -217,9 +216,10 @@ def get_type(struct_type: Symbol) -> TypeProxy:
217
216
 
218
217
 
219
218
  def do_div(lhs: Any, rhs: Any) -> Any:
220
- lhs = unwrap_sympy_numeric(lhs)
221
- rhs = unwrap_sympy_numeric(rhs)
222
- return lhs / rhs
219
+ res = lhs / rhs
220
+ if isinstance(res, sympy.Expr):
221
+ res = res.evalf()
222
+ return res
223
223
 
224
224
 
225
225
  _EXPRESSION_TYPES = get_args(ExpressionValue)
@@ -253,7 +253,7 @@ def do_subscript(value: Any, index: Any) -> Any:
253
253
  and not is_constant_subscript(index)
254
254
  and _is_qmod_value(index)
255
255
  ):
256
- return AnyClassicalValue(str(value))[index]
256
+ return AnyClassicalValue(qmod_val_to_expr_str(value))[index]
257
257
  return value[index]
258
258
  if index.is_signed or index.fraction_digits > 0:
259
259
  raise ClassiqExpansionError(
@@ -270,7 +270,9 @@ def do_subscript(value: Any, index: Any) -> Any:
270
270
  f"{index.size} qubits but the list size is {length} != 2**{index.size}"
271
271
  )
272
272
  if isinstance(value, ClassicalArrayProxy):
273
- return AnyClassicalValue(f"{value}[{index}]")
273
+ return AnyClassicalValue(
274
+ f"do_subscript({qmod_val_to_expr_str(value)}, {qmod_val_to_expr_str(index)})"
275
+ )
274
276
  else:
275
277
  return Piecewise(
276
278
  *[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
@@ -286,9 +288,29 @@ def do_slice(value: Any, lower: Any, upper: Any) -> Any:
286
288
  return do_subscript(value, slice(lower, upper))
287
289
 
288
290
 
291
+ def do_sum(val: Any) -> Any:
292
+ if isinstance(val, AnyClassicalValue):
293
+ return AnyClassicalValue(f"sum({val})")
294
+ return sum(val)
295
+
296
+
297
+ do_sum.__name__ = "sum"
298
+
299
+
300
+ def mod_inverse(a: Any, b: Any) -> Any:
301
+ if (
302
+ isinstance(a, AnyClassicalValue)
303
+ or (isinstance(a, sympy.Basic) and len(a.free_symbols) > 0)
304
+ or isinstance(b, AnyClassicalValue)
305
+ or (isinstance(b, sympy.Basic) and len(b.free_symbols) > 0)
306
+ ):
307
+ return AnyClassicalValue(f"mod_inverse({a}, {b})")
308
+ return sympy.mod_inverse(a, b)
309
+
310
+
289
311
  CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
290
312
  print,
291
- sum,
313
+ do_sum,
292
314
  struct_literal,
293
315
  get_field,
294
316
  get_type,
@@ -300,8 +322,29 @@ CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
300
322
  BitwiseNot,
301
323
  BitwiseOr,
302
324
  LogicalXor,
325
+ RShift,
326
+ LShift,
327
+ mod_inverse,
328
+ ]
329
+
330
+
331
+ def _symbolic_function(func: str) -> Callable:
332
+ def wrapper(*args: Any) -> AnyClassicalValue:
333
+ return AnyClassicalValue(
334
+ f"{func}({', '.join(map(qmod_val_to_expr_str, args))})"
335
+ )
336
+
337
+ wrapper.__name__ = func
338
+ return wrapper
339
+
340
+
341
+ QMOD_CLASSICAL_FUNCTIONS = [
342
+ _symbolic_function(func) for func in qmod_classical_functions
303
343
  ]
304
344
 
305
345
  ATOMIC_EXPRESSION_FUNCTIONS = {
306
- **{core_func.__name__: core_func for core_func in CORE_LIB_FUNCTIONS_LIST},
346
+ **{
347
+ core_func.__name__: core_func
348
+ for core_func in CORE_LIB_FUNCTIONS_LIST + QMOD_CLASSICAL_FUNCTIONS
349
+ },
307
350
  }
@@ -15,6 +15,7 @@ from classiq.interface.generator.functions.classical_type import ClassicalType
15
15
  from classiq.interface.generator.functions.port_declaration import (
16
16
  PortDeclarationDirection,
17
17
  )
18
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
18
19
  from classiq.interface.model.classical_parameter_declaration import (
19
20
  ClassicalParameterDeclaration,
20
21
  )
@@ -132,6 +133,7 @@ class _CapturedHandle(_Captured):
132
133
  name=self.mangled_name,
133
134
  quantum_type=self.quantum_type,
134
135
  direction=self.direction.dump(),
136
+ type_qualifier=TypeQualifier.Inferred, # TODO https://classiq.atlassian.net/browse/CLS-1830
135
137
  )
136
138
 
137
139
  def is_same_var(self, other: "_CapturedHandle") -> bool:
@@ -17,6 +17,7 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
17
17
  from classiq.interface.generator.expressions.proxies.classical.utils import (
18
18
  get_proxy_type,
19
19
  )
20
+ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
20
21
  from classiq.interface.model.quantum_function_declaration import (
21
22
  NamedParamsQuantumFunctionDeclaration,
22
23
  PositionalArg,
@@ -43,6 +44,10 @@ class Closure:
43
44
  positional_arg_declarations: Sequence[PositionalArg] = tuple()
44
45
  captured_vars: CapturedVars = field(default_factory=CapturedVars)
45
46
 
47
+ @property
48
+ def parameters_dict(self) -> dict[str, PositionalArg]:
49
+ return nameables_to_dict(self.positional_arg_declarations)
50
+
46
51
 
47
52
  @dataclass(frozen=True)
48
53
  class GenerativeClosure(Closure):
@@ -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,
@@ -178,7 +183,8 @@ def _evaluate_qarray_in_quantum_symbol(
178
183
  "length",
179
184
  param_name,
180
185
  )
181
- set_length(type_to_update, new_length)
186
+ if new_length is not None:
187
+ set_length(type_to_update, new_length)
182
188
  return type_to_update
183
189
 
184
190
 
@@ -194,7 +200,8 @@ def _evaluate_qnum_in_quantum_symbol(
194
200
  "sign",
195
201
  param_name,
196
202
  )
197
- type_to_update.is_signed = Expression(expr=str(new_is_sign))
203
+ if new_is_sign is not None:
204
+ type_to_update.is_signed = Expression(expr=str(new_is_sign))
198
205
  if type_to_update.fraction_digits is not None:
199
206
  new_fraction_digits = _eval_expr(
200
207
  type_to_update.fraction_digits,
@@ -204,7 +211,8 @@ def _evaluate_qnum_in_quantum_symbol(
204
211
  "fraction digits",
205
212
  param_name,
206
213
  )
207
- type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
214
+ if new_fraction_digits is not None:
215
+ type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
208
216
  if type_to_update.size is not None:
209
217
  new_size = _eval_expr(
210
218
  type_to_update.size,
@@ -214,7 +222,8 @@ def _evaluate_qnum_in_quantum_symbol(
214
222
  "size",
215
223
  param_name,
216
224
  )
217
- set_size(type_to_update, new_size, param_name)
225
+ if new_size is not None:
226
+ set_size(type_to_update, new_size, param_name)
218
227
  return type_to_update
219
228
 
220
229
 
@@ -228,17 +237,21 @@ def _eval_expr(
228
237
  type_name: str,
229
238
  attr_name: str,
230
239
  param_name: str,
231
- ) -> _EXPR_TYPE:
240
+ ) -> Optional[_EXPR_TYPE]:
232
241
  val = evaluate_classical_expression(expression, scope).value
233
242
  if expected_type is int and isinstance(val, float):
234
243
  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
244
+ if isinstance(val, expected_type):
245
+ return val
246
+ if isinstance(val, AnyClassicalValue) or (
247
+ isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
248
+ ):
249
+ return None
250
+ raise ClassiqExpansionError(
251
+ f"When inferring the type of parameter {param_name!r}: "
252
+ f"{type_name} {attr_name} must be {expected_type.__name__}, got "
253
+ f"{str(val)!r}"
254
+ )
242
255
 
243
256
 
244
257
  def _evaluate_qstruct_in_quantum_symbol(
@@ -73,7 +73,7 @@ def evaluate(
73
73
  try:
74
74
  sympify_result = sympify(sympy_expr, locals=model_locals)
75
75
  except (TypeError, IndexError) as e:
76
- raise ClassiqExpansionError(str(e)) from None
76
+ raise ClassiqExpansionError(str(e)) from e
77
77
  except AttributeError as e:
78
78
  if isinstance(e.obj, EnumMeta):
79
79
  raise ClassiqExpansionError(
@@ -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):
@@ -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,6 +16,9 @@ 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 (
@@ -57,11 +59,13 @@ from classiq.model_expansions.scope_initialization import (
57
59
  )
58
60
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
59
61
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
62
+ from classiq.qmod.builtins.constants import __all__ as builtin_constants
60
63
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
61
64
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
62
65
  from classiq.qmod.model_state_container import QMODULE
63
66
  from classiq.qmod.semantics.error_manager import ErrorManager
64
67
  from classiq.qmod.semantics.validation.model_validation import validate_model
68
+ from classiq.qmod.utilities import qmod_val_to_expr_str
65
69
 
66
70
 
67
71
  class BaseInterpreter:
@@ -126,7 +130,11 @@ class BaseInterpreter:
126
130
  classical_execution_code=self._model.classical_execution_code,
127
131
  execution_preferences=self._model.execution_preferences,
128
132
  functions=list(self._expanded_functions.values()),
129
- constants=self._model.constants,
133
+ constants=[
134
+ const
135
+ for name, const in QMODULE.constants.items()
136
+ if name not in builtin_constants
137
+ ],
130
138
  enums=[
131
139
  enum_decl
132
140
  for name, enum_decl in QMODULE.enum_decls.items()
@@ -157,14 +165,17 @@ class BaseInterpreter:
157
165
  @evaluate.register
158
166
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
159
167
  expr = evaluate_classical_expression(expression, self._builder.current_scope)
160
- if not isinstance(expr.value, sympy.Basic):
161
- return expr
162
168
  vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
163
- vrc.visit(ast.parse(str(expr.value)))
169
+ vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
164
170
  for handle in vrc.var_handles:
165
171
  if handle.name in self._builder.current_scope and isinstance(
166
- self._builder.current_scope[handle.name], QuantumSymbol
172
+ self._builder.current_scope[handle.name].value, QuantumSymbol
167
173
  ):
174
+ if (
175
+ isinstance(handle, FieldHandleBinding)
176
+ and handle.field in CLASSICAL_ATTRIBUTES
177
+ ):
178
+ handle = handle.base_handle
168
179
  self.evaluate(handle)
169
180
  return expr
170
181
 
@@ -5,6 +5,7 @@ from pydantic import ValidationError
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqError
7
7
  from classiq.interface.model.allocate import Allocate
8
+ from classiq.interface.model.bind_operation import BindOperation
8
9
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
9
10
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
11
  from classiq.interface.model.quantum_function_declaration import (
@@ -16,6 +17,7 @@ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunction
16
17
  from classiq.model_expansions.interpreters.generative_interpreter import (
17
18
  GenerativeInterpreter,
18
19
  )
20
+ from classiq.model_expansions.quantum_operations import BindEmitter
19
21
  from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
20
22
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
21
23
  DeclarativeQuantumFunctionCallEmitter,
@@ -40,6 +42,9 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
40
42
  def emit_allocate(self, allocate: Allocate) -> None:
41
43
  AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
42
44
 
45
+ def emit_bind(self, bind: BindOperation) -> None:
46
+ BindEmitter(self, allow_symbolic_size=True).emit(bind)
47
+
43
48
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
44
49
  DeclarativeQuantumFunctionCallEmitter(self).emit(call)
45
50
 
@@ -61,7 +61,11 @@ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
61
61
  from classiq.model_expansions.quantum_operations.assignment_result_processor import (
62
62
  AssignmentResultProcessor,
63
63
  )
64
- from classiq.model_expansions.quantum_operations.block_evaluator import BlockEvaluator
64
+ from classiq.model_expansions.quantum_operations.block_evaluator import (
65
+ BlockEvaluator,
66
+ IfElimination,
67
+ RepeatElimination,
68
+ )
65
69
  from classiq.model_expansions.quantum_operations.composite_emitter import (
66
70
  CompositeEmitter,
67
71
  )
@@ -152,10 +156,16 @@ class GenerativeInterpreter(BaseInterpreter):
152
156
  QuantumFunctionCallEmitter(self).emit(call)
153
157
 
154
158
  @emit.register
159
+ def _emit_allocate(self, allocate: Allocate) -> None:
160
+ return self.emit_allocate(allocate)
161
+
155
162
  def emit_allocate(self, allocate: Allocate) -> None:
156
163
  AllocateEmitter(self).emit(allocate)
157
164
 
158
165
  @emit.register
166
+ def _emit_bind(self, bind: BindOperation) -> None:
167
+ self.emit_bind(bind)
168
+
159
169
  def emit_bind(self, bind: BindOperation) -> None:
160
170
  BindEmitter(self).emit(bind)
161
171
 
@@ -210,6 +220,7 @@ class GenerativeInterpreter(BaseInterpreter):
210
220
  self,
211
221
  [
212
222
  ExpressionEvaluator(self, "condition"),
223
+ IfElimination(self),
213
224
  BlockEvaluator(
214
225
  self,
215
226
  CLASSICAL_IF_OPERATOR_NAME,
@@ -243,6 +254,7 @@ class GenerativeInterpreter(BaseInterpreter):
243
254
  self,
244
255
  [
245
256
  ExpressionEvaluator(self, "count"),
257
+ RepeatElimination(self),
246
258
  RepeatBlockEvaluator(self, REPEAT_OPERATOR_NAME, "body"),
247
259
  ],
248
260
  ).emit(repeat)