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
@@ -18,6 +18,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
18
18
  )
19
19
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
20
20
  ClassicalArrayProxy,
21
+ ClassicalSequenceProxy,
21
22
  )
22
23
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
23
24
  ClassicalProxy,
@@ -39,6 +40,7 @@ from classiq.interface.generator.functions.classical_type import (
39
40
  Bool,
40
41
  ClassicalArray,
41
42
  ClassicalList,
43
+ ClassicalTuple,
42
44
  ClassicalType,
43
45
  OpaqueHandle,
44
46
  QmodPyObject,
@@ -46,6 +48,7 @@ from classiq.interface.generator.functions.classical_type import (
46
48
  StructMetaType,
47
49
  )
48
50
  from classiq.interface.generator.functions.type_name import TypeName
51
+ from classiq.interface.helpers.backward_compatibility import zip_strict
49
52
 
50
53
  from classiq.model_expansions.model_tables import (
51
54
  HandleIdentifier,
@@ -92,6 +95,15 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
92
95
  if isinstance(val, list):
93
96
  return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
94
97
 
98
+ elif isinstance(qmod_type, ClassicalTuple):
99
+ if isinstance(val, list):
100
+ return [
101
+ qmod_val_to_python(elem, elem_type)
102
+ for elem_type, elem in zip_strict(
103
+ qmod_type.element_types, val, strict=True
104
+ )
105
+ ]
106
+
95
107
  elif isinstance(qmod_type, OpaqueHandle):
96
108
  if isinstance(val, HandleIdentifier):
97
109
  return HandleTable.get_handle_object(val)
@@ -133,7 +145,7 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
133
145
  field_name: python_val_to_qmod(val[field_name], field_type)
134
146
  for field_name, field_type in struct_decl.variables.items()
135
147
  }
136
- return QmodStructInstance(struct_decl, qmod_dict)
148
+ return QmodStructInstance(struct_decl.model_copy(), qmod_dict)
137
149
 
138
150
  if isinstance(qmod_type, ClassicalList):
139
151
  if not isinstance(val, list):
@@ -162,7 +174,7 @@ def python_call_wrapper(func: Callable, *args: ExpressionValue) -> Any:
162
174
 
163
175
  def struct_literal(struct_type_symbol: Symbol, **kwargs: Any) -> QmodStructInstance:
164
176
  return QmodStructInstance(
165
- QMODULE.type_decls[struct_type_symbol.name],
177
+ QMODULE.type_decls[struct_type_symbol.name].model_copy(),
166
178
  {field: sympy_to_python(field_value) for field, field_value in kwargs.items()},
167
179
  )
168
180
 
@@ -236,7 +248,7 @@ def _is_qmod_value(val: Any) -> bool:
236
248
 
237
249
 
238
250
  def do_subscript(value: Any, index: Any) -> Any:
239
- if not isinstance(value, (list, ClassicalArrayProxy)) or not isinstance(
251
+ if not isinstance(value, (list, ClassicalSequenceProxy)) or not isinstance(
240
252
  index, QmodQNumProxy
241
253
  ):
242
254
  if isinstance(index, (QmodSizedProxy, QmodStructInstance)):
@@ -260,7 +272,7 @@ def do_subscript(value: Any, index: Any) -> Any:
260
272
  "Quantum numeric subscript must be an unsigned integer (is_signed=False, "
261
273
  "fraction_digits=0)"
262
274
  )
263
- if isinstance(value, ClassicalArrayProxy):
275
+ if isinstance(value, ClassicalSequenceProxy):
264
276
  length = value.length
265
277
  else:
266
278
  length = len(value)
@@ -269,7 +281,7 @@ def do_subscript(value: Any, index: Any) -> Any:
269
281
  f"Quantum numeric subscript size mismatch: The quantum numeric has "
270
282
  f"{index.size} qubits but the list size is {length} != 2**{index.size}"
271
283
  )
272
- if isinstance(value, ClassicalArrayProxy):
284
+ if isinstance(value, ClassicalSequenceProxy):
273
285
  return AnyClassicalValue(
274
286
  f"do_subscript({qmod_val_to_expr_str(value)}, {qmod_val_to_expr_str(index)})"
275
287
  )
