classiq 0.78.0__py3-none-any.whl → 0.79.1__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 (48) hide show
  1. classiq/interface/_version.py +1 -1
  2. classiq/interface/generator/arith/arithmetic.py +10 -6
  3. classiq/interface/generator/arith/binary_ops.py +8 -11
  4. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +90 -23
  5. classiq/interface/generator/expressions/proxies/classical/utils.py +4 -0
  6. classiq/interface/generator/functions/classical_type.py +74 -0
  7. classiq/interface/generator/functions/concrete_types.py +3 -0
  8. classiq/interface/generator/functions/type_name.py +32 -3
  9. classiq/interface/generator/generated_circuit_data.py +3 -1
  10. classiq/interface/helpers/model_normalizer.py +47 -0
  11. classiq/interface/interface_version.py +1 -1
  12. classiq/interface/model/bounds.py +12 -0
  13. classiq/interface/model/model.py +9 -5
  14. classiq/interface/model/quantum_type.py +25 -3
  15. classiq/interface/model/statement_block.py +2 -0
  16. classiq/model_expansions/atomic_expression_functions_defs.py +20 -6
  17. classiq/model_expansions/evaluators/argument_types.py +20 -0
  18. classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
  19. classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
  20. classiq/model_expansions/generative_functions.py +1 -1
  21. classiq/model_expansions/interpreters/generative_interpreter.py +9 -0
  22. classiq/model_expansions/quantum_operations/__init__.py +3 -0
  23. classiq/model_expansions/quantum_operations/allocate.py +3 -1
  24. classiq/model_expansions/quantum_operations/assignment_result_processor.py +7 -3
  25. classiq/model_expansions/quantum_operations/bind.py +8 -1
  26. classiq/model_expansions/quantum_operations/bounds.py +30 -0
  27. classiq/model_expansions/quantum_operations/call_emitter.py +47 -7
  28. classiq/model_expansions/quantum_operations/function_calls_cache.py +8 -1
  29. classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
  30. classiq/model_expansions/scope.py +14 -4
  31. classiq/model_expansions/scope_initialization.py +1 -14
  32. classiq/model_expansions/visitors/symbolic_param_inference.py +32 -16
  33. classiq/model_expansions/visitors/variable_references.py +39 -43
  34. classiq/open_library/functions/linear_pauli_rotation.py +6 -6
  35. classiq/open_library/functions/state_preparation.py +1 -1
  36. classiq/open_library/functions/utility_functions.py +2 -2
  37. classiq/qmod/declaration_inferrer.py +5 -3
  38. classiq/qmod/model_state_container.py +21 -1
  39. classiq/qmod/native/pretty_printer.py +8 -0
  40. classiq/qmod/pretty_print/expression_to_python.py +8 -1
  41. classiq/qmod/pretty_print/pretty_printer.py +7 -0
  42. classiq/qmod/python_classical_type.py +1 -1
  43. classiq/qmod/qmod_parameter.py +43 -7
  44. classiq/qmod/semantics/annotation/qstruct_annotator.py +15 -4
  45. classiq/qmod/utilities.py +1 -1
  46. {classiq-0.78.0.dist-info → classiq-0.79.1.dist-info}/METADATA +1 -1
  47. {classiq-0.78.0.dist-info → classiq-0.79.1.dist-info}/RECORD +48 -45
  48. {classiq-0.78.0.dist-info → classiq-0.79.1.dist-info}/WHEEL +0 -0
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.78.0'
6
+ SEMVER_VERSION = '0.79.1'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -122,12 +122,16 @@ def get_arithmetic_params(
122
122
  def compute_arithmetic_result_type(
123
123
  expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
124
124
  ) -> QuantumNumeric:
125
- if is_zero(expr_str) or is_bool(expr_str):
126
- return QuantumNumeric(
127
- size=Expression(expr="1"),
128
- is_signed=Expression(expr="False"),
129
- fraction_digits=Expression(expr="0"),
130
- )
125
+ one_qbit_qnum = QuantumNumeric(
126
+ size=Expression(expr="1"),
127
+ is_signed=Expression(expr="False"),
128
+ fraction_digits=Expression(expr="0"),
129
+ )
130
+ if is_zero(expr_str):
131
+ one_qbit_qnum.set_bounds((0, 0))
132
+ return one_qbit_qnum
133
+ if is_bool(expr_str):
134
+ return one_qbit_qnum
131
135
  arith_param = get_arithmetic_params(expr_str, var_types, machine_precision)
132
136
  return register_info_to_quantum_type(
133
137
  arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
@@ -535,14 +535,11 @@ class Multiplier(BinaryOpWithFloatInputs):
535
535
  elif isinstance(right_arg, float) and right_arg == 1.0:
536
536
  assert isinstance(left_arg, RegisterArithmeticInfo)
537
537
  return left_arg.size
538
- largest_bound = max(bounds, key=abs)
539
- integer_places = int(largest_bound).bit_length() + int(largest_bound < 0)
540
- extra_sign_bit = int(
541
- argument_utils.is_signed(left_arg)
542
- and argument_utils.is_signed(right_arg)
543
- and largest_bound > 0
544
- )
545
- return max(1, integer_places + fraction_places + extra_sign_bit)
538
+ if bounds[0] == 0 and bounds[1] == 0:
539
+ return max(1, fraction_places)
540
+
541
+ integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
542
+ return integer_part_size + fraction_places
546
543
 
547
544
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
548
545
  return max(
@@ -610,7 +607,7 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
610
607
  )
611
608
  for bound in self.left_arg.bounds
612
609
  ]
613
- if (self.right_arg % 2) or min(bounds) >= 0:
610
+ if (self.right_arg % 2) or min(bounds) >= 0 or max(bounds) <= 0:
614
611
  return (
615
612
  number_utils.limit_fraction_places(
616
613
  bounds[0] ** self.right_arg,
@@ -633,8 +630,8 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
633
630
  fraction_places = min(self.machine_precision, self.expected_fraction_places())
634
631
  bounds = self._get_result_bounds()
635
632
  size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
636
- if bounds[0] == bounds[1]:
637
- size = 1
633
+ if bounds[0] == 0 and bounds[1] == 0:
634
+ size = max(1, fraction_places)
638
635
  return RegisterArithmeticInfo(
639
636
  size=size,
640
637
  is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
@@ -35,20 +35,13 @@ def _is_int(val: Any) -> TypeGuard[Union[int, sympy.Basic]]:
35
35
  return isinstance(val, int)
36
36
 
37
37
 
38
- class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
39
- def __init__(
40
- self,
41
- handle: HandleBinding,
42
- element_type: "ConcreteClassicalType",
43
- length: "ExpressionValue",
44
- ) -> None:
38
+ class ClassicalSequenceProxy(NonSymbolicExpr, ClassicalProxy):
39
+ def __init__(self, handle: HandleBinding) -> None:
45
40
  super().__init__(handle)
46
- self._element_type = element_type
47
- self._length = length
48
41
 
49
42
  @property
50
43
  def fields(self) -> Mapping[str, "ExpressionValue"]:
51
- return {"len": self._length}
44
+ return {"len": self.length}
52
45
 
53
46
  @property
54
47
  def type_name(self) -> str:
@@ -56,7 +49,7 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
56
49
 
57
50
  @property
58
51
  def length(self) -> "ExpressionValue":
59
- return self._length
52
+ raise NotImplementedError
60
53
 
61
54
  def __getitem__(
62
55
  self, key: Union[slice, int, Integer, ClassicalProxy]
@@ -73,17 +66,12 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
73
66
  stop = int(stop_)
74
67
  if start >= stop:
75
68
  raise ClassiqIndexError("Array slice has non-positive length")
76
- if start < 0 or (isinstance(self._length, int) and stop > self._length):
69
+ if start < 0 or (isinstance(self.length, int) and stop > self.length):
77
70
  raise ClassiqIndexError("Array slice is out of bounds")
78
- return ClassicalArrayProxy(
79
- SlicedHandleBinding(
80
- base_handle=self.handle,
81
- start=Expression(expr=str(start_)),
82
- end=Expression(expr=str(stop_)),
83
- ),
84
- self._element_type,
85
- stop_ - start_,
86
- )
71
+ return self.get_slice_at(start_, stop_)
72
+
73
+ def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
74
+ raise NotImplementedError
87
75
 
88
76
  def _get_subscript(
89
77
  self, index_: Union[int, Integer, ClassicalProxy]
@@ -94,10 +82,89 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
94
82
  raise ClassiqIndexError(
95
83
  "Array index is out of bounds (negative indices are not supported)"
96
84
  )
97
- if isinstance(self._length, int) and index >= self._length:
85
+ if isinstance(self.length, int) and index >= self.length:
98
86
  raise ClassiqIndexError("Array index is out of bounds")
87
+ return self.get_subscript_at(index_)
88
+
89
+ def get_subscript_at(self, index: Any) -> ClassicalProxy:
90
+ raise NotImplementedError
91
+
92
+
93
+ class ClassicalArrayProxy(ClassicalSequenceProxy):
94
+ def __init__(
95
+ self,
96
+ handle: HandleBinding,
97
+ element_type: "ConcreteClassicalType",
98
+ length: "ExpressionValue",
99
+ ) -> None:
100
+ super().__init__(handle)
101
+ self._element_type = element_type
102
+ if _is_int(length):
103
+ length = int(length)
104
+ self._length = length
105
+
106
+ @property
107
+ def length(self) -> "ExpressionValue":
108
+ return self._length
109
+
110
+ def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
111
+ return ClassicalArrayProxy(
112
+ SlicedHandleBinding(
113
+ base_handle=self.handle,
114
+ start=Expression(expr=str(start)),
115
+ end=Expression(expr=str(stop)),
116
+ ),
117
+ self._element_type,
118
+ stop - start,
119
+ )
120
+
121
+ def get_subscript_at(self, index: Any) -> ClassicalProxy:
99
122
  return self._element_type.get_classical_proxy(
100
123
  SubscriptHandleBinding(
101
- base_handle=self._handle, index=Expression(expr=str(index_))
124
+ base_handle=self._handle, index=Expression(expr=str(index))
125
+ )
126
+ )
127
+
128
+
129
+ class ClassicalTupleProxy(ClassicalSequenceProxy):
130
+ def __init__(
131
+ self, handle: HandleBinding, element_types: list["ConcreteClassicalType"]
132
+ ) -> None:
133
+ super().__init__(handle)
134
+ self._element_types = element_types
135
+
136
+ @property
137
+ def length(self) -> int:
138
+ return len(self._element_types)
139
+
140
+ def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
141
+ handle = SlicedHandleBinding(
142
+ base_handle=self.handle,
143
+ start=Expression(expr=str(start)),
144
+ end=Expression(expr=str(stop)),
145
+ )
146
+ if (_is_int(start) or start is None) and (_is_int(stop) or stop is None):
147
+ return ClassicalTupleProxy(
148
+ handle, self._element_types.__getitem__(slice(start, stop))
149
+ )
150
+ return ClassicalArrayProxy(
151
+ handle,
152
+ self._element_types[0].get_raw_type(),
153
+ stop - start,
154
+ )
155
+
156
+ def get_subscript_at(self, index: Any) -> ClassicalProxy:
157
+ handle = SubscriptHandleBinding(
158
+ base_handle=self._handle, index=Expression(expr=str(index))
159
+ )
160
+ if _is_int(index):
161
+ return self._element_types[int(index)].get_classical_proxy(handle)
162
+ return (
163
+ self._element_types[0]
164
+ .get_raw_type()
165
+ .get_classical_proxy(
166
+ SubscriptHandleBinding(
167
+ base_handle=self._handle, index=Expression(expr=str(index))
168
+ )
102
169
  )
103
170
  )
@@ -1,6 +1,7 @@
1
1
  from classiq.interface.exceptions import ClassiqInternalExpansionError
2
2
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
3
3
  ClassicalArrayProxy,
4
+ ClassicalTupleProxy,
4
5
  )
5
6
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
6
7
  ClassicalProxy,
@@ -14,6 +15,7 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
14
15
  from classiq.interface.generator.functions.classical_type import (
15
16
  ClassicalArray,
16
17
  ClassicalList,
18
+ ClassicalTuple,
17
19
  ClassicalType,
18
20
  )
19
21
  from classiq.interface.generator.functions.type_name import Struct
@@ -26,6 +28,8 @@ def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
26
28
  if not isinstance(proxy.length, int):
27
29
  return ClassicalList(element_type=proxy._element_type)
28
30
  return ClassicalArray(element_type=proxy._element_type, size=proxy.length)
31
+ if isinstance(proxy, ClassicalTupleProxy):
32
+ return ClassicalTuple(element_types=proxy._element_types)
29
33
  if isinstance(proxy, ClassicalStructProxy):
30
34
  classical_type = Struct(name=proxy._decl.name)
31
35
  classical_type.set_classical_struct_decl(proxy._decl)
@@ -1,3 +1,4 @@
1
+ from itertools import chain
1
2
  from typing import TYPE_CHECKING, Any, Literal
2
3
 
3
4
  import pydantic
@@ -11,6 +12,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
11
12
  )
12
13
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
13
14
  ClassicalArrayProxy,
15
+ ClassicalTupleProxy,
14
16
  )
15
17
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
16
18
  ClassicalProxy,
@@ -47,6 +49,10 @@ class ClassicalType(HashableASTNode):
47
49
  def is_purely_declarative(self) -> bool:
48
50
  return not self._is_generative
49
51
 
52
+ @property
53
+ def is_purely_generative(self) -> bool:
54
+ return self._is_generative
55
+
50
56
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
51
57
  return ClassicalScalarProxy(handle, self)
52
58
 
@@ -59,6 +65,9 @@ class ClassicalType(HashableASTNode):
59
65
  res._is_generative = False
60
66
  return res
61
67
 
68
+ def get_raw_type(self) -> "ConcreteClassicalType":
69
+ return self # type:ignore[return-value]
70
+
62
71
 
63
72
  class Integer(ClassicalType):
64
73
  kind: Literal["int"]
@@ -109,6 +118,16 @@ class ClassicalList(ClassicalType):
109
118
  def is_purely_declarative(self) -> bool:
110
119
  return super().is_purely_declarative and self.element_type.is_purely_declarative
111
120
 
121
+ @property
122
+ def is_purely_generative(self) -> bool:
123
+ return super().is_purely_generative and self.element_type.is_purely_generative
124
+
125
+ def get_raw_type(self) -> "ConcreteClassicalType":
126
+ raw_type = ClassicalList(element_type=self.element_type.get_raw_type())
127
+ if self._is_generative:
128
+ raw_type.set_generative()
129
+ return raw_type
130
+
112
131
 
113
132
  class StructMetaType(ClassicalType):
114
133
  kind: Literal["type_proxy"]
@@ -143,6 +162,61 @@ class ClassicalArray(ClassicalType):
143
162
  def is_purely_declarative(self) -> bool:
144
163
  return super().is_purely_declarative and self.element_type.is_purely_declarative
145
164
 
165
+ @property
166
+ def is_purely_generative(self) -> bool:
167
+ return super().is_purely_generative and self.element_type.is_purely_generative
168
+
169
+ def get_raw_type(self) -> "ConcreteClassicalType":
170
+ raw_type = ClassicalList(element_type=self.element_type.get_raw_type())
171
+ if self._is_generative:
172
+ raw_type.set_generative()
173
+ return raw_type
174
+
175
+
176
+ class ClassicalTuple(ClassicalType):
177
+ kind: Literal["tuple"]
178
+ element_types: list["ConcreteClassicalType"]
179
+
180
+ @pydantic.model_validator(mode="before")
181
+ @classmethod
182
+ def _set_kind(cls, values: Any) -> dict[str, Any]:
183
+ return values_with_discriminator(values, "kind", "tuple")
184
+
185
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
186
+ return ClassicalTupleProxy(handle=handle, element_types=self.element_types)
187
+
188
+ @property
189
+ def expressions(self) -> list[Expression]:
190
+ return list(
191
+ chain.from_iterable(
192
+ element_type.expressions for element_type in self.element_types
193
+ )
194
+ )
195
+
196
+ @property
197
+ def is_purely_declarative(self) -> bool:
198
+ return super().is_purely_declarative and all(
199
+ element_type.is_purely_declarative for element_type in self.element_types
200
+ )
201
+
202
+ @property
203
+ def is_purely_generative(self) -> bool:
204
+ return super().is_purely_generative and all(
205
+ element_type.is_purely_generative for element_type in self.element_types
206
+ )
207
+
208
+ def get_raw_type(self) -> "ConcreteClassicalType":
209
+ if len(self.element_types) == 0:
210
+ return self
211
+ raw_type = ClassicalList(element_type=self.element_types[0].get_raw_type())
212
+ if self._is_generative:
213
+ raw_type.set_generative()
214
+ return raw_type
215
+
216
+ @property
217
+ def size(self) -> int:
218
+ return len(self.element_types)
219
+
146
220
 
147
221
  class OpaqueHandle(ClassicalType):
148
222
  pass
@@ -6,6 +6,7 @@ from classiq.interface.generator.functions.classical_type import (
6
6
  Bool,
7
7
  ClassicalArray,
8
8
  ClassicalList,
9
+ ClassicalTuple,
9
10
  Estimation,
10
11
  Histogram,
11
12
  Integer,
@@ -32,6 +33,7 @@ ConcreteClassicalType = Annotated[
32
33
  StructMetaType,
33
34
  TypeName,
34
35
  ClassicalArray,
36
+ ClassicalTuple,
35
37
  VQEResult,
36
38
  Histogram,
37
39
  Estimation,
@@ -41,6 +43,7 @@ ConcreteClassicalType = Annotated[
41
43
  ]
42
44
  ClassicalList.model_rebuild()
43
45
  ClassicalArray.model_rebuild()
46
+ ClassicalTuple.model_rebuild()
44
47
 
45
48
  NativePythonClassicalTypes = (int, float, bool, list)
46
49
  PythonClassicalPydanticTypes = (Enum,)
@@ -28,7 +28,10 @@ from classiq.interface.model.quantum_type import (
28
28
  )
29
29
 
30
30
  if TYPE_CHECKING:
31
- from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
31
+ from classiq.interface.generator.functions.concrete_types import (
32
+ ConcreteClassicalType,
33
+ ConcreteQuantumType,
34
+ )
32
35
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
33
36
 
34
37
 
@@ -138,16 +141,42 @@ class TypeName(ClassicalType, QuantumType):
138
141
  @property
139
142
  def is_purely_declarative(self) -> bool:
140
143
  if self.is_enum:
141
- return False
142
- return super().is_purely_declarative and all(
144
+ return not self.is_generative
145
+ return all(
143
146
  field_type.is_purely_declarative
144
147
  for field_type in self.classical_struct_decl.variables.values()
145
148
  )
146
149
 
150
+ @property
151
+ def is_purely_generative(self) -> bool:
152
+ if self.is_enum:
153
+ return self.is_generative
154
+ return all(
155
+ field_type.is_purely_generative
156
+ for field_type in self.classical_struct_decl.variables.values()
157
+ )
158
+
147
159
  @property
148
160
  def is_enum(self) -> bool:
149
161
  return not self.has_fields and not self.has_classical_struct_decl
150
162
 
163
+ def get_raw_type(self) -> "ConcreteClassicalType":
164
+ if self.is_enum:
165
+ return self
166
+ if TYPE_CHECKING:
167
+ assert self._classical_struct_decl is not None
168
+ raw_type = TypeName(name=self.name)
169
+ raw_decl = self._classical_struct_decl.model_copy(
170
+ update=dict(
171
+ variables={
172
+ field_name: field_type.get_raw_type()
173
+ for field_name, field_type in self._classical_struct_decl.variables.items()
174
+ }
175
+ )
176
+ )
177
+ raw_type.set_classical_struct_decl(raw_decl)
178
+ return raw_type
179
+
151
180
 
152
181
  class Enum(TypeName):
153
182
  pass
@@ -210,7 +210,9 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
210
210
  return generated_name
211
211
 
212
212
  if isinstance(back_ref, QuantumFunctionCall):
213
- name = generate_original_function_name(back_ref.func_name)
213
+ name = generate_original_function_name(back_ref.func_name).removeprefix(
214
+ ARITH_ENGINE_PREFIX
215
+ )
214
216
  return self.add_suffix_from_generated_name(generated_name, name)
215
217
 
216
218
  statement_kind: str = back_ref.kind
@@ -0,0 +1,47 @@
1
+ import ast
2
+ from typing import Any
3
+
4
+ from classiq.interface.ast_node import ASTNode
5
+ from classiq.interface.debug_info.debug_info import DebugInfoCollection
6
+ from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.functions.classical_type import ClassicalType
8
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
9
+ from classiq.interface.generator.visitor import Transformer, Visitor
10
+ from classiq.interface.model.model import Model
11
+ from classiq.interface.model.port_declaration import AnonPortDeclaration
12
+
13
+ from classiq.model_expansions.transformers.model_renamer import ExprNormalizer
14
+
15
+
16
+ class ModelNormalizer(Visitor):
17
+ def visit(self, node: Any) -> None:
18
+ if isinstance(node, ASTNode):
19
+ node.model_config["frozen"] = False
20
+ node.source_ref = None
21
+ node.back_ref = None
22
+ if hasattr(node, "uuid"):
23
+ node.uuid = None
24
+ super().visit(node)
25
+
26
+ def visit_Model(self, model: Model) -> None:
27
+ model.debug_info = DebugInfoCollection()
28
+ model.functions.sort(key=lambda x: x.name)
29
+ self.generic_visit(model)
30
+
31
+ def visit_Expression(self, expr: Expression) -> None:
32
+ expr.expr = ast.unparse(ExprNormalizer().visit(ast.parse(expr.expr)))
33
+
34
+ def visit_AnonPortDeclaration(self, decl: AnonPortDeclaration) -> None:
35
+ decl.type_qualifier = TypeQualifier.Quantum
36
+
37
+
38
+ class ClearModelInternals(Transformer):
39
+ def visit_Expression(self, expr: Expression) -> Expression:
40
+ if expr._evaluated_expr is not None and not expr._evaluated_expr.is_constant():
41
+ expr._evaluated_expr = None
42
+ return expr
43
+
44
+ def visit_ClassicalType(self, classical_type: ClassicalType) -> ClassicalType:
45
+ return type(classical_type).model_validate_json(
46
+ classical_type.model_dump_json()
47
+ )
@@ -1 +1 @@
1
- INTERFACE_VERSION = "10"
1
+ INTERFACE_VERSION = "11"
@@ -0,0 +1,12 @@
1
+ from typing import Literal, Optional
2
+
3
+ from classiq.interface.helpers.custom_pydantic_types import PydanticFloatTuple
4
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding
5
+ from classiq.interface.model.quantum_statement import QuantumOperation
6
+
7
+
8
+ class SetBoundsStatement(QuantumOperation):
9
+ kind: Literal["SetBoundsStatement"]
10
+
11
+ target: ConcreteHandleBinding
12
+ bounds: Optional[PydanticFloatTuple]
@@ -1,6 +1,6 @@
1
- from collections import Counter
1
+ from collections import Counter, defaultdict
2
2
  from collections.abc import Mapping
3
- from typing import Any, Literal, NewType
3
+ from typing import Annotated, Any, Literal, NewType
4
4
 
5
5
  import pydantic
6
6
 
@@ -99,9 +99,13 @@ class Model(VersionedModel, ASTNode):
99
99
  debug_info: DebugInfoCollection = pydantic.Field(
100
100
  default_factory=DebugInfoCollection
101
101
  )
102
- functions_compilation_metadata: dict[str, CompilationMetadata] = pydantic.Field(
103
- default_factory=dict
104
- )
102
+ functions_compilation_metadata: defaultdict[
103
+ str,
104
+ Annotated[
105
+ CompilationMetadata,
106
+ pydantic.Field(default_factory=CompilationMetadata),
107
+ ],
108
+ ] = pydantic.Field(default_factory=lambda: defaultdict(CompilationMetadata))
105
109
 
106
110
  @property
107
111
  def main_func(self) -> NativeFunctionDefinition:
@@ -22,6 +22,7 @@ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy
22
22
  from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
23
23
  QmodSizedProxy,
24
24
  )
25
+ from classiq.interface.helpers.custom_pydantic_types import PydanticFloatTuple
25
26
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
26
27
  from classiq.interface.model.handle_binding import HandleBinding
27
28
 
@@ -193,6 +194,8 @@ class QuantumNumeric(QuantumScalar):
193
194
  is_signed: Optional[Expression] = pydantic.Field(default=None)
194
195
  fraction_digits: Optional[Expression] = pydantic.Field(default=None)
195
196
 
197
+ _bounds: Optional[PydanticFloatTuple] = pydantic.PrivateAttr(default=None)
198
+
196
199
  @pydantic.model_validator(mode="before")
197
200
  @classmethod
198
201
  def _set_kind(cls, values: Any) -> dict[str, Any]:
@@ -287,6 +290,22 @@ class QuantumNumeric(QuantumScalar):
287
290
  exprs.append(self.fraction_digits)
288
291
  return exprs
289
292
 
293
+ def get_bounds(self) -> Optional[tuple[float, float]]:
294
+ return self._bounds
295
+
296
+ def set_bounds(self, bounds: Optional[tuple[float, float]]) -> None:
297
+ self._bounds = bounds
298
+
299
+ def reset_bounds(self) -> None:
300
+ self.set_bounds(None)
301
+
302
+ def get_maximal_bounds(self) -> tuple[float, float]:
303
+ return RegisterArithmeticInfo.get_maximal_bounds(
304
+ size=self.size_in_bits,
305
+ is_signed=self.sign_value,
306
+ fraction_places=self.fraction_digits_value,
307
+ )
308
+
290
309
 
291
310
  class RegisterQuantumType(BaseModel):
292
311
  quantum_types: "ConcreteQuantumType"
@@ -308,6 +327,7 @@ def register_info_to_quantum_type(reg_info: RegisterArithmeticInfo) -> QuantumNu
308
327
  result.set_size_in_bits(reg_info.size)
309
328
  result.is_signed = Expression(expr=str(reg_info.is_signed))
310
329
  result.fraction_digits = Expression(expr=str(reg_info.fraction_places))
330
+ result.set_bounds(reg_info.bounds)
311
331
  return result
312
332
 
313
333
 
@@ -315,17 +335,19 @@ UNRESOLVED_SIZE = 1000
315
335
 
316
336
 
317
337
  def quantum_var_to_register(name: str, qtype: QuantumType) -> RegisterUserInput:
338
+ signed: bool = False
339
+ fraction_places: int = 0
340
+ bounds: Optional[tuple[float, float]] = None
318
341
  if isinstance(qtype, QuantumNumeric):
319
342
  signed = qtype.sign_value
320
343
  fraction_places = qtype.fraction_digits_value
321
- else:
322
- signed = False
323
- fraction_places = 0
344
+ bounds = qtype.get_bounds()
324
345
  return RegisterUserInput(
325
346
  name=name,
326
347
  size=qtype.size_in_bits if qtype.has_size_in_bits else UNRESOLVED_SIZE,
327
348
  is_signed=signed,
328
349
  fraction_places=fraction_places,
350
+ bounds=bounds,
329
351
  )
330
352
 
331
353
 
@@ -5,6 +5,7 @@ from pydantic import Field
5
5
  from classiq.interface.model.allocate import Allocate
6
6
  from classiq.interface.model.bind_operation import BindOperation
7
7
  from classiq.interface.model.block import Block
8
+ from classiq.interface.model.bounds import SetBoundsStatement
8
9
  from classiq.interface.model.classical_if import ClassicalIf
9
10
  from classiq.interface.model.control import Control
10
11
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
@@ -51,6 +52,7 @@ ConcreteQuantumStatement = Annotated[
51
52
  Compute,
52
53
  Action,
53
54
  Uncompute,
55
+ SetBoundsStatement,
54
56
  ],
55
57
  Field(..., discriminator="kind"),
56
58
  ]