classiq 0.43.3__py3-none-any.whl → 0.45.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 (220) hide show
  1. classiq/__init__.py +8 -3
  2. classiq/_internals/api_wrapper.py +2 -2
  3. classiq/_internals/authentication/auth0.py +1 -1
  4. classiq/_internals/authentication/device.py +5 -1
  5. classiq/_internals/authentication/token_manager.py +5 -4
  6. classiq/_internals/client.py +5 -8
  7. classiq/_internals/config.py +1 -2
  8. classiq/_internals/host_checker.py +34 -13
  9. classiq/_internals/jobs.py +3 -3
  10. classiq/analyzer/analyzer.py +1 -1
  11. classiq/analyzer/analyzer_utilities.py +1 -1
  12. classiq/analyzer/rb.py +1 -1
  13. classiq/applications/chemistry/chemistry_model_constructor.py +13 -7
  14. classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
  15. classiq/applications/combinatorial_helpers/arithmetic/isolation.py +1 -1
  16. classiq/applications/combinatorial_helpers/encoding_mapping.py +1 -1
  17. classiq/applications/combinatorial_helpers/encoding_utils.py +2 -1
  18. classiq/applications/combinatorial_helpers/optimization_model.py +1 -1
  19. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
  20. classiq/applications/combinatorial_helpers/pyomo_utils.py +1 -2
  21. classiq/applications/combinatorial_helpers/transformations/encoding.py +1 -1
  22. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +5 -4
  23. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +1 -1
  24. classiq/applications/combinatorial_helpers/transformations/sign_seperation.py +1 -1
  25. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  26. classiq/applications/finance/finance_model_constructor.py +4 -3
  27. classiq/applications/grover/grover_model_constructor.py +7 -5
  28. classiq/applications/hamiltonian/__init__.py +0 -0
  29. classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
  30. classiq/applications/qnn/circuit_utils.py +1 -1
  31. classiq/applications/qnn/datasets/dataset_base_classes.py +2 -1
  32. classiq/applications/qnn/datasets/dataset_not.py +2 -1
  33. classiq/applications/qnn/qlayer.py +3 -2
  34. classiq/applications/qnn/torch_utils.py +2 -1
  35. classiq/applications/qsvm/qsvm_model_constructor.py +1 -1
  36. classiq/execution/execution_session.py +1 -1
  37. classiq/execution/jobs.py +5 -2
  38. classiq/interface/_version.py +1 -1
  39. classiq/interface/analyzer/cytoscape_graph.py +1 -2
  40. classiq/interface/analyzer/result.py +2 -3
  41. classiq/interface/ast_node.py +1 -18
  42. classiq/interface/backend/backend_preferences.py +11 -18
  43. classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
  44. classiq/interface/backend/pydantic_backend.py +0 -5
  45. classiq/interface/backend/quantum_backend_providers.py +4 -3
  46. classiq/interface/chemistry/fermionic_operator.py +1 -2
  47. classiq/interface/chemistry/ground_state_problem.py +2 -3
  48. classiq/interface/chemistry/molecule.py +1 -2
  49. classiq/interface/chemistry/operator.py +8 -10
  50. classiq/interface/combinatorial_optimization/encoding_types.py +1 -1
  51. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +1 -1
  52. classiq/interface/combinatorial_optimization/solver_types.py +1 -1
  53. classiq/interface/debug_info/__init__.py +0 -0
  54. classiq/interface/debug_info/debug_info.py +32 -0
  55. classiq/{exceptions.py → interface/exceptions.py} +4 -0
  56. classiq/interface/executor/aws_execution_cost.py +2 -2
  57. classiq/interface/executor/execution_preferences.py +2 -47
  58. classiq/interface/executor/execution_result.py +1 -2
  59. classiq/interface/executor/optimizer_preferences.py +2 -3
  60. classiq/interface/executor/quantum_code.py +1 -2
  61. classiq/interface/executor/quantum_instruction_set.py +2 -2
  62. classiq/interface/executor/register_initialization.py +1 -2
  63. classiq/interface/executor/result.py +29 -14
  64. classiq/interface/finance/function_input.py +6 -11
  65. classiq/interface/generator/amplitude_loading.py +2 -3
  66. classiq/interface/generator/ansatz_library.py +1 -1
  67. classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
  68. classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
  69. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
  70. classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
  71. classiq/interface/generator/application_apis/finance_declarations.py +37 -44
  72. classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
  73. classiq/interface/generator/arith/arithmetic.py +10 -8
  74. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +1 -2
  75. classiq/interface/generator/arith/arithmetic_expression_abc.py +22 -3
  76. classiq/interface/generator/arith/arithmetic_expression_parser.py +3 -4
  77. classiq/interface/generator/arith/arithmetic_expression_validator.py +1 -2
  78. classiq/interface/generator/arith/arithmetic_param_getters.py +1 -2
  79. classiq/interface/generator/arith/arithmetic_result_builder.py +15 -11
  80. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  81. classiq/interface/generator/arith/binary_ops.py +7 -7
  82. classiq/interface/generator/arith/endianness.py +1 -1
  83. classiq/interface/generator/arith/extremum_operations.py +44 -21
  84. classiq/interface/generator/arith/logical_ops.py +1 -2
  85. classiq/interface/generator/arith/register_user_input.py +1 -2
  86. classiq/interface/generator/arith/unary_ops.py +1 -2
  87. classiq/interface/generator/arith/uncomputation_methods.py +1 -1
  88. classiq/interface/generator/chemistry_function_params.py +1 -2
  89. classiq/interface/generator/circuit_code/circuit_code.py +1 -2
  90. classiq/interface/generator/circuit_code/types_and_constants.py +1 -2
  91. classiq/interface/generator/commuting_pauli_exponentiation.py +1 -2
  92. classiq/interface/generator/constant.py +1 -1
  93. classiq/interface/generator/control_state.py +1 -2
  94. classiq/interface/generator/custom_ansatz.py +1 -2
  95. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
  96. classiq/interface/generator/expressions/enums/finance_functions.py +4 -5
  97. classiq/interface/generator/expressions/evaluated_expression.py +1 -2
  98. classiq/interface/generator/expressions/expression.py +1 -2
  99. classiq/interface/generator/expressions/expression_constants.py +3 -1
  100. classiq/interface/generator/expressions/non_symbolic_expr.py +1 -1
  101. classiq/interface/generator/expressions/qmod_qarray_proxy.py +53 -70
  102. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +2 -7
  103. classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
  104. classiq/interface/generator/expressions/qmod_sized_proxy.py +1 -1
  105. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  106. classiq/interface/generator/function_params.py +2 -3
  107. classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
  108. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
  109. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
  110. classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
  111. classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
  112. classiq/interface/generator/functions/classical_function_declaration.py +14 -6
  113. classiq/interface/generator/functions/classical_type.py +7 -114
  114. classiq/interface/generator/functions/concrete_types.py +55 -0
  115. classiq/interface/generator/functions/function_declaration.py +10 -10
  116. classiq/interface/generator/functions/port_declaration.py +1 -2
  117. classiq/interface/generator/functions/type_name.py +80 -0
  118. classiq/interface/generator/generated_circuit_data.py +3 -3
  119. classiq/interface/generator/grover_diffuser.py +1 -2
  120. classiq/interface/generator/grover_operator.py +1 -2
  121. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +1 -2
  122. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +1 -2
  123. classiq/interface/generator/hardware/hardware_data.py +1 -2
  124. classiq/interface/generator/hardware_efficient_ansatz.py +2 -3
  125. classiq/interface/generator/hartree_fock.py +1 -2
  126. classiq/interface/generator/linear_pauli_rotations.py +1 -2
  127. classiq/interface/generator/mcmt_method.py +1 -1
  128. classiq/interface/generator/mcu.py +1 -2
  129. classiq/interface/generator/mcx.py +1 -2
  130. classiq/interface/generator/model/constraints.py +2 -3
  131. classiq/interface/generator/model/model.py +12 -2
  132. classiq/interface/generator/model/preferences/preferences.py +7 -3
  133. classiq/interface/generator/model/quantum_register.py +1 -2
  134. classiq/interface/generator/oracles/arithmetic_oracle.py +1 -2
  135. classiq/interface/generator/oracles/custom_oracle.py +1 -2
  136. classiq/interface/generator/oracles/oracle_abc.py +1 -2
  137. classiq/interface/generator/partitioned_register.py +1 -2
  138. classiq/interface/generator/piecewise_linear_amplitude_loading.py +1 -2
  139. classiq/interface/generator/preferences/optimization.py +1 -2
  140. classiq/interface/generator/qpe.py +1 -2
  141. classiq/interface/generator/qsvm.py +2 -3
  142. classiq/interface/generator/quantum_function_call.py +4 -2
  143. classiq/interface/generator/quantum_program.py +6 -7
  144. classiq/interface/generator/range_types.py +1 -1
  145. classiq/interface/generator/register_role.py +8 -2
  146. classiq/interface/generator/slice_parsing_utils.py +1 -2
  147. classiq/interface/generator/standard_gates/controlled_standard_gates.py +1 -2
  148. classiq/interface/generator/state_preparation/metrics.py +2 -3
  149. classiq/interface/generator/state_preparation/state_preparation.py +1 -2
  150. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
  151. classiq/interface/generator/transpiler_basis_gates.py +1 -1
  152. classiq/interface/generator/types/builtin_enum_declarations.py +38 -45
  153. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
  154. classiq/interface/generator/types/enum_declaration.py +1 -2
  155. classiq/interface/generator/types/qstruct_declaration.py +17 -0
  156. classiq/interface/generator/types/struct_declaration.py +2 -3
  157. classiq/interface/generator/ucc.py +1 -2
  158. classiq/interface/generator/unitary_gate.py +1 -2
  159. classiq/interface/generator/validations/flow_graph.py +1 -2
  160. classiq/interface/generator/validations/validator_functions.py +1 -2
  161. classiq/interface/hardware.py +1 -1
  162. classiq/interface/helpers/validation_helpers.py +2 -19
  163. classiq/interface/ide/visual_model.py +10 -4
  164. classiq/interface/interface_version.py +1 -0
  165. classiq/interface/jobs.py +2 -3
  166. classiq/interface/model/bind_operation.py +26 -7
  167. classiq/interface/model/classical_parameter_declaration.py +8 -5
  168. classiq/interface/model/control.py +5 -5
  169. classiq/interface/model/handle_binding.py +185 -12
  170. classiq/interface/model/inplace_binary_operation.py +17 -6
  171. classiq/interface/model/model.py +29 -7
  172. classiq/interface/model/native_function_definition.py +8 -4
  173. classiq/interface/model/parameter.py +13 -0
  174. classiq/interface/model/port_declaration.py +21 -4
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +22 -8
  176. classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
  177. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
  178. classiq/interface/model/quantum_function_call.py +136 -194
  179. classiq/interface/model/quantum_function_declaration.py +147 -165
  180. classiq/interface/model/quantum_lambda_function.py +23 -6
  181. classiq/interface/model/quantum_statement.py +34 -8
  182. classiq/interface/model/quantum_type.py +41 -11
  183. classiq/interface/model/quantum_variable_declaration.py +1 -1
  184. classiq/interface/model/statement_block.py +2 -0
  185. classiq/interface/model/validation_handle.py +8 -2
  186. classiq/interface/server/global_versions.py +4 -4
  187. classiq/interface/server/routes.py +2 -0
  188. classiq/interface/source_reference.py +59 -0
  189. classiq/qmod/__init__.py +2 -3
  190. classiq/qmod/builtins/classical_execution_primitives.py +1 -1
  191. classiq/qmod/builtins/functions.py +39 -11
  192. classiq/qmod/builtins/operations.py +172 -41
  193. classiq/qmod/classical_function.py +1 -1
  194. classiq/qmod/declaration_inferrer.py +102 -57
  195. classiq/qmod/expression_query.py +1 -1
  196. classiq/qmod/model_state_container.py +2 -0
  197. classiq/qmod/native/pretty_printer.py +71 -53
  198. classiq/qmod/pretty_print/pretty_printer.py +98 -52
  199. classiq/qmod/qfunc.py +11 -5
  200. classiq/qmod/qmod_constant.py +1 -1
  201. classiq/qmod/qmod_parameter.py +27 -4
  202. classiq/qmod/qmod_variable.py +405 -174
  203. classiq/qmod/quantum_callable.py +3 -3
  204. classiq/qmod/quantum_expandable.py +128 -68
  205. classiq/qmod/quantum_function.py +24 -5
  206. classiq/qmod/semantics/annotation.py +13 -15
  207. classiq/qmod/semantics/error_manager.py +36 -10
  208. classiq/qmod/semantics/static_semantics_visitor.py +164 -76
  209. classiq/qmod/semantics/validation/func_call_validation.py +43 -97
  210. classiq/qmod/semantics/validation/handle_validation.py +85 -0
  211. classiq/qmod/semantics/validation/types_validation.py +108 -1
  212. classiq/qmod/symbolic.py +2 -1
  213. classiq/qmod/type_attribute_remover.py +32 -0
  214. classiq/qmod/utilities.py +26 -5
  215. classiq/{interface/ide/show.py → show.py} +1 -1
  216. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/METADATA +3 -3
  217. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/RECORD +219 -207
  218. classiq/qmod/qmod_struct.py +0 -13
  219. /classiq/{_internals → interface}/enum_utils.py +0 -0
  220. {classiq-0.43.3.dist-info → classiq-0.45.0.dist-info}/WHEEL +0 -0
