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
@@ -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
@@ -145,6 +145,8 @@ class StatementType(StrEnum):
145
145
  POWER = "power"
146
146
  INVERT = "invert"
147
147
  WITHIN_APPLY = "within apply"
148
+ WITHIN = "within"
149
+ APPLY = "apply"
148
150
  ASSIGN = "assign"
149
151
  ASSIGN_AMPLITUDE = "assign amplitude"
150
152
  PHASE = "phase"
@@ -161,6 +163,9 @@ STATEMENTS_NAME: dict[str, StatementType] = {
161
163
  "Power": StatementType.POWER,
162
164
  "Invert": StatementType.INVERT,
163
165
  "WithinApply": StatementType.WITHIN_APPLY,
166
+ "Compute": StatementType.WITHIN,
167
+ "Action": StatementType.APPLY,
168
+ "Uncompute": StatementType.WITHIN,
164
169
  ArithmeticOperationKind.Assignment.value: StatementType.ASSIGN,
165
170
  "InplaceBinaryOperation": StatementType.ASSIGN,
166
171
  "AmplitudeLoadingOperation": StatementType.ASSIGN_AMPLITUDE,
@@ -176,6 +181,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
176
181
  children: list["FunctionDebugInfoInterface"]
177
182
  relative_qubits: tuple[int, ...]
178
183
  absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
184
+ control_variable: Optional[str] = Field(default=None)
179
185
  is_basis_gate: Optional[bool] = Field(default=None)
180
186
  is_inverse: bool = Field(default=False)
181
187
  is_unitary: bool = Field(default=True, exclude=True)
@@ -204,15 +210,24 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
204
210
  return generated_name
205
211
 
206
212
  if isinstance(back_ref, QuantumFunctionCall):
207
- name = generate_original_function_name(back_ref.func_name)
208
- if part_match := PART_SUFFIX_REGEX.match(generated_name):
209
- name += f" [{part_match.group(1)}]"
210
- return name.removeprefix(ARITH_ENGINE_PREFIX)
213
+ name = generate_original_function_name(back_ref.func_name).removeprefix(
214
+ ARITH_ENGINE_PREFIX
215
+ )
216
+ return self.add_suffix_from_generated_name(generated_name, name)
211
217
 
212
218
  statement_kind: str = back_ref.kind
213
219
  if isinstance(back_ref, ArithmeticOperation):
214
220
  statement_kind = back_ref.operation_kind.value
215
- return STATEMENTS_NAME[statement_kind]
221
+ return self.add_suffix_from_generated_name(
222
+ generated_name, STATEMENTS_NAME[statement_kind]
223
+ )
224
+
225
+ def add_suffix_from_generated_name(self, generated_name: str, name: str) -> str:
226
+ if part_match := PART_SUFFIX_REGEX.match(generated_name):
227
+ suffix = f" [{part_match.group(1)}]"
228
+ else:
229
+ suffix = ""
230
+ return f"{name}{suffix}"
216
231
 
217
232
  @property
218
233
  def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
@@ -347,9 +362,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
347
362
  if role is RegisterRole.INPUT:
348
363
  return RegisterRole.OUTPUT
349
364
  if role is RegisterRole.EXPLICIT_ZERO_INPUT or role is RegisterRole.ZERO_INPUT:
350
- if self.release_by_inverse:
351
- return RegisterRole.ZERO_OUTPUT
352
- return RegisterRole.OUTPUT
365
+ return RegisterRole.ZERO_OUTPUT
353
366
  if role is RegisterRole.AUXILIARY:
354
367
  return RegisterRole.AUXILIARY
355
368
  if role is RegisterRole.OUTPUT or role is RegisterRole.GARBAGE_OUTPUT:
@@ -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
+ )
@@ -15,6 +15,7 @@ from classiq.interface.helpers.versioned_model import VersionedModel
15
15
 
16
16
  class OperationType(StrEnum):
17
17
  REGULAR = "REGULAR"
18
+ INVISIBLE = "INVISIBLE"
18
19
  ALLOCATE = "ALLOCATE"
19
20
  FREE = "FREE"
20
21
  BIND = "BIND"
@@ -124,6 +125,7 @@ class Operation(pydantic.BaseModel):
124
125
  )
125
126
  is_daggered: bool = pydantic.Field(default=False)
126
127
  expanded: bool = pydantic.Field(default=False)
128
+ show_expanded_label: bool = pydantic.Field(default=False)
127
129
 
128
130
 
129
131
  class ProgramVisualModel(VersionedModel):
@@ -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
  ]