@@ -289,7 +301,9 @@ def do_slice(value: Any, lower: Any, upper: Any) -> Any:
289
301
 
290
302
 
291
303
  def do_sum(val: Any) -> Any:
292
- if isinstance(val, AnyClassicalValue):
304
+ if (isinstance(val, sympy.Basic) and len(val.free_symbols) > 0) or (
305
+ isinstance(val, ClassicalArrayProxy) and not isinstance(val.length, int)
306
+ ):
293
307
  return AnyClassicalValue(f"sum({val})")
294
308
  return sum(val)
295
309
 
@@ -1,22 +1,11 @@
1
1
  import dataclasses
2
- import json
3
- from collections.abc import Collection, Sequence
2
+ from collections.abc import Sequence
4
3
  from dataclasses import dataclass, field
5
- from functools import singledispatch
6
4
  from typing import Any, Optional
7
5
 
8
6
  from typing_extensions import Self
9
7
 
10
8
  from classiq.interface.exceptions import ClassiqInternalExpansionError
11
- from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
12
- ClassicalProxy,
13
- )
14
- from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
15
- ClassicalStructProxy,
16
- )
17
- from classiq.interface.generator.expressions.proxies.classical.utils import (
18
- get_proxy_type,
19
- )
20
9
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
10
  from classiq.interface.model.quantum_function_declaration import (
22
11
  NamedParamsQuantumFunctionDeclaration,
@@ -27,10 +16,7 @@ from classiq.interface.model.quantum_statement import QuantumStatement
27
16
 
28
17
  from classiq.model_expansions.capturing.captured_vars import CapturedVars
29
18
  from classiq.model_expansions.scope import (
30
- Evaluated,
31
- QuantumSymbol,
32
19
  Scope,
33
- evaluated_to_str as evaluated_classical_param_to_str,
34
20
  )
35
21
  from classiq.qmod.builtins.functions import permute
36
22
  from classiq.qmod.quantum_function import GenerativeQFunc
@@ -67,13 +53,6 @@ class FunctionClosure(Closure):
67
53
  raise ClassiqInternalExpansionError
68
54
  return self._depth
69
55
 
70
- # creates a unique id for the function closure based on the arguments values.
71
- # The closure is changing across the interpreter flow so it's closure_id may change
72
- @property
73
- def closure_id(self) -> str:
74
- signature = _generate_closure_id(self.scope.data.values())
75
- return f"{self.name}__{signature}"
76
-
77
56
  @property
78
57
  def body(self) -> Sequence[QuantumStatement]:
79
58
  if self.name == permute.func_decl.name:
@@ -134,39 +113,3 @@ class FunctionClosure(Closure):
134
113
  @dataclass(frozen=True)
135
114
  class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
136
115
  pass
137
-
138
-
139
- def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
140
- args_signature = [
141
- _evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
142
- ]
143
- return json.dumps(args_signature)
144
-
145
-
146
- @singledispatch
147
- def _evaluated_arg_to_str(arg: Any) -> str:
148
- if isinstance(arg, str):
149
- return arg
150
- if isinstance(arg, QuantumSymbol):
151
- return _evaluated_quantum_symbol_to_str(arg)
152
- if isinstance(arg, FunctionClosure):
153
- return _evaluated_one_operand_to_str(arg)
154
- if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
155
- return _evaluated_operands_list_to_str(arg)
156
- if isinstance(arg, ClassicalProxy):
157
- if isinstance(arg, ClassicalStructProxy):
158
- return repr(arg.struct_declaration)
159
- return repr(get_proxy_type(arg))
160
- return evaluated_classical_param_to_str(arg)
161
-
162
-
163
- def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
164
- return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
165
-
166
-
167
- def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
168
- return operand.closure_id
169
-
170
-
171
- def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
172
- return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
@@ -3,8 +3,10 @@ from collections.abc import Sequence
3
3
  from classiq.interface.generator.functions.port_declaration import (
4
4
  PortDeclarationDirection,
5
5
  )
6
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
6
7
  from classiq.interface.model.port_declaration import AnonPortDeclaration
7
8
  from classiq.interface.model.quantum_function_declaration import AnonPositionalArg
9
+ from classiq.interface.model.quantum_type import QuantumNumeric
8
10
 
9
11
  from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
10
12
  from classiq.model_expansions.scope import Evaluated, QuantumVariable
@@ -13,7 +15,7 @@ from classiq.model_expansions.scope import Evaluated, QuantumVariable
13
15
  def add_information_from_output_arguments(
14
16
  parameters: Sequence[AnonPositionalArg],
15
17
  args: list[Evaluated],
16
- ) -> list[Evaluated]:
18
+ ) -> None:
17
19
  """
18
20
  This function propagates the quantum type information from the output arguments
19
21
  to the arguments that were passed to it.
@@ -40,4 +42,21 @@ def add_information_from_output_arguments(
40
42
  argument_as_quantum_symbol.quantum_type,
41
43
  str(argument_as_quantum_symbol),
42
44
  )
43
- return args
45
+
46
+
47
+ def handle_args_numeric_bounds(
48
+ parameters: Sequence[AnonPositionalArg],
49
+ args: list[Evaluated],
50
+ ) -> None:
51
+ for parameter, argument in zip(parameters, args):
52
+ if not isinstance(parameter, AnonPortDeclaration):
53
+ continue
54
+
55
+ argument_as_quantum_symbol = argument.as_type(QuantumVariable)
56
+
57
+ if (
58
+ parameter.direction != PortDeclarationDirection.Output
59
+ and parameter.type_qualifier != TypeQualifier.Const
60
+ and isinstance(argument_as_quantum_symbol.quantum_type, QuantumNumeric)
61
+ ):
62
+ argument_as_quantum_symbol.quantum_type.reset_bounds()
@@ -1,11 +1,12 @@
1
- from typing import Any, Union
1
+ from typing import TYPE_CHECKING, Any, Union
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
4
  from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
5
5
  AnyClassicalValue,
6
6
  )
7
7
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
8
- ClassicalArrayProxy,
8
+ ClassicalSequenceProxy,
9
+ _is_int,
9
10
  )
10
11
  from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
11
12
  ClassicalStructProxy,
@@ -16,6 +17,7 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
16
17
  from classiq.interface.generator.functions.classical_type import (
17
18
  ClassicalArray,
18
19
  ClassicalList,
20
+ ClassicalTuple,
19
21
  ClassicalType,
20
22
  )
21
23
  from classiq.interface.generator.functions.type_name import TypeName
@@ -26,7 +28,7 @@ from classiq.interface.helpers.backward_compatibility import zip_strict
26
28
  def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
27
29
  if isinstance(classical_type, TypeName):
28
30
  return _infer_classical_struct_type(val, classical_type)
29
- if isinstance(classical_type, (ClassicalArray, ClassicalList)):
31
+ if isinstance(classical_type, (ClassicalArray, ClassicalList, ClassicalTuple)):
30
32
  return _infer_classical_array_type(val, classical_type)
31
33
  return classical_type
32
34
 
@@ -34,7 +36,11 @@ def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalTy
34
36
  def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> ClassicalType:
35
37
  if not isinstance(val, (QmodStructInstance, ClassicalStructProxy)):
36
38
  return classical_type
37
- decl = val.struct_declaration
39
+ if classical_type.is_enum:
40
+ raise ClassiqExpansionError(
41
+ f"{classical_type.type_name!r} expected, got {str(val)!r}"
42
+ )
43
+ decl = classical_type.classical_struct_decl
38
44
  new_fields = {
39
45
  field_name: infer_classical_type(field_val, field_type)
40
46
  for (field_name, field_val), field_type in zip_strict(
@@ -51,9 +57,9 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
51
57
 
52
58
 
53
59
  def _infer_classical_array_type(
54
- val: Any, classical_type: Union[ClassicalArray, ClassicalList]
60
+ val: Any, classical_type: Union[ClassicalArray, ClassicalList, ClassicalTuple]
55
61
  ) -> ClassicalType:
56
- if isinstance(val, ClassicalArrayProxy):
62
+ if isinstance(val, ClassicalSequenceProxy):
57
63
  val_length = val.length
58
64
  elif isinstance(val, list):
59
65
  val_length = len(val)
@@ -62,7 +68,7 @@ def _infer_classical_array_type(
62
68
  else:
63
69
  raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
64
70
  if (
65
- isinstance(classical_type, ClassicalArray)
71
+ isinstance(classical_type, (ClassicalArray, ClassicalTuple))
66
72
  and isinstance(val_length, int)
67
73
  and isinstance(classical_type.size, int)
68
74
  and val_length != classical_type.size
@@ -71,11 +77,34 @@ def _infer_classical_array_type(
71
77
  f"Type mismatch: Argument has {val_length} items but "
72
78
  f"{classical_type.size} expected"
73
79
  )
74
- return ClassicalArray(
75
- element_type=(
76
- infer_classical_type(val[0], classical_type.element_type)
77
- if not isinstance(val_length, int) or val_length > 0
78
- else classical_type.element_type
80
+ new_classical_type = _infer_inner_array_types(classical_type, val, val_length)
81
+ if classical_type.is_generative:
82
+ new_classical_type.set_generative()
83
+ return new_classical_type
84
+
85
+
86
+ def _infer_inner_array_types(
87
+ classical_type: ClassicalType, val: Any, val_length: Any
88
+ ) -> ClassicalType:
89
+ if isinstance(classical_type, (ClassicalArray, ClassicalList)):
90
+ if _is_int(val_length) and val_length != 0:
91
+ return ClassicalTuple(
92
+ element_types=(
93
+ infer_classical_type(val[i], classical_type.element_type)
94
+ for i in range(int(val_length))
95
+ ),
96
+ )
97
+ element_type: ClassicalType
98
+ if val_length == 0:
99
+ element_type = classical_type.element_type
100
+ else:
101
+ element_type = infer_classical_type(val[0], classical_type.element_type)
102
+ return ClassicalArray(element_type=element_type, size=val_length)
103
+ if TYPE_CHECKING:
104
+ assert isinstance(classical_type, ClassicalTuple)
105
+ return ClassicalTuple(
106
+ element_types=(
107
+ infer_classical_type(val[idx], element_type)
108
+ for idx, element_type in enumerate(classical_type.element_types)
79
109
  ),
80
- size=val_length,
81
110
  )
@@ -38,6 +38,7 @@ def copy_type_information(
38
38
  expr=str(from_type.fraction_digits_value)
39
39
  )
40
40
  set_size(to_type, from_type.size_in_bits, to_param_name)
41
+ set_bounds(from_type, to_type)
41
42
  elif isinstance(to_type, QuantumBitvector):
42
43
  if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
43
44
  from_type.element_type
@@ -146,16 +147,20 @@ def set_length_by_size(
146
147
  quantum_array.length = Expression(expr=str(size // element_size))
147
148
 
148
149
 
149
- def validate_bind_targets(bind: BindOperation, scope: Scope) -> None:
150
+ def validate_bind_targets(
151
+ bind: BindOperation, scope: Scope, allow_symbolic_size: bool
152
+ ) -> None:
150
153
  illegal_qnum_bind_targets = []
151
154
  for out_handle in bind.out_handles:
152
155
  out_var = scope[out_handle.name].as_type(QuantumSymbol)
153
156
  out_var_type = out_var.quantum_type
154
157
  if not isinstance(out_var_type, QuantumNumeric):
155
158
  continue
156
- if not out_var_type.has_size_in_bits:
159
+ if (allow_symbolic_size and not out_var_type.is_instantiated) or (
160
+ not allow_symbolic_size and not out_var_type.has_size_in_bits
161
+ ):
157
162
  illegal_qnum_bind_targets.append(str(out_var.handle))
158
- elif not out_var_type.has_sign:
163
+ elif not allow_symbolic_size and not out_var_type.has_sign:
159
164
  assert not out_var_type.has_fraction_digits
160
165
  illegal_qnum_bind_targets.append(str(out_var.handle))
161
166
  if len(illegal_qnum_bind_targets) > 0:
@@ -187,3 +192,26 @@ def is_signature_monomorphic(params: Sequence[PositionalArg]) -> bool:
187
192
  isinstance(param, PortDeclaration) and param.quantum_type.is_evaluated
188
193
  for param in params
189
194
  )
195
+
196
+
197
+ def set_bounds(from_type: QuantumType, to_type: QuantumNumeric) -> None:
198
+ if not isinstance(from_type, QuantumNumeric):
199
+ to_type.reset_bounds()
200
+ return
201
+
202
+ if from_type.is_evaluated and to_type.is_evaluated:
203
+ same_attributes = to_type.sign_value == from_type.sign_value and (
204
+ to_type.fraction_digits_value == from_type.fraction_digits_value
205
+ )
206
+ else:
207
+ same_attributes = (
208
+ (from_type.is_signed is not None and from_type.fraction_digits is not None)
209
+ and (to_type.is_signed is not None and to_type.fraction_digits is not None)
210
+ and (to_type.is_signed.expr == from_type.is_signed.expr)
211
+ and (to_type.fraction_digits.expr == from_type.fraction_digits.expr)
212
+ )
213
+
214
+ if same_attributes:
215
+ to_type.set_bounds(from_type.get_bounds())
216
+ else:
217
+ to_type.reset_bounds()
@@ -11,15 +11,12 @@ 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
15
14
  from classiq.interface.model.model import MAIN_FUNCTION_NAME
16
15
  from classiq.interface.model.native_function_definition import (
17
16
  NativeFunctionDefinition,
18
17
  )
19
- from classiq.interface.model.port_declaration import PortDeclaration
20
18
  from classiq.interface.model.quantum_function_declaration import (
21
19
  PositionalArg,
22
- QuantumOperandDeclaration,
23
20
  )
24
21
  from classiq.interface.model.quantum_statement import QuantumStatement
25
22
  from classiq.interface.model.variable_declaration_statement import (
@@ -215,7 +212,6 @@ class OperationBuilder:
215
212
  self, function_context: FunctionContext, params: Sequence[PositionalArg]
216
213
  ) -> NativeFunctionDefinition:
217
214
  name = self._get_expanded_function_name(function_context)
218
- self._override_type_qualifier(function_context, params)
219
215
 
220
216
  return NativeFunctionDefinition(
221
217
  name=name,
@@ -248,44 +244,3 @@ class OperationBuilder:
248
244
  raise ClassiqInternalExpansionError("Could not allocate function name")
249
245
 
250
246
  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
@@ -114,7 +114,7 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, value: Any) -> Any:
114
114
  return [QTerminalCallable(inner_decl, index_=idx) for idx in range(len(value))]
115
115
  if (
116
116
  isinstance(value, QmodStructInstance)
117
- and param.classical_type.is_purely_declarative
117
+ and not param.classical_type.is_purely_generative
118
118
  ):
119
119
  classical_type = Struct(name=value.struct_declaration.name)
120
120
  classical_type.set_classical_struct_decl(value.struct_declaration)
@@ -104,10 +104,8 @@ class BaseInterpreter:
104
104
  main_closure.positional_arg_declarations, main_closure
105
105
  )
106
106
  context = self._expand_operation(main_closure)
107
- self._expanded_functions[main_closure.closure_id] = (
108
- self._builder.create_definition(
109
- cast(FunctionContext, context), main_closure.positional_arg_declarations
110
- )
107
+ self._expanded_functions[main_closure.name] = self._builder.create_definition(
108
+ cast(FunctionContext, context), main_closure.positional_arg_declarations
111
109
  )
112
110
 
113
111
  def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
@@ -275,19 +273,19 @@ class BaseInterpreter:
275
273
 
276
274
  def _expand_operation(self, operation: Closure) -> OperationContext:
277
275
  with self._builder.operation_context(operation) as context:
278
- if isinstance(operation, FunctionClosure) and (
279
- (func_def := self._expanded_functions.get(operation.closure_id))
280
- is not None
281
- ):
282
- cached_closure = self._top_level_scope[func_def.name].value
283
- operation.captured_vars.set(
284
- cached_closure.captured_vars, cached_closure, operation
285
- )
286
- else:
287
- self._expand_body(operation)
276
+ self._expand_body(operation)
288
277
 
289
278
  return context
290
279
 
280
+ def _expand_cached_function(
281
+ self, operation: FunctionClosure, func_def: NativeFunctionDefinition
282
+ ) -> None:
283
+ with self._builder.operation_context(operation):
284
+ cached_closure = self._top_level_scope[func_def.name].value
285
+ operation.captured_vars.set(
286
+ cached_closure.captured_vars, cached_closure, operation
287
+ )
288
+
291
289
  def _expand_body(self, operation: Closure) -> None:
292
290
  for block, block_body in operation.blocks.items():
293
291
  self._expand_block(block_body, block)
@@ -1,17 +1,12 @@
1
1
  import inspect
2
2
  import os
3
- from typing import Optional
4
3
 
5
4
  from pydantic import ValidationError
6
5
 
7
6
  from classiq.interface.exceptions import ClassiqError
8
7
  from classiq.interface.model.allocate import Allocate
9
8
  from classiq.interface.model.bind_operation import BindOperation
10
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
11
9
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
12
- from classiq.interface.model.quantum_function_declaration import (
13
- NamedParamsQuantumFunctionDeclaration,
14
- )
15
10
  from classiq.interface.source_reference import SourceReference
16
11
 
17
12
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
@@ -24,22 +19,10 @@ from classiq.model_expansions.quantum_operations.quantum_function_call import (
24
19
  DeclarativeQuantumFunctionCallEmitter,
25
20
  )
26
21
  from classiq.model_expansions.scope import Scope
27
- from classiq.model_expansions.visitors.symbolic_param_inference import (
28
- SymbolicParamInference,
29
- )
30
22
  from classiq.qmod.model_state_container import QMODULE
31
23
 
32
24
 
33
25
  class FrontendGenerativeInterpreter(GenerativeInterpreter):
34
- def infer_symbolic_parameters(
35
- self,
36
- functions: list[NativeFunctionDefinition],
37
- additional_signatures: Optional[
38
- list[NamedParamsQuantumFunctionDeclaration]
39
- ] = None,
40
- ) -> None:
41
- SymbolicParamInference(functions, additional_signatures).infer()
42
-
43
26
  def emit_allocate(self, allocate: Allocate) -> None:
44
27
  AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
45
28
 
@@ -15,6 +15,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
15
15
  )
16
16
  from classiq.interface.model.allocate import Allocate
17
17
  from classiq.interface.model.bind_operation import BindOperation
18
+ from classiq.interface.model.bounds import SetBoundsStatement
18
19
  from classiq.interface.model.classical_if import ClassicalIf
19
20
  from classiq.interface.model.control import Control
20
21
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
@@ -66,6 +67,7 @@ from classiq.model_expansions.quantum_operations.block_evaluator import (
66
67
  IfElimination,
67
68
  RepeatElimination,
68
69
  )
70
+ from classiq.model_expansions.quantum_operations.bounds import SetBoundsEmitter
69
71
  from classiq.model_expansions.quantum_operations.composite_emitter import (
70
72
  CompositeEmitter,
71
73
  )
@@ -297,6 +299,13 @@ class GenerativeInterpreter(BaseInterpreter):
297
299
  ],
298
300
  ).emit(phase)
299
301
 
302
+ @emit.register
303
+ def emit_set_bounds(self, op: SetBoundsStatement) -> None:
304
+ CompositeEmitter[SetBoundsStatement](
305
+ self,
306
+ [HandleEvaluator(self, "target"), SetBoundsEmitter(self)],
307
+ ).emit(op)
308
+
300
309
  def _expand_body(self, operation: Closure) -> None:
301
310
  if isinstance(operation, FunctionClosure) and operation.name == "permute":
302
311
  # special expansion since permute is generative
@@ -1,4 +1,7 @@
1
1
  from classiq.model_expansions.quantum_operations.bind import BindEmitter
2
+ from classiq.model_expansions.quantum_operations.bounds import (
3
+ SetBoundsEmitter,
4
+ )
2
5
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
3
6
  QuantumFunctionCallEmitter,
4
7
  )
@@ -10,7 +10,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
10
10
  )
11
11
  from classiq.interface.model.allocate import Allocate
12
12
  from classiq.interface.model.handle_binding import NestedHandleBinding
13
- from classiq.interface.model.quantum_type import QuantumBitvector
13
+ from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
14
14
 
15
15
  from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
16
16
  from classiq.model_expansions.quantum_operations.emitter import Emitter
@@ -38,6 +38,8 @@ class AllocateEmitter(Emitter[Allocate]):
38
38
  )
39
39
 
40
40
  size_expr = self._get_var_size(target, allocate.size)
41
+ if isinstance(target.quantum_type, QuantumNumeric):
42
+ target.quantum_type.set_bounds((0, 0))
41
43
  allocate = allocate.model_copy(
42
44
  update=dict(
43
45
  size=Expression(expr=size_expr),
@@ -37,15 +37,17 @@ from classiq.qmod.builtins.functions.standard_gates import CX
37
37
 
38
38
  class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
39
39
  def emit(self, op: QuantumAssignmentOperation, /) -> bool:
40
+ result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
41
+ result_type = result_symbol.quantum_type
42
+
40
43
  if not (
41
44
  isinstance(op, ArithmeticOperation)
42
45
  and op.operation_kind == ArithmeticOperationKind.Assignment
43
46
  ):
47
+ if isinstance(result_type, QuantumNumeric):
48
+ result_type.reset_bounds()
44
49
  return False
45
50
 
46
- result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
47
- result_type = result_symbol.quantum_type
48
-
49
51
  validate_assignment_bool_expression(
50
52
  result_symbol, op.expression.expr, op.operation_kind
51
53
  )
@@ -69,6 +71,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
69
71
  result_type, inferred_result_type, str(op.result_var)
70
72
  )
71
73
  self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
74
+ result_type.set_bounds(inferred_result_type.get_bounds())
72
75
  return True
73
76
 
74
77
  def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
@@ -105,6 +108,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
105
108
  result_type.fraction_digits = Expression(
106
109
  expr=str(inferred_result_type.fraction_digits_value)
107
110
  )
111
+ result_type.set_bounds(inferred_result_type.get_bounds())
108
112
 
109
113
  @staticmethod
110
114
  def _same_numeric_attributes(
@@ -1,3 +1,4 @@
1
+ from itertools import chain
1
2
  from typing import TYPE_CHECKING
2
3
 
3
4
  from classiq.interface.exceptions import (
@@ -5,6 +6,7 @@ from classiq.interface.exceptions import (
5
6
  ClassiqInternalExpansionError,
6
7
  )
7
8
  from classiq.interface.model.bind_operation import BindOperation
9
+ from classiq.interface.model.quantum_type import QuantumNumeric
8
10
 
9
11
  from classiq.model_expansions.evaluators.parameter_types import (
10
12
  evaluate_types_in_quantum_symbols,
@@ -29,8 +31,13 @@ class BindEmitter(Emitter[BindOperation]):
29
31
 
30
32
  def emit(self, bind: BindOperation, /) -> bool:
31
33
  inputs, outputs = self._get_inputs_outputs(bind)
32
- validate_bind_targets(bind, self._current_scope)
34
+ validate_bind_targets(bind, self._current_scope, self._allow_symbolic_size)
33
35
  self._process_var_sizes(bind, inputs, outputs)
36
+
37
+ for symbol in chain(inputs, outputs):
38
+ if isinstance(symbol.quantum_type, QuantumNumeric):
39
+ symbol.quantum_type.reset_bounds()
40
+
34
41
  self.emit_statement(
35
42
  BindOperation(
36
43
  in_handles=bind.in_handles,