@@ -8,30 +8,41 @@ from typing import ( # type: ignore[attr-defined]
8
8
  Generic,
9
9
  Iterator,
10
10
  Literal,
11
+ Mapping,
11
12
  Optional,
12
13
  Tuple,
13
14
  Type,
14
15
  TypeVar,
15
16
  Union,
16
17
  _GenericAlias,
18
+ cast,
17
19
  get_args,
18
20
  get_origin,
19
- overload,
20
21
  )
21
22
 
22
23
  from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
23
24
 
24
- from classiq.interface.ast_node import SourceReference
25
+ from classiq.interface.exceptions import ClassiqValueError
25
26
  from classiq.interface.generator.expressions.expression import Expression
27
+ from classiq.interface.generator.expressions.qmod_qarray_proxy import (
28
+ ILLEGAL_SLICE_BOUNDS_MSG,
29
+ ILLEGAL_SLICE_MSG,
30
+ ILLEGAL_SLICING_STEP_MSG,
31
+ SLICE_OUT_OF_BOUNDS_MSG,
32
+ SUBSCRIPT_OUT_OF_BOUNDS_MSG,
33
+ )
26
34
  from classiq.interface.generator.functions.port_declaration import (
27
35
  PortDeclarationDirection,
28
36
  )
37
+ from classiq.interface.generator.functions.type_name import TypeName
38
+ from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
29
39
  from classiq.interface.model.handle_binding import (
40
+ FieldHandleBinding,
30
41
  HandleBinding,
31
42
  SlicedHandleBinding,
32
43
  SubscriptHandleBinding,
33
44
  )
