classiq 0.77.0__py3-none-any.whl → 0.79.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 (69) hide show
  1. classiq/applications/iqae/__init__.py +0 -0
  2. classiq/applications/iqae/iqae.py +207 -0
  3. classiq/execution/__init__.py +1 -1
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/applications/iqae/__init__.py +0 -0
  6. classiq/interface/applications/iqae/generic_iqae.py +222 -0
  7. classiq/interface/applications/iqae/iqae_result.py +45 -0
  8. classiq/interface/debug_info/debug_info.py +3 -0
  9. classiq/interface/executor/execution_result.py +1 -1
  10. classiq/interface/executor/user_budget.py +1 -1
  11. classiq/interface/generator/arith/arithmetic.py +10 -6
  12. classiq/interface/generator/arith/binary_ops.py +8 -11
  13. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
  14. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +90 -23
  15. classiq/interface/generator/expressions/proxies/classical/utils.py +4 -0
  16. classiq/interface/generator/functions/classical_type.py +74 -0
  17. classiq/interface/generator/functions/concrete_types.py +3 -0
  18. classiq/interface/generator/functions/type_name.py +32 -3
  19. classiq/interface/generator/generated_circuit_data.py +21 -8
  20. classiq/interface/helpers/model_normalizer.py +47 -0
  21. classiq/interface/ide/visual_model.py +2 -0
  22. classiq/interface/interface_version.py +1 -1
  23. classiq/interface/model/bounds.py +12 -0
  24. classiq/interface/model/model.py +9 -5
  25. classiq/interface/model/quantum_type.py +25 -3
  26. classiq/interface/model/statement_block.py +2 -0
  27. classiq/model_expansions/atomic_expression_functions_defs.py +20 -6
  28. classiq/model_expansions/closure.py +1 -58
  29. classiq/model_expansions/evaluators/argument_types.py +21 -2
  30. classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
  31. classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
  32. classiq/model_expansions/function_builder.py +0 -45
  33. classiq/model_expansions/generative_functions.py +1 -1
  34. classiq/model_expansions/interpreters/base_interpreter.py +12 -14
  35. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
  36. classiq/model_expansions/interpreters/generative_interpreter.py +9 -0
  37. classiq/model_expansions/quantum_operations/__init__.py +3 -0
  38. classiq/model_expansions/quantum_operations/allocate.py +3 -1
  39. classiq/model_expansions/quantum_operations/assignment_result_processor.py +7 -3
  40. classiq/model_expansions/quantum_operations/bind.py +8 -1
  41. classiq/model_expansions/quantum_operations/bounds.py +30 -0
  42. classiq/model_expansions/quantum_operations/call_emitter.py +116 -37
  43. classiq/model_expansions/quantum_operations/emitter.py +6 -1
  44. classiq/model_expansions/quantum_operations/function_calls_cache.py +91 -0
  45. classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
  46. classiq/model_expansions/scope.py +33 -13
  47. classiq/model_expansions/scope_initialization.py +1 -14
  48. classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
  49. classiq/model_expansions/utils/text_utils.py +4 -2
  50. classiq/model_expansions/visitors/symbolic_param_inference.py +33 -28
  51. classiq/model_expansions/visitors/variable_references.py +39 -43
  52. classiq/open_library/functions/linear_pauli_rotation.py +6 -6
  53. classiq/open_library/functions/state_preparation.py +1 -1
  54. classiq/open_library/functions/utility_functions.py +2 -2
  55. classiq/qmod/builtins/classical_execution_primitives.py +1 -1
  56. classiq/qmod/declaration_inferrer.py +5 -3
  57. classiq/qmod/model_state_container.py +21 -1
  58. classiq/qmod/native/pretty_printer.py +8 -0
  59. classiq/qmod/pretty_print/expression_to_python.py +8 -1
  60. classiq/qmod/pretty_print/pretty_printer.py +7 -0
  61. classiq/qmod/python_classical_type.py +1 -1
  62. classiq/qmod/qmod_parameter.py +43 -7
  63. classiq/qmod/qmod_variable.py +7 -4
  64. classiq/qmod/semantics/annotation/qstruct_annotator.py +15 -4
  65. classiq/qmod/utilities.py +5 -1
  66. {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/METADATA +1 -1
  67. {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/RECORD +68 -59
  68. classiq/interface/executor/iqae_result.py +0 -17
  69. {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,30 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from classiq.interface.exceptions import ClassiqExpansionError
4
+ from classiq.interface.model.bounds import SetBoundsStatement
5
+
6
+ from classiq.model_expansions.quantum_operations.bind import Emitter
7
+ from classiq.model_expansions.scope import QuantumSymbol
8
+ from classiq.qmod.qmod_variable import QuantumNumeric
9
+
10
+ if TYPE_CHECKING:
11
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
12
+
13
+
14
+ class SetBoundsEmitter(Emitter[SetBoundsStatement]):
15
+ def __init__(
16
+ self, interpreter: "BaseInterpreter", keep_statement: bool = True
17
+ ) -> None:
18
+ super().__init__(interpreter)
19
+ self._keep_statement = keep_statement
20
+
21
+ def emit(self, op: SetBoundsStatement, /) -> bool:
22
+ target = self._interpreter.evaluate(op.target).as_type(QuantumSymbol)
23
+ if not isinstance(target.quantum_type, QuantumNumeric):
24
+ raise ClassiqExpansionError(
25
+ f"Cannot set bounds of a non-numeric variable {op.target.qmod_expr!r}"
26
+ )
27
+ target.quantum_type.set_bounds(op.bounds)
28
+ if self._keep_statement:
29
+ self.emit_statement(op)
30
+ return True
@@ -28,16 +28,20 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
28
28
  from classiq.interface.generator.functions.port_declaration import (
29
29
  PortDeclarationDirection,
30
30
  )
31
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
32
+ from classiq.interface.helpers.backward_compatibility import zip_strict
31
33
  from classiq.interface.model.classical_parameter_declaration import (
32
34
  ClassicalParameterDeclaration,
33
35
  )
34
36
  from classiq.interface.model.handle_binding import HandleBinding
37
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME
35
38
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
36
39
  from classiq.interface.model.port_declaration import PortDeclaration
37
40
  from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
38
41
  from classiq.interface.model.quantum_function_declaration import (
39
42
  NamedParamsQuantumFunctionDeclaration,
40
43
  PositionalArg,
44
+ QuantumOperandDeclaration,
41
45
  )
42
46
  from classiq.interface.model.quantum_statement import QuantumStatement
43
47
  from classiq.interface.model.variable_declaration_statement import (
@@ -49,9 +53,10 @@ from classiq.model_expansions.capturing.captured_vars import (
49
53
  UNINITIALIZED_VAR_MESSAGE,
50
54
  validate_args_are_not_propagated,
51
55
  )
52
- from classiq.model_expansions.closure import FunctionClosure
56
+ from classiq.model_expansions.closure import Closure, FunctionClosure
53
57
  from classiq.model_expansions.evaluators.argument_types import (
54
58
  add_information_from_output_arguments,
59
+ handle_args_numeric_bounds,
55
60
  )
56
61
  from classiq.model_expansions.evaluators.parameter_types import (
57
62
  evaluate_parameter_types_from_args,
@@ -63,6 +68,9 @@ from classiq.model_expansions.quantum_operations.emitter import (
63
68
  Emitter,
64
69
  QuantumStatementT,
65
70
  )
71
+ from classiq.model_expansions.quantum_operations.function_calls_cache import (
72
+ get_func_call_cache_key,
73
+ )
66
74
  from classiq.model_expansions.scope import (
67
75
  Evaluated,
68
76
  QuantumSymbol,
@@ -70,8 +78,12 @@ from classiq.model_expansions.scope import (
70
78
  QuantumVariable,
71
79
  Scope,
72
80
  )
81
+ from classiq.model_expansions.transformers.type_qualifier_inference import (
82
+ TypeQualifierInference,
83
+ )
73
84
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
74
85
  from classiq.model_expansions.utils.text_utils import are, readable_list, s
86
+ from classiq.qmod.pretty_print.expression_to_python import transform_expression
75
87
  from classiq.qmod.semantics.validation.signature_validation import (
76
88
  validate_function_signature,
77
89
  )
@@ -114,6 +126,24 @@ def _is_symbolic(arg: Any) -> bool:
114
126
  return False
115
127
 
116
128
 
129
+ def _validate_gen_args(
130
+ function: FunctionClosure, evaluated_args: list[Evaluated]
131
+ ) -> None:
132
+ for param, arg in zip_strict(
133
+ function.positional_arg_declarations, evaluated_args, strict=True
134
+ ):
135
+ if (
136
+ isinstance(param, ClassicalParameterDeclaration)
137
+ and param.classical_type.is_purely_generative
138
+ and _is_symbolic(arg.value)
139
+ ):
140
+ raise ClassiqExpansionError(
141
+ f"Parameter {param.name!r} is used in a compile-time expression "
142
+ f"context but is passed a runtime expression "
143
+ f"{transform_expression(str(arg.value), {}, {}, one_line=True)!r}"
144
+ )
145
+
146
+
117
147
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
118
148
  def __init__(self, interpreter: "BaseInterpreter") -> None:
119
149
  Emitter.__init__(self, interpreter)
@@ -174,22 +204,38 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
174
204
  )
175
205
  new_positional_arg_decls = new_declaration.positional_arg_declarations
176
206
  if not self.should_expand_function(function, evaluated_args):
177
- is_atomic = True
178
207
  new_declaration = self._expanded_functions_by_name.get(
179
208
  function.name, new_declaration
180
209
  )
181
210
  else:
182
- is_atomic = False
211
+ # FIXME: enable for BE (CLS-2390)
212
+ if type(self._interpreter).__name__ == "FrontendGenerativeInterpreter":
213
+ _validate_gen_args(function, evaluated_args)
183
214
  new_declaration = self._expand_function(
184
215
  evaluated_args, new_declaration, function
185
216
  )
217
+ new_positional_arg_decls = new_declaration.positional_arg_declarations
218
+ evaluated_args = [
219
+ arg
220
+ for arg in evaluated_args
221
+ if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
222
+ ]
186
223
 
187
- new_positional_args = self._get_new_positional_args(
188
- evaluated_args, is_atomic, new_positional_arg_decls
189
- )
224
+ add_information_from_output_arguments(new_positional_arg_decls, evaluated_args)
225
+ handle_args_numeric_bounds(new_positional_arg_decls, evaluated_args)
190
226
  captured_args = function.captured_vars.filter_vars(function).get_captured_args(
191
227
  self._builder.current_function
192
228
  )
229
+ new_positional_args = [
230
+ arg.emit(param)
231
+ for param, arg in zip_strict(
232
+ new_positional_arg_decls[
233
+ : len(new_positional_arg_decls) - len(captured_args)
234
+ ],
235
+ evaluated_args,
236
+ strict=True,
237
+ )
238
+ ]
193
239
  validate_args_are_not_propagated(
194
240
  new_positional_args,
195
241
  captured_args,
@@ -228,19 +274,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
228
274
  decl: NamedParamsQuantumFunctionDeclaration,
229
275
  function: FunctionClosure,
230
276
  ) -> NamedParamsQuantumFunctionDeclaration:
231
- self._add_params_to_scope(decl.positional_arg_declarations, args, function)
232
- context = self._expand_operation(function.with_new_declaration(decl))
233
- function_context = cast(FunctionContext, context)
234
- closure_id = function_context.closure.closure_id
235
- if closure_id in self._expanded_functions:
236
- function_def = self._expanded_functions[closure_id]
277
+ inferred_args = self._add_params_to_scope(
278
+ decl.positional_arg_declarations, args, function
279
+ )
280
+ function = function.with_new_declaration(decl)
281
+ cache_key = get_func_call_cache_key(decl, inferred_args)
282
+ if cache_key in self._expanded_functions:
283
+ function_def = self._expanded_functions[cache_key]
284
+ self._expand_cached_function(function, function_def)
237
285
  self._expanded_functions_compilation_metadata[
238
286
  function_def.name
239
287
  ].occurrences_number += 1
240
288
  return function_def
241
289
 
290
+ context = self._expand_operation(function)
291
+ function_context = cast(FunctionContext, context)
242
292
  function_def = self._create_function_definition(function_context, args)
243
- self._expanded_functions[closure_id] = function_def
293
+ self._expanded_functions[cache_key] = function_def
244
294
  self._top_level_scope[function_def.name] = Evaluated(
245
295
  value=function_context.closure.with_new_declaration(function_def)
246
296
  )
@@ -270,6 +320,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
270
320
  )
271
321
  captured_ports = captured_vars.get_captured_parameters()
272
322
  if len(captured_ports) == 0:
323
+ self._override_type_qualifier(function_context, func_def)
273
324
  return func_def
274
325
  func_def.positional_arg_declarations = list(
275
326
  chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
@@ -281,6 +332,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
281
332
  rewrite_mapping |= captured_vars.get_classical_captured_mapping()
282
333
  func_def.body = self.rewrite(func_def.body, rewrite_mapping)
283
334
 
335
+ self._override_type_qualifier(function_context, func_def)
336
+
284
337
  return func_def
285
338
 
286
339
  @staticmethod
@@ -288,12 +341,13 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
288
341
  parameters: Sequence[PositionalArg],
289
342
  arguments: Sequence[Evaluated],
290
343
  closure: FunctionClosure,
291
- ) -> None:
344
+ ) -> list[Evaluated]:
345
+ inferred_args: list[Evaluated] = []
292
346
  for parameter, argument in zip(parameters, arguments):
293
347
  param_handle = HandleBinding(name=parameter.name)
294
348
  if isinstance(argument.value, QuantumVariable):
295
349
  assert isinstance(parameter, PortDeclaration)
296
- closure.scope[parameter.name] = Evaluated(
350
+ inferred_arg = Evaluated(
297
351
  QuantumSymbol(
298
352
  handle=param_handle,
299
353
  quantum_type=parameter.quantum_type,
@@ -302,32 +356,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
302
356
  )
303
357
  elif _is_symbolic(argument.value):
304
358
  assert isinstance(parameter, ClassicalParameterDeclaration)
305
- closure.scope[parameter.name] = Evaluated(
359
+ inferred_arg = Evaluated(
306
360
  value=parameter.classical_type.get_classical_proxy(param_handle),
307
361
  defining_function=closure,
308
362
  )
309
363
  else:
310
- closure.scope[parameter.name] = argument
311
-
312
- def _get_new_positional_args(
313
- self,
314
- evaluated_args: list[Evaluated],
315
- is_atomic: bool,
316
- new_positional_arg_decls: Sequence[PositionalArg],
317
- ) -> list[ArgValue]:
318
- evaluated_args = add_information_from_output_arguments(
319
- new_positional_arg_decls, evaluated_args
320
- )
321
- if is_atomic:
322
- return [arg.emit() for arg in evaluated_args]
323
-
324
- positional_args = [
325
- arg.emit()
326
- for arg in evaluated_args
327
- if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
328
- ]
329
-
330
- return positional_args
364
+ inferred_arg = argument
365
+ closure.scope[parameter.name] = inferred_arg
366
+ inferred_args.append(inferred_arg)
367
+ return inferred_args
331
368
 
332
369
  def _prepare_fully_typed_declaration(
333
370
  self, function: FunctionClosure, evaluated_args: list[Evaluated]
@@ -381,3 +418,45 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
381
418
  raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
382
419
  if var_state and param.direction == PortDeclarationDirection.Output:
383
420
  raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
421
+
422
+ def _override_type_qualifier(
423
+ self, func_context: FunctionContext, func_def: NativeFunctionDefinition
424
+ ) -> None:
425
+ """
426
+ The type qualifier can be changed according to the operand passed to the
427
+ function. For example,
428
+ apply_to_all(X, q) --> q will be QFree after expansion
429
+ apply_to_all(H, q) --> q will be Quantum after expansion
430
+ This also holds for the intermediate lambda created during the expansion.
431
+
432
+ We don't override the type qualifier if it's explicitly specified (QFree or
433
+ Const), neither in the function declaration nor in the operand declaration.
434
+ """
435
+
436
+ if func_context.is_lambda:
437
+ self._update_type_qualifiers(func_def)
438
+ return
439
+
440
+ orig_name = func_context.name
441
+ if (
442
+ orig_name == MAIN_FUNCTION_NAME
443
+ or orig_name not in func_context.closure.scope
444
+ ):
445
+ return
446
+
447
+ orig_func = func_context.closure.scope[orig_name].value
448
+ if isinstance(orig_func, Closure) and not any(
449
+ isinstance(param_decl, QuantumOperandDeclaration)
450
+ for param_decl in orig_func.positional_arg_declarations
451
+ ):
452
+ return
453
+
454
+ self._update_type_qualifiers(func_def)
455
+
456
+ @staticmethod
457
+ def _update_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
458
+ # only override the qualifier if it's unspecified (not QFree or Const)
459
+ for port in func_def.port_declarations:
460
+ if port.type_qualifier is TypeQualifier.Quantum:
461
+ port.type_qualifier = TypeQualifier.Inferred
462
+ TypeQualifierInference().run(func_def.port_declarations, func_def.body)
@@ -47,7 +47,7 @@ from classiq.interface.model.quantum_function_declaration import (
47
47
  )
48
48
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
49
49
 
50
- from classiq.model_expansions.closure import Closure, GenerativeClosure
50
+ from classiq.model_expansions.closure import Closure, FunctionClosure, GenerativeClosure
51
51
  from classiq.model_expansions.function_builder import (
52
52
  OperationBuilder,
53
53
  OperationContext,
@@ -87,6 +87,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
87
87
  def _expand_operation(self, closure: Closure) -> OperationContext:
88
88
  return self._interpreter._expand_operation(closure)
89
89
 
90
+ def _expand_cached_function(
91
+ self, closure: FunctionClosure, func_def: NativeFunctionDefinition
92
+ ) -> None:
93
+ return self._interpreter._expand_cached_function(closure, func_def)
94
+
90
95
  @property
91
96
  def _builder(self) -> OperationBuilder:
92
97
  return self._interpreter._builder
@@ -0,0 +1,91 @@
1
+ import json
2
+ from typing import Any
3
+
4
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
5
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
6
+ ClassicalProxy,
7
+ )
8
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
9
+ ClassicalStructProxy,
10
+ )
11
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
12
+ get_proxy_type,
13
+ )
14
+ from classiq.interface.generator.functions.port_declaration import (
15
+ PortDeclarationDirection,
16
+ )
17
+ from classiq.interface.model.port_declaration import PortDeclaration
18
+ from classiq.interface.model.quantum_function_declaration import (
19
+ NamedParamsQuantumFunctionDeclaration,
20
+ )
21
+ from classiq.interface.model.quantum_type import QuantumNumeric
22
+
23
+ from classiq.model_expansions.closure import FunctionClosure
24
+ from classiq.model_expansions.scope import (
25
+ Evaluated,
26
+ QuantumSymbol,
27
+ evaluated_to_str as evaluated_classical_param_to_str,
28
+ )
29
+
30
+
31
+ def get_func_call_cache_key(
32
+ decl: NamedParamsQuantumFunctionDeclaration,
33
+ args: list[Evaluated],
34
+ ) -> str:
35
+ if len(decl.positional_arg_declarations) != len(args):
36
+ raise ClassiqInternalExpansionError(
37
+ "Mismatch between number of args to number of arg declarations"
38
+ )
39
+
40
+ # output arguments cannot affect the morphization of the function, as their
41
+ # attributes are defined locally by the function, according to other arguments
42
+ non_outputs_args = [
43
+ arg
44
+ for arg_decl, arg in zip(decl.positional_arg_declarations, args)
45
+ if not (
46
+ isinstance(arg_decl, PortDeclaration)
47
+ and arg_decl.direction is PortDeclarationDirection.Output
48
+ )
49
+ ]
50
+ return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
51
+
52
+
53
+ def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str:
54
+ args_signature = [
55
+ _evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
56
+ ]
57
+ return json.dumps(args_signature)
58
+
59
+
60
+ def _evaluated_arg_to_str(arg: Any) -> str:
61
+ if isinstance(arg, str):
62
+ return arg
63
+ if isinstance(arg, QuantumSymbol):
64
+ return _evaluated_quantum_symbol_to_str(arg)
65
+ if isinstance(arg, FunctionClosure):
66
+ return _evaluated_one_operand_to_str(arg)
67
+ if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
68
+ return _evaluated_operands_list_to_str(arg)
69
+ if isinstance(arg, ClassicalProxy):
70
+ if isinstance(arg, ClassicalStructProxy):
71
+ return repr(arg.struct_declaration)
72
+ return repr(get_proxy_type(arg))
73
+ return evaluated_classical_param_to_str(arg)
74
+
75
+
76
+ def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
77
+ res = port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
78
+ if (
79
+ isinstance(port.quantum_type, QuantumNumeric)
80
+ and (bounds := port.quantum_type.get_bounds()) is not None
81
+ ):
82
+ res += f"_{float(bounds[0])}_{float(bounds[1])}"
83
+ return res
84
+
85
+
86
+ def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
87
+ return operand.name
88
+
89
+
90
+ def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
91
+ return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
@@ -1,10 +1,9 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ import sympy
4
+
3
5
  from classiq.interface.exceptions import ClassiqInternalExpansionError
4
6
  from classiq.interface.generator.expressions.expression import Expression
5
- from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
6
- ClassicalScalarProxy,
7
- )
8
7
  from classiq.interface.model.classical_if import ClassicalIf
9
8
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
9
  from classiq.interface.model.quantum_lambda_function import OperandIdentifier
@@ -29,7 +28,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
29
28
  def emit(self, call: QuantumFunctionCall, /) -> bool:
30
29
  if isinstance(call.function, OperandIdentifier):
31
30
  index_val = self._interpreter.evaluate(call.function.index).value
32
- if isinstance(index_val, ClassicalScalarProxy):
31
+ if isinstance(index_val, sympy.Basic):
33
32
  return self._emit_symbolic_lambda_list(call, index_val)
34
33
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
35
34
  args = call.positional_args
@@ -40,7 +39,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
40
39
  return True
41
40
 
42
41
  def _emit_symbolic_lambda_list(
43
- self, call: QuantumFunctionCall, index: ClassicalScalarProxy
42
+ self, call: QuantumFunctionCall, index: sympy.Basic
44
43
  ) -> bool:
45
44
  if TYPE_CHECKING:
46
45
  assert isinstance(call.function, OperandIdentifier)
@@ -55,7 +54,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
55
54
 
56
55
  @staticmethod
57
56
  def _create_recursive_if(
58
- call: QuantumFunctionCall, index: ClassicalScalarProxy, num_funcs: int
57
+ call: QuantumFunctionCall, index: sympy.Basic, num_funcs: int
59
58
  ) -> list[QuantumStatement]:
60
59
  if TYPE_CHECKING:
61
60
  assert isinstance(call.function, OperandIdentifier)
@@ -3,7 +3,9 @@ from collections import UserDict
3
3
  from collections.abc import Iterator
4
4
  from dataclasses import dataclass
5
5
  from functools import singledispatch
6
- from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
6
+ from typing import TYPE_CHECKING, Any, NoReturn, Optional, TypeVar, Union
7
+
8
+ import sympy
7
9
 
8
10
  from classiq.interface.exceptions import (
9
11
  ClassiqExpansionError,
@@ -29,11 +31,18 @@ from classiq.interface.model.handle_binding import (
29
31
  SubscriptHandleBinding,
30
32
  )
31
33
  from classiq.interface.model.quantum_function_call import ArgValue
34
+ from classiq.interface.model.quantum_function_declaration import (
35
+ AnonPositionalArg,
36
+ AnonQuantumOperandDeclaration,
37
+ )
32
38
  from classiq.interface.model.quantum_type import (
33
39
  QuantumBitvector,
40
+ QuantumNumeric,
34
41
  QuantumType,
35
42
  )
36
43
 
44
+ from classiq.model_expansions.utils.text_utils import readable_list, s
45
+
37
46
  if TYPE_CHECKING:
38
47
  from classiq.model_expansions.closure import FunctionClosure
39
48
 
@@ -180,6 +189,9 @@ class QuantumSymbolList(QuantumVariable):
180
189
  length = Expression(
181
190
  expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
182
191
  )
192
+ for symbol in symbols:
193
+ if isinstance(symbol.quantum_type, QuantumNumeric):
194
+ symbol.quantum_type.reset_bounds()
183
195
  return QuantumSymbolList(
184
196
  handles=handles, quantum_type=QuantumBitvector(length=length)
185
197
  )
@@ -206,37 +218,45 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
206
218
  return f"struct_literal({value.struct_declaration.name}, {', '.join(f'{k}={evaluated_to_str(v)}' for k, v in value.fields.items())})"
207
219
 
208
220
 
221
+ def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
222
+ if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
223
+ symbolic_vars = sorted(map(str, val.free_symbols))
224
+ suffix = f" {location_hint}" if location_hint is not None else ""
225
+ raise ClassiqExpansionError(
226
+ f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
227
+ )
228
+ raise ClassiqExpansionError(f"Invalid access to expression {val!r} as {t}")
229
+
230
+
209
231
  @dataclass(frozen=True)
210
232
  class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
211
233
  value: Any
212
234
  defining_function: Optional["FunctionClosure"] = None
213
235
 
214
- def as_type(self, t: type[T]) -> T:
236
+ def as_type(self, t: type[T], location_hint: Optional[str] = None) -> T:
215
237
  if t is int:
216
- return self._as_int() # type: ignore[return-value]
238
+ return self._as_int(location_hint) # type: ignore[return-value]
217
239
 
218
240
  if not isinstance(self.value, t):
219
- raise ClassiqExpansionError(
220
- f"Invalid access to expression {self.value!r} as {t}"
221
- )
241
+ _raise_type_error(self.value, t, location_hint)
222
242
 
223
243
  return self.value
224
244
 
225
- def _as_int(self) -> int:
245
+ def _as_int(self, location_hint: Optional[str]) -> int:
226
246
  if not isinstance(self.value, (int, float)):
227
- raise ClassiqExpansionError(
228
- f"Invalid access to expression {self.value!r} as {int}"
229
- )
247
+ _raise_type_error(self.value, int, location_hint)
230
248
 
231
249
  return int(self.value)
232
250
 
233
- def emit(self) -> ArgValue:
251
+ def emit(self, param: Optional[AnonPositionalArg] = None) -> ArgValue:
234
252
  from classiq.model_expansions.closure import FunctionClosure
235
253
 
236
254
  if isinstance(self.value, (QuantumVariable, FunctionClosure)):
237
255
  return self.value.emit()
238
- if isinstance(self.value, list) and all(
239
- isinstance(item, FunctionClosure) for item in self.value
256
+ if (
257
+ isinstance(param, AnonQuantumOperandDeclaration)
258
+ and isinstance(self.value, list)
259
+ and all(isinstance(item, FunctionClosure) for item in self.value)
240
260
  ):
241
261
  return [item.emit() for item in self.value]
242
262
 
@@ -1,12 +1,7 @@
1
1
  from collections.abc import Sequence
2
2
 
3
- from classiq.interface.exceptions import ClassiqError, ClassiqInternalExpansionError
3
+ from classiq.interface.exceptions import ClassiqError
4
4
  from classiq.interface.generator.constant import Constant
5
- from classiq.interface.generator.functions.classical_type import (
6
- ClassicalArray,
7
- ClassicalList,
8
- )
9
- from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
10
5
  from classiq.interface.model.classical_parameter_declaration import (
11
6
  ClassicalParameterDeclaration,
12
7
  )
@@ -138,11 +133,3 @@ def init_top_level_scope(model: Model, scope: Scope) -> None:
138
133
  def init_builtin_types() -> None:
139
134
  QMODULE.enum_decls |= BUILTIN_ENUM_DECLARATIONS
140
135
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
141
-
142
-
143
- def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
144
- if isinstance(classical_type, ClassicalList):
145
- raise ClassiqInternalExpansionError("Unexpected classical list")
146
- if isinstance(classical_type, ClassicalArray):
147
- return classical_type.size, *_get_shape(classical_type.element_type)
148
- return ()