34
- from classiq.interface.model.port_declaration import PortDeclaration
45
+ from classiq.interface.model.port_declaration import AnonPortDeclaration
35
46
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
36
47
  AmplitudeLoadingOperation,
37
48
  )
@@ -44,18 +55,25 @@ from classiq.interface.model.quantum_type import (
44
55
  QuantumNumeric,
45
56
  QuantumType,
46
57
  )
58
+ from classiq.interface.source_reference import SourceReference
47
59
 
48
- from classiq.exceptions import ClassiqValueError
49
- from classiq.qmod.qmod_parameter import ArrayBase, CBool, CInt, CParam, CParamScalar
60
+ from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
61
+ from classiq.qmod.qmod_parameter import ArrayBase, CBool, CInt, CParamScalar
50
62
  from classiq.qmod.quantum_callable import QCallable
51
63
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
52
64
  from classiq.qmod.symbolic_type import SymbolicTypes
53
65
  from classiq.qmod.utilities import get_source_ref, version_portable_get_args
54
66
 
55
- ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
56
- SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
57
- UNSUPPORTED_ELEMENT_TYPE = "Only QBit and QNum are supported as element type for QArray"
58
- QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
67
+ QVAR_PROPERTIES_ARE_SYMBOLIC = True
68
+
69
+
70
+ @contextmanager
71
+ def set_symbolic_qvar_properties(symbolic: bool) -> Iterator[None]:
72
+ global QVAR_PROPERTIES_ARE_SYMBOLIC
73
+ previous_symbolic = QVAR_PROPERTIES_ARE_SYMBOLIC
74
+ QVAR_PROPERTIES_ARE_SYMBOLIC = symbolic
75
+ yield
76
+ QVAR_PROPERTIES_ARE_SYMBOLIC = previous_symbolic
59
77
 
60
78
 
61
79
  def _is_input_output_typehint(type_hint: Any) -> bool:
@@ -84,18 +102,30 @@ def _no_current_expandable() -> Iterator[None]:
84
102
 
85
103
 
86
104
  class QVar(Symbolic):
87
- def __init__(self, name: str, depth: int = 2) -> None:
88
- super().__init__(name, True)
89
- self._name = name
90
- source_ref = get_source_ref(sys._getframe(depth))
91
- if QCallable.CURRENT_EXPANDABLE is not None:
105
+ def __init__(
106
+ self,
107
+ origin: Union[str, HandleBinding],
108
+ *,
109
+ expr_str: Optional[str] = None,
110
+ depth: int = 2,
111
+ ) -> None:
112
+ super().__init__(str(origin), True)
113
+ source_ref = (
114
+ get_source_ref(sys._getframe(depth))
115
+ if isinstance(origin, str)
116
+ else origin.source_ref
117
+ )
118
+ self._base_handle: HandleBinding = (
119
+ HandleBinding(name=origin) if isinstance(origin, str) else origin
120
+ )
121
+ if isinstance(origin, str) and QCallable.CURRENT_EXPANDABLE is not None:
92
122
  QCallable.CURRENT_EXPANDABLE.add_local_handle(
93
- self._name, self.get_qmod_type(), source_ref
123
+ origin, self.get_qmod_type(), source_ref
94
124
  )
125
+ self._expr_str = expr_str if expr_str is not None else str(origin)
95
126
 
96
- @abc.abstractmethod
97
127
  def get_handle_binding(self) -> HandleBinding:
98
- raise NotImplementedError()
128
+ return self._base_handle
99
129
 
100
130
  @abc.abstractmethod
101
131
  def get_qmod_type(self) -> QuantumType:
@@ -107,6 +137,9 @@ class QVar(Symbolic):
107
137
  return QVar.from_type_hint(type_hint.__args__[0])
108
138
  type_ = get_origin(type_hint) or type_hint
109
139
  if issubclass(type_, QVar):
140
+ if issubclass(type_, QStruct):
141
+ with _no_current_expandable():
142
+ type_("DUMMY")._add_qmod_qstruct(qmodule=QMODULE)
110
143
  return type_
111
144
  return None
112
145
 
@@ -115,6 +148,16 @@ class QVar(Symbolic):
115
148
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
116
149
  raise NotImplementedError()
117
150
 
151
+ @classmethod
152
+ @abc.abstractmethod
153
+ def to_qvar(
154
+ cls,
155
+ origin: Union[str, HandleBinding],
156
+ type_hint: Any,
157
+ expr_str: Optional[str],
158
+ ) -> Self:
159
+ raise NotImplementedError()
160
+
118
161
  @classmethod
119
162
  def port_direction(cls, type_hint: Any) -> PortDeclarationDirection:
120
163
  if _is_input_output_typehint(type_hint):
@@ -124,7 +167,17 @@ class QVar(Symbolic):
124
167
  return PortDeclarationDirection.Inout
125
168
 
126
169
  def __str__(self) -> str:
127
- return str(self.get_handle_binding())
170
+ return self._expr_str
171
+
172
+ @property
173
+ def size(self) -> Union[CParamScalar, int]:
174
+ if not QVAR_PROPERTIES_ARE_SYMBOLIC:
175
+ return self._evaluate_size()
176
+ return CParamScalar(f"get_field({self}, 'size')")
177
+
178
+ @abc.abstractmethod
179
+ def _evaluate_size(self) -> int:
180
+ raise NotImplementedError
128
181
 
129
182
 
130
183
  _Q = TypeVar("_Q", bound=QVar)
@@ -133,9 +186,15 @@ Input = Annotated[_Q, PortDeclarationDirection.Input]
133
186
 
134
187
 
135
188
  class QScalar(QVar, SymbolicExpr):
136
- def __init__(self, name: str, depth: int = 2) -> None:
137
- QVar.__init__(self, name, depth)
138
- SymbolicExpr.__init__(self, name, True)
189
+ def __init__(
190
+ self,
191
+ origin: Union[str, HandleBinding],
192
+ *,
193
+ _expr_str: Optional[str] = None,
194
+ depth: int = 2,
195
+ ) -> None:
196
+ QVar.__init__(self, origin, expr_str=_expr_str, depth=depth)
197
+ SymbolicExpr.__init__(self, str(origin), True)
139
198
 
140
199
  def _insert_arith_operation(
141
200
  self, expr: SymbolicTypes, inplace: bool, source_ref: SourceReference
@@ -165,9 +224,6 @@ class QScalar(QVar, SymbolicExpr):
165
224
  )
166
225
  )
167
226
 
168
- def get_handle_binding(self) -> HandleBinding:
169
- return HandleBinding(name=self._name)
170
-
171
227
  def __ior__(self, other: Any) -> Self:
172
228
  if not isinstance(other, get_args(SymbolicTypes)):
173
229
  raise TypeError(
@@ -201,36 +257,33 @@ class QBit(QScalar):
201
257
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
202
258
  return QuantumBit()
203
259
 
260
+ @classmethod
261
+ def to_qvar(
262
+ cls,
263
+ origin: Union[str, HandleBinding],
264
+ type_hint: Any,
265
+ expr_str: Optional[str],
266
+ ) -> "QBit":
267
+ return QBit(origin, _expr_str=expr_str)
268
+
204
269
  def get_qmod_type(self) -> QuantumType:
205
270
  return QuantumBit()
206
271
 
272
+ def _evaluate_size(self) -> int:
273
+ return 1
274
+
207
275
 
208
276
  _P = ParamSpec("_P")
209
277
 
210
278
 
211
279
  class QNum(Generic[_P], QScalar):
212
- QMOD_TYPE = QuantumNumeric
213
-
214
- @overload
215
- def __init__(self, name: str):
216
- pass
217
-
218
- @overload
219
280
  def __init__(
220
281
  self,
221
- name: str,
222
- size: Union[int, CInt],
223
- is_signed: Union[bool, CBool],
224
- fraction_digits: Union[int, CInt],
225
- ):
226
- pass
227
-
228
- def __init__(
229
- self,
230
- name: str,
231
- size: Union[int, CInt, None] = None,
232
- is_signed: Union[bool, CBool, None] = None,
233
- fraction_digits: Union[int, CInt, None] = None,
282
+ name: Union[str, HandleBinding],
283
+ size: Union[int, CInt, Expression, None] = None,
284
+ is_signed: Union[bool, CBool, Expression, None] = None,
285
+ fraction_digits: Union[int, CInt, Expression, None] = None,
286
+ _expr_str: Optional[str] = None,
234
287
  ):
235
288
  if (
236
289
  size is None
@@ -241,48 +294,90 @@ class QNum(Generic[_P], QScalar):
241
294
  raise ClassiqValueError(
242
295
  "Assign none or all of size, is_signed, and fraction_digits"
243
296
  )
244
- self._size = None if size is None else Expression(expr=str(size))
245
- self._is_signed = None if is_signed is None else Expression(expr=str(is_signed))
297
+ self._size = (
298
+ size
299
+ if size is None or isinstance(size, Expression)
300
+ else Expression(expr=str(size))
301
+ )
302
+ self._is_signed = (
303
+ is_signed
304
+ if is_signed is None or isinstance(is_signed, Expression)
305
+ else Expression(expr=str(is_signed))
306
+ )
246
307
  self._fraction_digits = (
247
- None if fraction_digits is None else Expression(expr=str(fraction_digits))
308
+ fraction_digits
309
+ if fraction_digits is None or isinstance(fraction_digits, Expression)
310
+ else Expression(expr=str(fraction_digits))
248
311
  )
249
- super().__init__(name, 3)
312
+ super().__init__(name, _expr_str=_expr_str, depth=3)
250
313
 
251
314
  @classmethod
252
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
253
- type_args = get_args(type_hint)
315
+ def _get_attributes(cls, type_hint: Any) -> Tuple[Any, Any, Any]:
316
+ type_args = version_portable_get_args(type_hint)
254
317
  if len(type_args) == 0:
255
- return cls.QMOD_TYPE()
256
- type_args = type_args[0]
318
+ return None, None, None
257
319
  if len(type_args) != 3:
258
320
  raise ClassiqValueError(
259
321
  "QNum receives three type arguments: QNum[size: int | CInt, "
260
322
  "is_signed: bool | CBool, fraction_digits: int | CInt]"
261
323
  )
262
- return cls.QMOD_TYPE(
263
- size=Expression(expr=get_type_hint_expr(type_args[0])),
264
- is_signed=Expression(expr=get_type_hint_expr(type_args[1])),
265
- fraction_digits=Expression(expr=get_type_hint_expr(type_args[2])),
324
+ return type_args[0], type_args[1], type_args[2]
325
+
326
+ @classmethod
327
+ def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
328
+ size, is_signed, fraction_digits = cls._get_attributes(type_hint)
329
+ return QuantumNumeric(
330
+ size=(
331
+ Expression(expr=get_type_hint_expr(size)) if size is not None else None
332
+ ),
333
+ is_signed=(
334
+ Expression(expr=get_type_hint_expr(is_signed))
335
+ if is_signed is not None
336
+ else None
337
+ ),
338
+ fraction_digits=(
339
+ Expression(expr=get_type_hint_expr(fraction_digits))
340
+ if fraction_digits is not None
341
+ else None
342
+ ),
266
343
  )
267
344
 
345
+ @classmethod
346
+ def to_qvar(
347
+ cls,
348
+ origin: Union[str, HandleBinding],
349
+ type_hint: Any,
350
+ expr_str: Optional[str],
351
+ ) -> "QNum":
352
+ return QNum(origin, *cls._get_attributes(type_hint), _expr_str=expr_str)
353
+
268
354
  def get_qmod_type(self) -> QuantumType:
269
- return self.QMOD_TYPE(
355
+ return QuantumNumeric(
270
356
  size=self._size,
271
357
  is_signed=self._is_signed,
272
358
  fraction_digits=self._fraction_digits,
273
359
  )
274
360
 
275
- @property
276
- def size(self) -> CParamScalar:
277
- return CParamScalar(f"get_field({self._name}, 'size')")
361
+ def _evaluate_size(self) -> int:
362
+ if TYPE_CHECKING:
363
+ assert self._size is not None
364
+ return self._size.to_int_value()
278
365
 
279
366
  @property
280
- def fraction_digits(self) -> CParamScalar:
281
- return CParamScalar(f"get_field({self._name}, 'fraction_digits')")
367
+ def fraction_digits(self) -> Union[CParamScalar, int]:
368
+ if not QVAR_PROPERTIES_ARE_SYMBOLIC:
369
+ if TYPE_CHECKING:
370
+ assert self._fraction_digits is not None
371
+ return self._fraction_digits.to_int_value()
372
+ return CParamScalar(f"get_field({self}, 'fraction_digits')")
282
373
 
283
374
  @property
284
- def is_signed(self) -> CParamScalar:
285
- return CParamScalar(f"get_field({self._name}, 'is_signed')")
375
+ def is_signed(self) -> Union[CParamScalar, bool]:
376
+ if not QVAR_PROPERTIES_ARE_SYMBOLIC:
377
+ if TYPE_CHECKING:
378
+ assert self._is_signed is not None
379
+ return self._is_signed.to_bool_value()
380
+ return CParamScalar(f"get_field({self}, 'is_signed')")
286
381
 
287
382
  # Support comma-separated generic args in older Python versions
288
383
  if sys.version_info[0:2] < (3, 10):
@@ -292,84 +387,83 @@ class QNum(Generic[_P], QScalar):
292
387
 
293
388
 
294
389
  class QArray(ArrayBase[_P], QVar):
390
+ # TODO [CAD-18620]: improve type hints
295
391
  def __init__(
296
392
  self,
297
- name: str,
298
- element_type: _GenericAlias = QBit,
299
- length: Optional[Union[int, CInt]] = None,
300
- # TODO [CAD-18620]: improve type hints
301
- slice_: Optional[Tuple[int, int]] = None,
302
- index_: Optional[Union[int, CInt]] = None,
393
+ name: Union[str, HandleBinding],
394
+ element_type: Union[_GenericAlias, QuantumType] = QBit,
395
+ length: Optional[Union[int, SymbolicExpr, Expression]] = None,
396
+ _expr_str: Optional[str] = None,
303
397
  ) -> None:
304
- if not issubclass(get_origin(element_type) or element_type, (QBit, QNum)):
305
- raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
306
398
  self._element_type = element_type
307
- self._length = length
308
- self._slice = slice_
309
- self._index = index_
310
- super().__init__(name)
311
-
312
- def get_handle_binding(self) -> HandleBinding:
313
- if self._index is not None:
314
- return SubscriptHandleBinding(
315
- name=self._name,
316
- index=Expression(expr=str(self._index)),
317
- )
318
-
319
- if self._slice is not None:
320
- return SlicedHandleBinding(
321
- name=self._name,
322
- start=Expression(expr=str(self._slice[0])),
323
- end=Expression(expr=str(self._slice[1])),
324
- )
325
-
326
- return HandleBinding(name=self._name)
327
-
328
- def __getitem__(self, key: Union[slice, int, CInt]) -> Any:
329
- if self._index is not None:
330
- raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
331
-
332
- # TODO [CAD-18620]: improve type hints
333
- new_index: Optional[Any] = None
399
+ self._length = (
400
+ length
401
+ if length is None or isinstance(length, Expression)
402
+ else Expression(expr=str(length))
403
+ )
404
+ super().__init__(name, expr_str=_expr_str)
334
405
 
335
- if isinstance(key, slice):
336
- if key.step is not None:
337
- raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
338
- new_slice = self._get_new_slice(key.start, key.stop)
406
+ def __getitem__(self, key: Union[slice, int, SymbolicExpr]) -> Any:
407
+ return (
408
+ self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
409
+ )
339
410
 
340
- else:
341
- if isinstance(key, CParam) and not isinstance(key, CParamScalar):
342
- raise ClassiqValueError("Non-classical parameter for slicing")
343
- new_slice = self._get_new_slice(key, key + 1)
344
- new_index = new_slice[0]
411
+ def _get_subscript(self, index: Union[slice, int, SymbolicExpr]) -> Any:
412
+ if isinstance(index, SymbolicExpr) and index.is_quantum:
413
+ raise ClassiqValueError("Non-classical parameter for slicing")
414
+ if (
415
+ isinstance(index, int)
416
+ and self._length is not None
417
+ and self._length.is_evaluated()
418
+ ):
419
+ length = self._length.to_int_value()
420
+ if index < 0 or index >= length:
421
+ raise ClassiqValueError(SUBSCRIPT_OUT_OF_BOUNDS_MSG)
422
+
423
+ return _create_qvar_for_qtype(
424
+ self.get_qmod_type().element_type,
425
+ SubscriptHandleBinding(
426
+ base_handle=self._base_handle,
427
+ index=Expression(expr=str(index)),
428
+ ),
429
+ expr_str=f"{self}[{index}]",
430
+ )
345
431
 
432
+ def _get_slice(self, slice_: slice) -> Any:
433
+ if slice_.step is not None:
434
+ raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
435
+ if not isinstance(slice_.start, (int, SymbolicExpr)) or not isinstance(
436
+ slice_.stop, (int, SymbolicExpr)
437
+ ):
438
+ raise ClassiqValueError(ILLEGAL_SLICE_MSG)
346
439
  if (
347
- self._slice is not None
348
- and not isinstance(new_slice[1], Symbolic)
349
- and not isinstance(self._slice[1], Symbolic)
350
- and new_slice[1] > self._slice[1]
351
- ) or (
352
- self._length is not None
353
- and not isinstance(new_slice[1], Symbolic)
354
- and not isinstance(self._length, Symbolic)
355
- and new_slice[1] > self._length
440
+ isinstance(slice_.start, int)
441
+ and isinstance(slice_.stop, int)
442
+ and slice_.start >= slice_.stop
356
443
  ):
357
- raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
358
- # prevent addition to local handles, since this is used for slicing existing local handles
359
- with _no_current_expandable():
360
- if new_index is None:
361
- array_class = QArray
362
- else:
363
- array_class = QArraySubscript
364
- return array_class(
365
- self._name, length=self._length, slice_=new_slice, index_=new_index
444
+ raise ClassiqValueError(
445
+ ILLEGAL_SLICE_BOUNDS_MSG.format(slice_.start, slice_.stop)
366
446
  )
367
-
368
- # TODO [CAD-18620]: improve type hints
369
- def _get_new_slice(self, start: Any, end: Any) -> Tuple[Any, Any]:
370
- if self._slice is not None:
371
- return (self._slice[0] + start, self._slice[0] + end)
372
- return (start, end)
447
+ if self._length is not None and self._length.is_evaluated():
448
+ length = self._length.to_int_value()
449
+ if (
450
+ isinstance(slice_.start, int)
451
+ and slice_.start < 0
452
+ or isinstance(slice_.stop, int)
453
+ and slice_.stop > length
454
+ ):
455
+ raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
456
+
457
+ return QArray(
458
+ name=SlicedHandleBinding(
459
+ base_handle=self._base_handle,
460
+ start=Expression(expr=str(slice_.start)),
461
+ end=Expression(expr=str(slice_.stop)),
462
+ ),
463
+ element_type=self._element_type,
464
+ length=slice_.stop - slice_.start,
465
+ _expr_str=f"{self}[{slice_.start}:{slice_.stop}]",
466
+ )
373
467
 
374
468
  def __len__(self) -> int:
375
469
  raise ClassiqValueError(
@@ -384,69 +478,206 @@ class QArray(ArrayBase[_P], QVar):
384
478
  else:
385
479
 
386
480
  @property
387
- def len(self) -> CParamScalar:
481
+ def len(self) -> Union[CParamScalar, int]:
482
+ if not QVAR_PROPERTIES_ARE_SYMBOLIC:
483
+ return self._length.to_int_value()
388
484
  if self._length is not None:
389
485
  return CParamScalar(f"{self._length}")
390
- return CParamScalar(f"get_field({self._name}, 'len')")
486
+ return CParamScalar(f"get_field({self}, 'len')")
487
+
488
+ def _evaluate_size(self) -> int:
489
+ if TYPE_CHECKING:
490
+ assert self._length is not None
491
+ return self._element_type.size_in_bits * self._length.to_int_value()
391
492
 
392
493
  @classmethod
393
- def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
494
+ def _get_attributes(cls, type_hint: Any) -> Tuple[Type[QVar], Any]:
394
495
  type_args = version_portable_get_args(type_hint)
395
- if len(type_args) == 1 and isinstance(type_args[0], (str, int)):
396
- type_args = (QBit, type_args[0])
496
+ if len(type_args) == 0:
497
+ return QBit, None
498
+ if len(type_args) == 1:
499
+ if isinstance(type_args[0], (str, int)):
500
+ return QBit, type_args[0]
501
+ return type_args[0], None
502
+ if len(type_args) != 2:
503
+ raise ClassiqValueError(
504
+ "QArray receives two type arguments: QArray[element_type: QVar, "
505
+ "length: int | CInt]"
506
+ )
507
+ return cast(Tuple[Type[QVar], Any], type_args)
397
508
 
398
- api_element_type = QBit if len(type_args) == 0 else type_args[0]
509
+ @classmethod
510
+ def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
511
+ api_element_type, length = cls._get_attributes(type_hint)
399
512
  api_element_class = get_origin(api_element_type) or api_element_type
400
513
  element_type = api_element_class.to_qmod_quantum_type(api_element_type)
401
514
 
402
515
  length_expr: Optional[Expression] = None
403
- if len(type_args) == 2:
404
- length_expr = Expression(expr=get_type_hint_expr(type_args[1]))
516
+ if length is not None:
517
+ length_expr = Expression(expr=get_type_hint_expr(length))
405
518
 
406
519
  return QuantumBitvector(element_type=element_type, length=length_expr)
407
520
 
408
- def get_qmod_type(self) -> QuantumType:
409
- element_class = get_origin(self._element_type) or self._element_type
410
- length = None
411
- if self._length is not None:
412
- length = Expression(expr=str(self._length))
521
+ @classmethod
522
+ def to_qvar(
523
+ cls,
524
+ origin: Union[str, HandleBinding],
525
+ type_hint: Any,
526
+ expr_str: Optional[str],
527
+ ) -> "QArray":
528
+ return QArray(origin, *cls._get_attributes(type_hint), _expr_str=expr_str)
529
+
530
+ def get_qmod_type(self) -> QuantumBitvector:
531
+ if isinstance(self._element_type, QuantumType):
532
+ element_type = self._element_type
533
+ else:
534
+ element_class = get_origin(self._element_type) or self._element_type
535
+ element_type = element_class.to_qmod_quantum_type(self._element_type)
413
536
  return QuantumBitvector(
414
- element_type=element_class.to_qmod_quantum_type(self._element_type),
415
- length=length,
537
+ element_type=element_type,
538
+ length=self._length,
416
539
  )
417
540
 
418
541
 
419
- class QArraySubscript(QArray, QScalar):
420
- @property
421
- def size(self) -> CParamScalar:
422
- return CParamScalar(f"get_field({self.get_handle_binding()}, 'size')")
542
+ class QStruct(QVar):
543
+ _struct_name: str
544
+ _fields: Mapping[str, QVar]
423
545
 
424
- @property
425
- def fraction_digits(self) -> CParamScalar:
426
- return CParamScalar(
427
- f"get_field({self.get_handle_binding()}, 'fraction_digits')"
546
+ def __init__(
547
+ self,
548
+ name: Union[str, HandleBinding],
549
+ _struct_name: Optional[str] = None,
550
+ _fields: Optional[Mapping[str, QVar]] = None,
551
+ _expr_str: Optional[str] = None,
552
+ ) -> None:
553
+ if _struct_name is None or _fields is None:
554
+ with _no_current_expandable():
555
+ temp_var = QStruct.to_qvar(name, type(self), _expr_str)
556
+ _struct_name = temp_var._struct_name
557
+ _fields = temp_var._fields
558
+ self._struct_name = _struct_name
559
+ self._fields = _fields
560
+ for field_name, var in _fields.items():
561
+ setattr(self, field_name, var)
562
+ super().__init__(name)
563
+ self._add_qmod_qstruct(qmodule=QMODULE)
564
+
565
+ def get_qmod_type(self) -> QuantumType:
566
+ return TypeName(name=self._struct_name)
567
+
568
+ @classmethod
569
+ def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
570
+ with _no_current_expandable():
571
+ type_hint("DUMMY")
572
+ return TypeName(name=type_hint.__name__)
573
+
574
+ @classmethod
575
+ def to_qvar(
576
+ cls,
577
+ origin: Union[str, HandleBinding],
578
+ type_hint: Any,
579
+ expr_str: Optional[str],
580
+ ) -> "QStruct":
581
+ field_types = {
582
+ field_name: (QVar.from_type_hint(field_type), field_type)
583
+ for field_name, field_type in type_hint.__annotations__.items()
584
+ }
585
+ illegal_fields = [
586
+ (field_name, field_type)
587
+ for field_name, (field_class, field_type) in field_types.items()
588
+ if field_class is None
589
+ ]
590
+ if len(illegal_fields) > 0:
591
+ raise ClassiqValueError(
592
+ f"Field {illegal_fields[0][0]!r} of quantum struct "
593
+ f"{type_hint.__name__} has a non-quantum type "
594
+ f"{illegal_fields[0][1].__name__}."
595
+ )
596
+ base_handle = HandleBinding(name=origin) if isinstance(origin, str) else origin
597
+ with _no_current_expandable():
598
+ field_vars = {
599
+ field_name: cast(Type[QVar], field_class).to_qvar(
600
+ FieldHandleBinding(base_handle=base_handle, field=field_name),
601
+ field_type,
602
+ f"get_field({expr_str if expr_str is not None else str(origin)}, '{field_name}')",
603
+ )
604
+ for field_name, (field_class, field_type) in field_types.items()
605
+ }
606
+ return QStruct(
607
+ name=origin,
608
+ _struct_name=type_hint.__name__,
609
+ _fields=field_vars,
610
+ _expr_str=expr_str,
428
611
  )
429
612
 
430
- @property
431
- def is_signed(self) -> CParamScalar:
432
- return CParamScalar(f"get_field({self.get_handle_binding()}, 'is_signed')")
613
+ def _add_qmod_qstruct(self, *, qmodule: ModelStateContainer) -> None:
614
+ if self._struct_name in qmodule.qstruct_decls:
615
+ return
616
+
617
+ qmodule.qstruct_decls[self._struct_name] = QStructDeclaration(
618
+ name=self._struct_name,
619
+ fields={name: qvar.get_qmod_type() for name, qvar in self._fields.items()},
620
+ )
621
+
622
+ def _evaluate_size(self) -> int:
623
+ return sum(var._evaluate_size() for var in self._fields.values())
624
+
433
625
 
626
+ def create_qvar_for_port_decl(port: AnonPortDeclaration, name: str) -> QVar:
627
+ return _create_qvar_for_qtype(port.quantum_type, HandleBinding(name=name))
434
628
 
435
- def create_qvar_for_port_decl(port: PortDeclaration, name: str) -> QVar:
629
+
630
+ def _create_qvar_for_qtype(
631
+ qtype: QuantumType, origin: HandleBinding, expr_str: Optional[str] = None
632
+ ) -> QVar:
436
633
  # prevent addition to local handles, since this is used for ports
437
634
  with _no_current_expandable():
438
- if _is_single_qbit_vector(port):
439
- return QBit(name)
440
- elif isinstance(port.quantum_type, QuantumNumeric):
441
- return QNum(name)
442
- return QArray(name)
443
-
444
-
445
- def _is_single_qbit_vector(port: PortDeclaration) -> bool:
446
- return (
447
- isinstance(port.quantum_type, QuantumBit)
448
- or isinstance(port.quantum_type, QuantumBitvector)
449
- and port.size is not None
450
- and port.size.is_evaluated()
451
- and port.size.to_int_value() == 1
452
- )
635
+ if isinstance(qtype, QuantumBit):
636
+ return QBit(origin, _expr_str=expr_str)
637
+ elif isinstance(qtype, QuantumNumeric):
638
+ return QNum(
639
+ origin,
640
+ qtype.size,
641
+ qtype.is_signed,
642
+ qtype.fraction_digits,
643
+ _expr_str=expr_str,
644
+ )
645
+ elif isinstance(qtype, TypeName):
646
+ struct_decl = QMODULE.qstruct_decls[qtype.name]
647
+ return QStruct(
648
+ origin,
649
+ struct_decl.name,
650
+ {
651
+ field_name: _create_qvar_for_qtype(
652
+ field_type,
653
+ FieldHandleBinding(base_handle=origin, field=field_name),
654
+ f"get_field({expr_str if expr_str is not None else str(origin)}, '{field_name}')",
655
+ )
656
+ for field_name, field_type in struct_decl.fields.items()
657
+ },
658
+ _expr_str=expr_str,
659
+ )
660
+ if TYPE_CHECKING:
661
+ assert isinstance(qtype, QuantumBitvector)
662
+ return QArray(origin, qtype.element_type, qtype.length, _expr_str=expr_str)
663
+
664
+
665
+ def get_qvar(qtype: QuantumType, origin: HandleBinding) -> "QVar":
666
+ if isinstance(qtype, QuantumBit):
667
+ return QBit(origin)
668
+ elif isinstance(qtype, QuantumBitvector):
669
+ return QArray(origin, qtype.element_type, qtype.length)
670
+ elif isinstance(qtype, QuantumNumeric):
671
+ return QNum(origin, qtype.size, qtype.is_signed, qtype.fraction_digits)
672
+ elif isinstance(qtype, TypeName):
673
+ return QStruct(
674
+ origin,
675
+ qtype.name,
676
+ {
677
+ field_name: get_qvar(
678
+ field_type, FieldHandleBinding(base_handle=origin, field=field_name)
679
+ )
680
+ for field_name, field_type in qtype.fields.items()
681
+ },
682
+ )
683
+ raise NotImplementedError