classiq 0.37.0__py3-none-any.whl → 0.38.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 (228) hide show
  1. classiq/__init__.py +2 -2
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
  3. classiq/_analyzer_extras/interactive_hardware.py +3 -3
  4. classiq/_internals/api_wrapper.py +24 -16
  5. classiq/_internals/async_utils.py +1 -74
  6. classiq/_internals/authentication/device.py +9 -4
  7. classiq/_internals/authentication/password_manager.py +25 -10
  8. classiq/_internals/authentication/token_manager.py +2 -2
  9. classiq/_internals/client.py +13 -5
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +26 -28
  12. classiq/analyzer/analyzer_utilities.py +5 -5
  13. classiq/analyzer/rb.py +4 -5
  14. classiq/analyzer/show_interactive_hack.py +6 -6
  15. classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
  16. classiq/applications/combinatorial_optimization/__init__.py +5 -0
  17. classiq/applications/qnn/circuit_utils.py +2 -2
  18. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  19. classiq/applications/qnn/types.py +2 -2
  20. classiq/applications/qsvm/qsvm.py +4 -7
  21. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  22. classiq/applications_model_constructors/__init__.py +9 -1
  23. classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
  24. classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
  25. classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
  26. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
  27. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  28. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
  29. classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
  30. classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
  31. classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
  32. classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
  33. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
  34. classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
  35. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  36. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  37. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
  38. classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
  39. classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
  40. classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
  41. classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
  42. classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
  43. classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
  44. classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
  45. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
  46. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
  47. classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  48. classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
  49. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
  50. classiq/applications_model_constructors/finance_model_constructor.py +4 -17
  51. classiq/applications_model_constructors/grover_model_constructor.py +20 -91
  52. classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
  53. classiq/builtin_functions/standard_gates.py +1 -1
  54. classiq/exceptions.py +43 -1
  55. classiq/executor.py +10 -9
  56. classiq/interface/_version.py +1 -1
  57. classiq/interface/analyzer/analysis_params.py +6 -3
  58. classiq/interface/analyzer/result.py +12 -4
  59. classiq/interface/applications/qsvm.py +13 -1
  60. classiq/interface/backend/backend_preferences.py +4 -2
  61. classiq/interface/backend/pydantic_backend.py +3 -1
  62. classiq/interface/backend/quantum_backend_providers.py +1 -0
  63. classiq/interface/chemistry/fermionic_operator.py +15 -13
  64. classiq/interface/chemistry/ground_state_problem.py +18 -3
  65. classiq/interface/chemistry/molecule.py +8 -6
  66. classiq/interface/chemistry/operator.py +20 -14
  67. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  68. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  69. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  70. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  71. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
  73. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  74. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  75. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  76. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  77. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  78. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  79. classiq/interface/executor/aws_execution_cost.py +4 -3
  80. classiq/interface/executor/estimation.py +2 -2
  81. classiq/interface/executor/execution_preferences.py +5 -34
  82. classiq/interface/executor/execution_request.py +19 -17
  83. classiq/interface/executor/optimizer_preferences.py +22 -13
  84. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  85. classiq/interface/executor/quantum_instruction_set.py +2 -1
  86. classiq/interface/executor/register_initialization.py +1 -3
  87. classiq/interface/executor/result.py +41 -10
  88. classiq/interface/executor/vqe_result.py +1 -1
  89. classiq/interface/finance/function_input.py +17 -4
  90. classiq/interface/finance/gaussian_model_input.py +3 -1
  91. classiq/interface/finance/log_normal_model_input.py +3 -1
  92. classiq/interface/finance/model_input.py +2 -0
  93. classiq/interface/generator/amplitude_loading.py +6 -3
  94. classiq/interface/generator/application_apis/__init__.py +1 -0
  95. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  96. classiq/interface/generator/arith/argument_utils.py +14 -4
  97. classiq/interface/generator/arith/arithmetic.py +3 -1
  98. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  99. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  100. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  101. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  102. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  103. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  104. classiq/interface/generator/arith/binary_ops.py +202 -54
  105. classiq/interface/generator/arith/extremum_operations.py +5 -3
  106. classiq/interface/generator/arith/logical_ops.py +4 -2
  107. classiq/interface/generator/arith/machine_precision.py +3 -0
  108. classiq/interface/generator/arith/number_utils.py +34 -44
  109. classiq/interface/generator/arith/register_user_input.py +21 -1
  110. classiq/interface/generator/arith/unary_ops.py +16 -25
  111. classiq/interface/generator/chemistry_function_params.py +4 -4
  112. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  113. classiq/interface/generator/compiler_keywords.py +4 -0
  114. classiq/interface/generator/complex_type.py +3 -10
  115. classiq/interface/generator/control_state.py +5 -3
  116. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  117. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  118. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  119. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  120. classiq/interface/generator/expressions/expression.py +1 -1
  121. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  122. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  123. classiq/interface/generator/finance.py +1 -1
  124. classiq/interface/generator/function_params.py +7 -6
  125. classiq/interface/generator/functions/__init__.py +1 -1
  126. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
  127. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
  128. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  129. classiq/interface/generator/functions/function_implementation.py +8 -4
  130. classiq/interface/generator/functions/native_function_definition.py +4 -2
  131. classiq/interface/generator/functions/register.py +4 -2
  132. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  133. classiq/interface/generator/generated_circuit_data.py +2 -2
  134. classiq/interface/generator/grover_operator.py +5 -3
  135. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  136. classiq/interface/generator/hardware/hardware_data.py +6 -4
  137. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  138. classiq/interface/generator/hartree_fock.py +3 -1
  139. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  140. classiq/interface/generator/mcu.py +5 -3
  141. classiq/interface/generator/mcx.py +7 -5
  142. classiq/interface/generator/model/constraints.py +2 -1
  143. classiq/interface/generator/model/model.py +11 -19
  144. classiq/interface/generator/model/preferences/preferences.py +4 -3
  145. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  146. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  147. classiq/interface/generator/qpe.py +6 -4
  148. classiq/interface/generator/qsvm.py +5 -8
  149. classiq/interface/generator/quantum_function_call.py +21 -16
  150. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  151. classiq/interface/generator/range_types.py +3 -1
  152. classiq/interface/generator/slice_parsing_utils.py +8 -3
  153. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  154. classiq/interface/generator/state_preparation/metrics.py +2 -1
  155. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  156. classiq/interface/generator/state_propagator.py +16 -5
  157. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  158. classiq/interface/generator/types/struct_declaration.py +8 -3
  159. classiq/interface/generator/ucc.py +6 -4
  160. classiq/interface/generator/unitary_gate.py +7 -3
  161. classiq/interface/generator/validations/flow_graph.py +6 -4
  162. classiq/interface/generator/validations/validator_functions.py +6 -4
  163. classiq/interface/hardware.py +2 -2
  164. classiq/interface/helpers/custom_encoders.py +3 -0
  165. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  166. classiq/interface/helpers/validation_helpers.py +1 -1
  167. classiq/interface/helpers/versioned_model.py +4 -1
  168. classiq/interface/ide/show.py +2 -2
  169. classiq/interface/jobs.py +72 -3
  170. classiq/interface/model/bind_operation.py +18 -11
  171. classiq/interface/model/call_synthesis_data.py +68 -0
  172. classiq/interface/model/inplace_binary_operation.py +2 -2
  173. classiq/interface/model/model.py +27 -21
  174. classiq/interface/model/native_function_definition.py +3 -5
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  176. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  177. classiq/interface/model/quantum_function_call.py +25 -139
  178. classiq/interface/model/quantum_function_declaration.py +8 -0
  179. classiq/interface/model/quantum_if_operation.py +2 -3
  180. classiq/interface/model/quantum_lambda_function.py +64 -0
  181. classiq/interface/model/quantum_type.py +57 -56
  182. classiq/interface/model/quantum_variable_declaration.py +1 -1
  183. classiq/interface/model/statement_block.py +32 -0
  184. classiq/interface/model/validations/handles_validator.py +14 -12
  185. classiq/interface/model/within_apply_operation.py +11 -0
  186. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  187. classiq/interface/server/routes.py +5 -0
  188. classiq/model/function_handler.py +5 -9
  189. classiq/model/model.py +2 -19
  190. classiq/qmod/__init__.py +13 -6
  191. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  192. classiq/qmod/builtins/classical_functions.py +24 -14
  193. classiq/qmod/builtins/functions.py +162 -145
  194. classiq/qmod/builtins/operations.py +24 -35
  195. classiq/qmod/builtins/structs.py +15 -15
  196. classiq/qmod/cfunc.py +42 -0
  197. classiq/qmod/classical_function.py +6 -14
  198. classiq/qmod/declaration_inferrer.py +12 -21
  199. classiq/qmod/expression_query.py +23 -0
  200. classiq/qmod/model_state_container.py +2 -0
  201. classiq/qmod/native/__init__.py +0 -0
  202. classiq/qmod/native/expression_to_qmod.py +189 -0
  203. classiq/qmod/native/pretty_printer.py +311 -0
  204. classiq/qmod/qfunc.py +27 -0
  205. classiq/qmod/qmod_constant.py +76 -0
  206. classiq/qmod/qmod_parameter.py +34 -12
  207. classiq/qmod/qmod_struct.py +3 -3
  208. classiq/qmod/qmod_variable.py +102 -18
  209. classiq/qmod/quantum_expandable.py +16 -16
  210. classiq/qmod/quantum_function.py +37 -8
  211. classiq/qmod/symbolic.py +47 -4
  212. classiq/qmod/symbolic_expr.py +9 -0
  213. classiq/qmod/utilities.py +13 -0
  214. classiq/qmod/write_qmod.py +39 -0
  215. classiq/quantum_functions/__init__.py +2 -2
  216. classiq/quantum_functions/annotation_parser.py +9 -11
  217. classiq/quantum_functions/function_parser.py +1 -1
  218. classiq/quantum_functions/quantum_function.py +3 -3
  219. classiq/quantum_register.py +17 -9
  220. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
  221. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
  222. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
  223. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  224. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  225. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  226. classiq/interface/model/numeric_reinterpretation.py +0 -25
  227. classiq/interface/model/operator_synthesis_data.py +0 -48
  228. classiq/model/function_handler.pyi +0 -152
@@ -1,5 +1,7 @@
1
1
  import abc
2
+ import sys
2
3
  from contextlib import contextmanager
4
+ from typing import _GenericAlias # type: ignore[attr-defined]
3
5
  from typing import (
4
6
  TYPE_CHECKING,
5
7
  Any,
@@ -14,6 +16,7 @@ from typing import (
14
16
  Union,
15
17
  get_args,
16
18
  get_origin,
19
+ overload,
17
20
  )
18
21
 
19
22
  from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
@@ -40,11 +43,13 @@ from classiq.interface.model.quantum_type import (
40
43
  from classiq.exceptions import ClassiqValueError
41
44
  from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
42
45
  from classiq.qmod.quantum_callable import QCallable
43
- from classiq.qmod.symbolic_expr import SymbolicExpr
46
+ from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
44
47
  from classiq.qmod.symbolic_type import SymbolicTypes
48
+ from classiq.qmod.utilities import version_portable_get_args
45
49
 
46
50
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
47
51
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
52
+ UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
48
53
 
49
54
 
50
55
  def _is_input_output_typehint(type_hint: Any) -> bool:
@@ -183,22 +188,72 @@ class QBit(QScalar):
183
188
  return QuantumBit()
184
189
 
185
190
 
186
- _T = TypeVar("_T")
191
+ _P = ParamSpec("_P")
187
192
 
188
193
 
189
- class QNum(Generic[_T], QScalar):
194
+ class QNum(Generic[_P], QScalar):
190
195
  QMOD_TYPE = QuantumNumeric
191
196
 
197
+ @overload
198
+ def __init__(self, name: str):
199
+ pass
200
+
201
+ @overload
202
+ def __init__(
203
+ self,
204
+ name: str,
205
+ size: Union[int, QParam[int]],
206
+ is_signed: Union[bool, QParam[bool]],
207
+ fraction_digits: Union[int, QParam[int]],
208
+ ):
209
+ pass
210
+
211
+ def __init__(
212
+ self,
213
+ name: str,
214
+ size: Union[int, QParam[int], None] = None,
215
+ is_signed: Union[bool, QParam[bool], None] = None,
216
+ fraction_digits: Union[int, QParam[int], None] = None,
217
+ ):
218
+ if (
219
+ size is None
220
+ and (is_signed is not None or fraction_digits is not None)
221
+ or size is not None
222
+ and (is_signed is None or fraction_digits is None)
223
+ ):
224
+ raise ClassiqValueError(
225
+ "Assign none or all of size, is_signed, and fraction_digits"
226
+ )
227
+ self._size = None if size is None else Expression(expr=str(size))
228
+ self._is_signed = None if is_signed is None else Expression(expr=str(is_signed))
229
+ self._fraction_digits = (
230
+ None if fraction_digits is None else Expression(expr=str(fraction_digits))
231
+ )
232
+ super().__init__(name)
233
+
192
234
  @classmethod
193
235
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
194
- size_expr: Optional[Expression] = None
195
- if get_args(type_hint):
196
- size_expr = Expression(expr=get_type_hint_expr(get_args(type_hint)[0]))
197
-
198
- return cls.QMOD_TYPE(size=size_expr)
236
+ type_args = get_args(type_hint)
237
+ if len(type_args) == 0:
238
+ return cls.QMOD_TYPE()
239
+ type_args = type_args[0]
240
+ if len(type_args) != 3:
241
+ raise ClassiqValueError(
242
+ "QNum receives three type arguments: QNum[size: int | QParam[int], "
243
+ "is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
244
+ )
245
+ return cls.QMOD_TYPE(
246
+ size=Expression(expr=get_type_hint_expr(type_args[0])),
247
+ is_signed=Expression(expr=get_type_hint_expr(type_args[1])),
248
+ fraction_digits=Expression(expr=get_type_hint_expr(type_args[2])),
249
+ )
199
250
 
200
251
  def get_qmod_type(self) -> QuantumType:
201
- return self.QMOD_TYPE()
252
+ return self.QMOD_TYPE(
253
+ size=self._size,
254
+ is_signed=self._is_signed,
255
+ fraction_digits=self._fraction_digits,
256
+ )
202
257
 
203
258
  @property
204
259
  def size(self) -> QParamScalar:
@@ -212,14 +267,27 @@ class QNum(Generic[_T], QScalar):
212
267
  def is_signed(self) -> QParamScalar:
213
268
  return QParamScalar(f"is_signed({self._name})")
214
269
 
270
+ # Support comma-separated generic args in older Python versions
271
+ if sys.version_info[0:2] < (3, 10):
215
272
 
216
- _P = ParamSpec("_P")
273
+ def __class_getitem__(cls, args) -> _GenericAlias:
274
+ return _GenericAlias(cls, args)
217
275
 
218
276
 
219
277
  class QArray(ArrayBase[_P], QVar):
220
- def __init__(self, name: str, slice_: Optional[Tuple[int, int]] = None) -> None:
221
- super().__init__(name)
278
+ def __init__(
279
+ self,
280
+ name: str,
281
+ element_type: _GenericAlias = QBit,
282
+ length: Optional[Union[int, QParam[int]]] = None,
283
+ slice_: Optional[Tuple[int, int]] = None,
284
+ ) -> None:
285
+ if element_type is not QBit:
286
+ raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
287
+ self._element_type = element_type
288
+ self._length = length
222
289
  self._slice = slice_
290
+ super().__init__(name)
223
291
 
224
292
  def get_handle_binding(self) -> HandleBinding:
225
293
  if self._slice is None:
@@ -234,20 +302,30 @@ class QArray(ArrayBase[_P], QVar):
234
302
  offset = self._slice[0] if self._slice is not None else 0
235
303
  if isinstance(key, slice):
236
304
  if key.step is not None:
237
- raise NotImplementedError(ILLEGAL_SLICING_STEP_MSG)
305
+ raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
238
306
  new_slice = (offset + key.start, offset + key.stop)
239
307
  else:
240
308
  if isinstance(key, QParam) and not isinstance(key, QParamScalar):
241
309
  raise ClassiqValueError("Non-classical parameter for slicing")
242
310
  new_slice = (offset + key, offset + key + 1)
243
- if self._slice is not None and new_slice[1] > self._slice[1]:
311
+ if (
312
+ self._slice is not None
313
+ and not isinstance(new_slice[1], Symbolic)
314
+ and not isinstance(self._slice[1], Symbolic)
315
+ and new_slice[1] > self._slice[1]
316
+ ) or (
317
+ self._length is not None
318
+ and not isinstance(new_slice[1], Symbolic)
319
+ and not isinstance(self._length, Symbolic)
320
+ and new_slice[1] > self._length
321
+ ):
244
322
  raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
245
323
  # prevent addition to local handles, since this is used for slicing existing local handles
246
324
  with _no_current_expandable():
247
- return QArray(self._name, slice_=new_slice)
325
+ return QArray(self._name, length=self._length, slice_=new_slice)
248
326
 
249
327
  def __len__(self) -> int:
250
- raise ValueError(
328
+ raise ClassiqValueError(
251
329
  "len(<var>) is not supported for quantum variables - use <var>.len() instead"
252
330
  )
253
331
 
@@ -258,16 +336,22 @@ class QArray(ArrayBase[_P], QVar):
258
336
  else:
259
337
 
260
338
  def len(self) -> QParamScalar:
339
+ if self._length is not None:
340
+ return QParamScalar(f"{self._length}")
261
341
  return QParamScalar(f"len({self._name})")
262
342
 
263
343
  @classmethod
264
344
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
265
345
  length_expr: Optional[Expression] = None
266
- if len(get_args(type_hint)) == 2:
267
- length_expr = Expression(expr=get_type_hint_expr(get_args(type_hint)[1]))
346
+ if len(version_portable_get_args(type_hint)) == 2:
347
+ length_expr = Expression(
348
+ expr=get_type_hint_expr(version_portable_get_args(type_hint)[1])
349
+ )
268
350
  return QuantumBitvector(length=length_expr)
269
351
 
270
352
  def get_qmod_type(self) -> QuantumType:
353
+ if self._length is not None:
354
+ return QuantumBitvector(length=Expression(expr=str(self._length)))
271
355
  return QuantumBitvector()
272
356
 
273
357
 
@@ -1,3 +1,4 @@
1
+ import inspect
1
2
  from abc import ABC
2
3
  from types import TracebackType
3
4
  from typing import (
@@ -25,14 +26,16 @@ from classiq.interface.model.quantum_function_call import (
25
26
  ArgValue,
26
27
  OperandIdentifier,
27
28
  QuantumFunctionCall,
28
- QuantumLambdaFunction,
29
- QuantumOperand,
30
29
  )
31
30
  from classiq.interface.model.quantum_function_declaration import (
32
31
  PositionalArg,
33
32
  QuantumFunctionDeclaration,
34
33
  QuantumOperandDeclaration,
35
34
  )
35
+ from classiq.interface.model.quantum_lambda_function import (
36
+ QuantumLambdaFunction,
37
+ QuantumOperand,
38
+ )
36
39
  from classiq.interface.model.quantum_statement import QuantumStatement
37
40
  from classiq.interface.model.quantum_type import QuantumType
38
41
  from classiq.interface.model.variable_declaration_statement import (
@@ -41,6 +44,7 @@ from classiq.interface.model.variable_declaration_statement import (
41
44
 
42
45
  from classiq.exceptions import ClassiqValueError
43
46
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
47
+ from classiq.qmod.qmod_constant import QConstant
44
48
  from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
45
49
  from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
46
50
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
@@ -96,9 +100,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
96
100
  for arg in self.func_decl.get_positional_arg_decls():
97
101
  if isinstance(arg, ClassicalParameterDeclaration):
98
102
  rename_dict = self.infer_rename_params()
99
- actual_name = (
100
- rename_dict[arg.name] if arg.name in rename_dict else arg.name
101
- )
103
+ actual_name = rename_dict.get(arg.name, arg.name)
102
104
  result.append(
103
105
  create_param(actual_name, arg.classical_type, self._qmodule)
104
106
  )
@@ -126,17 +128,12 @@ class QLambdaFunction(QExpandable):
126
128
  return self._decl
127
129
 
128
130
  def infer_rename_params(self) -> Dict[str, str]:
129
- if not self._py_callable.__annotations__:
130
- return {}
131
+ py_params = inspect.getfullargspec(self._py_callable)
132
+ decl_params = self.func_decl.param_decls.keys()
131
133
  return {
132
- decl_name: actual_name
133
- for decl_name, actual_name in list(
134
- zip(
135
- [arg.name for arg in self.func_decl.get_positional_arg_decls()],
136
- self._py_callable.__annotations__.keys(),
137
- )
138
- )
139
- if decl_name != actual_name
134
+ decl_param: py_param
135
+ for decl_param, py_param in zip(decl_params, py_params.args)
136
+ if decl_param != py_param
140
137
  }
141
138
 
142
139
 
@@ -163,7 +160,7 @@ class QTerminalCallable(QCallable):
163
160
  return QTerminalCallable(self._decl, key)
164
161
 
165
162
  def __len__(self) -> int:
166
- raise ValueError(
163
+ raise ClassiqValueError(
167
164
  "len(<func>) is not supported for quantum callables - use <func>.len() instead (Only if it is an operand list)"
168
165
  )
169
166
 
@@ -205,6 +202,9 @@ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
205
202
 
206
203
 
207
204
  def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
205
+ if isinstance(val, QConstant):
206
+ val.add_to_model()
207
+ return Expression(expr=str(val.name))
208
208
  if isinstance(arg_decl, ClassicalParameterDeclaration):
209
209
  return Expression(expr=str(val))
210
210
  elif isinstance(arg_decl, PortDeclaration):
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  import functools
2
3
  from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
3
4
 
@@ -13,6 +14,7 @@ from classiq.interface.model.quantum_function_declaration import (
13
14
  from classiq.exceptions import ClassiqError
14
15
  from classiq.qmod.classical_function import CFunc
15
16
  from classiq.qmod.declaration_inferrer import infer_func_decl
17
+ from classiq.qmod.qmod_constant import QConstant
16
18
  from classiq.qmod.qmod_parameter import QParam
17
19
  from classiq.qmod.qmod_variable import QVar
18
20
  from classiq.qmod.quantum_callable import QCallable, QCallableList
@@ -63,21 +65,23 @@ class QFunc(QExpandable):
63
65
  ) -> Model:
64
66
  self._qmodule.type_decls = dict()
65
67
  self._qmodule.native_defs = dict()
68
+ self._qmodule.constants = dict()
69
+ QConstant.set_current_model(self._qmodule)
66
70
  self._add_native_func_def()
67
71
  model_extra_settings: List[Tuple[str, Any]] = [
68
72
  ("constraints", constraints),
69
73
  ("execution_preferences", execution_preferences),
70
74
  ("preferences", preferences),
71
75
  ]
72
- classical_execution_function = (
73
- CFunc.default_cmain()
74
- if classical_execution_function is None
75
- else classical_execution_function
76
- )
76
+ if classical_execution_function is not None:
77
+ self._add_constants_from_classical_code(classical_execution_function)
78
+ model_extra_settings.append(
79
+ ("classical_execution_code", classical_execution_function.code)
80
+ )
77
81
  return Model(
82
+ constants=list(self._qmodule.constants.values()),
78
83
  functions=list(self._qmodule.native_defs.values()),
79
84
  types=list(self._qmodule.type_decls.values()),
80
- classical_execution_code=classical_execution_function.code,
81
85
  **{key: value for key, value in model_extra_settings if value},
82
86
  )
83
87
 
@@ -89,18 +93,43 @@ class QFunc(QExpandable):
89
93
  **self.func_decl.__dict__, body=self.body
90
94
  )
91
95
 
96
+ def _add_constants_from_classical_code(
97
+ self, classical_execution_function: CFunc
98
+ ) -> None:
99
+ # FIXME: https://classiq.atlassian.net/browse/CAD-18050
100
+ # We use this visitor to add the constants that were used in the classical
101
+ # execution code to the model. In the future, if we will have a better notion
102
+ # of "QModule" and a "QConstant" will be a part of it then we may be able to
103
+ # remove the handling of the QConstants from this visitor, but I think we will
104
+ # need similar logic to allow using python variables in the classical execution
105
+ # code
106
+ class IdentifierVisitor(ast.NodeVisitor):
107
+ def visit_Name(self, node: ast.Name) -> None:
108
+ if (
109
+ node.id in classical_execution_function._caller_constants
110
+ and isinstance(
111
+ classical_execution_function._caller_constants[node.id],
112
+ QConstant,
113
+ )
114
+ ):
115
+ classical_execution_function._caller_constants[
116
+ node.id
117
+ ].add_to_model()
118
+
119
+ IdentifierVisitor().visit(ast.parse(classical_execution_function.code))
120
+
92
121
 
93
122
  class ExternalQFunc(QTerminalCallable):
94
123
  def __init__(self, py_callable: Callable) -> None:
95
124
  decl = _lookup_qfunc(unmangle_keyword(py_callable.__name__))
96
125
  if decl is None:
97
- raise ValueError(f"Definition of {py_callable.__name__!r} not found")
126
+ raise ClassiqError(f"Definition of {py_callable.__name__!r} not found")
98
127
 
99
128
  py_callable.__annotations__.pop("return", None)
100
129
  if py_callable.__annotations__.keys() != {
101
130
  mangle_keyword(arg.name) for arg in decl.get_positional_arg_decls()
102
131
  }:
103
- raise ValueError(
132
+ raise ClassiqError(
104
133
  f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
105
134
  )
106
135
  super().__init__(decl)
classiq/qmod/symbolic.py CHANGED
@@ -1,7 +1,10 @@
1
1
  import sys
2
- from typing import Tuple
2
+ from typing import Optional, Tuple, Type, get_args, get_origin, overload
3
3
 
4
- from classiq.qmod.qmod_parameter import QParamScalar
4
+ from classiq.exceptions import ClassiqValueError
5
+ from classiq.qmod import model_state_container
6
+ from classiq.qmod.declaration_inferrer import python_type_to_qmod
7
+ from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
5
8
  from classiq.qmod.symbolic_expr import SymbolicExpr
6
9
  from classiq.qmod.symbolic_type import SymbolicTypes
7
10
 
@@ -13,9 +16,44 @@ EulerGamma = SymbolicExpr("EulerGamma")
13
16
  Catalan = SymbolicExpr("Catalan")
14
17
 
15
18
 
16
- def symbolic_function(*args: SymbolicTypes) -> QParamScalar:
19
+ @overload
20
+ def symbolic_function(
21
+ *args: SymbolicTypes, return_type: None = None
22
+ ) -> QParamScalar: ...
23
+
24
+
25
+ @overload
26
+ def symbolic_function(*args: SymbolicTypes, return_type: Type[QParam]) -> QParam: ...
27
+
28
+
29
+ def symbolic_function(
30
+ *args: SymbolicTypes, return_type: Optional[Type[QParam]] = None
31
+ ) -> QParam:
32
+ qmodule = (
33
+ model_state_container.QMODULE
34
+ ) # FIXME: https://classiq.atlassian.net/browse/CAD-15126
17
35
  str_args = [str(x) for x in args]
18
- return QParamScalar(f"{sys._getframe(1).f_code.co_name}({','.join(str_args)})")
36
+ expr = f"{sys._getframe(1).f_code.co_name}({','.join(str_args)})"
37
+
38
+ if return_type is None:
39
+ return QParamScalar(expr)
40
+
41
+ if get_origin(return_type) is not QParam:
42
+ raise ClassiqValueError(
43
+ f"Unsupported return type for symbolic function: {return_type}"
44
+ )
45
+
46
+ qmod_type = python_type_to_qmod(get_args(return_type)[0], qmodule=qmodule)
47
+ if qmod_type is None:
48
+ raise ClassiqValueError(
49
+ f"Unsupported return type for symbolic function: {return_type}"
50
+ )
51
+
52
+ return create_param(
53
+ expr,
54
+ qmod_type,
55
+ qmodule,
56
+ )
19
57
 
20
58
 
21
59
  def sin(x: SymbolicTypes) -> QParamScalar:
@@ -238,6 +276,10 @@ def logical_not(x: SymbolicTypes) -> SymbolicExpr:
238
276
  return SymbolicExpr._unary_op(x, "not")
239
277
 
240
278
 
279
+ def mod_inverse(a: SymbolicTypes, m: SymbolicTypes) -> QParamScalar:
280
+ return symbolic_function(a, m)
281
+
282
+
241
283
  __all__ = [
242
284
  "pi",
243
285
  "E",
@@ -300,6 +342,7 @@ __all__ = [
300
342
  "logical_and",
301
343
  "logical_or",
302
344
  "logical_not",
345
+ "mod_inverse",
303
346
  ]
304
347
 
305
348
 
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import ast
3
4
  from typing import Any
4
5
 
5
6
 
@@ -13,6 +14,14 @@ class Symbolic:
13
14
  def __repr__(self) -> str:
14
15
  return self._expr
15
16
 
17
+ def __bool__(self) -> bool:
18
+ try:
19
+ return bool(ast.literal_eval(self._expr))
20
+ except ValueError:
21
+ raise TypeError(
22
+ f"Symbolic expression {self._expr!r} cannot be converted to bool"
23
+ ) from None
24
+
16
25
 
17
26
  class SymbolicExpr(Symbolic):
18
27
  def __init__(self, expr: str) -> None:
classiq/qmod/utilities.py CHANGED
@@ -1,4 +1,8 @@
1
1
  import keyword
2
+ import sys
3
+ from typing import get_args, get_origin
4
+
5
+ DEFAULT_DECIMAL_PRECISION = 4
2
6
 
3
7
 
4
8
  def mangle_keyword(name: str) -> str:
@@ -12,3 +16,12 @@ def unmangle_keyword(name: str) -> str:
12
16
  if name[-1] == "_" and keyword.iskeyword(name[:-1]):
13
17
  name = name[:-1]
14
18
  return name
19
+
20
+
21
+ def version_portable_get_args(py_type: type) -> tuple:
22
+ if get_origin(py_type) is None:
23
+ return tuple()
24
+ if sys.version_info[0:2] < (3, 10):
25
+ return get_args(py_type) # The result of __class_getitem__
26
+ else:
27
+ return get_args(py_type)[0]
@@ -0,0 +1,39 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from classiq.interface.model.model import Model, SerializedModel
6
+
7
+ from classiq.qmod.native.pretty_printer import DSLPrettyPrinter
8
+ from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
9
+
10
+ _QMOD_SUFFIX = "qmod"
11
+ _SYNTHESIS_OPTIONS_SUFFIX = "synthesis_options.json"
12
+
13
+
14
+ def write_qmod(
15
+ serialized_model: SerializedModel,
16
+ name: str,
17
+ directory: Optional[Path] = None,
18
+ decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
19
+ ) -> None:
20
+ model = Model.parse_raw(serialized_model)
21
+ pretty_printed_model = DSLPrettyPrinter(decimal_precision=decimal_precision).visit(
22
+ model
23
+ )
24
+
25
+ synthesis_options = model.dict(
26
+ include={"constraints", "preferences"}, exclude_unset=True
27
+ )
28
+
29
+ synthesis_options_path = Path(f"{name}.{_SYNTHESIS_OPTIONS_SUFFIX}")
30
+ if directory is not None:
31
+ synthesis_options_path = directory / synthesis_options_path
32
+
33
+ synthesis_options_path.write_text(json.dumps(synthesis_options, indent=2))
34
+
35
+ native_qmod_path = Path(f"{name}.{_QMOD_SUFFIX}")
36
+ if directory is not None:
37
+ native_qmod_path = directory / native_qmod_path
38
+
39
+ native_qmod_path.write_text(pretty_printed_model)
@@ -1,4 +1,4 @@
1
- from classiq.quantum_functions.decorators import quantum_function as qfunc
1
+ from classiq.quantum_functions.decorators import quantum_function
2
2
  from classiq.quantum_functions.function_library import (
3
3
  QASM3_INTRO,
4
4
  QASM_INTRO,
@@ -8,7 +8,7 @@ from classiq.quantum_functions.function_library import (
8
8
  )
9
9
 
10
10
  __all__ = [
11
- "qfunc",
11
+ "quantum_function",
12
12
  "QASM_INTRO",
13
13
  "QASM3_INTRO",
14
14
  "FunctionLibrary",
@@ -1,4 +1,4 @@
1
- # type: ignore
1
+ # type: ignore # noqa: PGH003
2
2
  # We can either ignore each line individually, or ignore the entire file and wait until mypy can ignore
3
3
  # specific errors per-file.
4
4
  import inspect
@@ -77,18 +77,16 @@ class AnnotationParser:
77
77
  self.input_names, self.input_values, self.output_values
78
78
  ):
79
79
  # Is arithmetic QReg
80
- if issubclass(input_type.__origin__, QSFixed):
81
- if input_type != output_type:
82
- raise ClassiqQFuncError(
83
- f"Arithmetic QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
84
- )
80
+ if issubclass(input_type.__origin__, QSFixed) and input_type != output_type:
81
+ raise ClassiqQFuncError(
82
+ f"Arithmetic QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
83
+ )
85
84
 
86
85
  # Is Auxillary QReg
87
- if issubclass(input_type.__origin__, AuxQReg):
88
- if input_type != output_type:
89
- raise ClassiqQFuncError(
90
- f"Auxillary QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
91
- )
86
+ if issubclass(input_type.__origin__, AuxQReg) and input_type != output_type:
87
+ raise ClassiqQFuncError(
88
+ f"Auxillary QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
89
+ )
92
90
 
93
91
  # Is Zero QReg
94
92
  if input_type.__origin__ is ZeroQReg:
@@ -36,7 +36,7 @@ class FunctionParser:
36
36
  def _validate_function_output(output: Any) -> None:
37
37
  # Todo: validate QASM
38
38
 
39
- if type(output) is not str:
39
+ if not isinstance(output, str):
40
40
  raise ClassiqQFuncError(
41
41
  "Invalid output. Please return a string of OpenQASM2.0."
42
42
  )
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
3
  from types import FunctionType
4
- from typing import Callable, Dict, List, Optional, Sequence, Tuple
4
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple
5
5
 
6
6
  from classiq.interface.generator.functions import (
7
7
  FunctionImplementation,
@@ -213,7 +213,7 @@ class QuantumFunctionFactory(ABC):
213
213
  )
214
214
  return "_".join(str_list)
215
215
 
216
- def __call__(self, *args, **kwargs):
216
+ def __call__(self, *args: Any, **kwargs: Any) -> Any:
217
217
  try:
218
218
  return self._apply_method(str(self), *args, **kwargs)
219
219
  except AttributeError as e:
@@ -227,7 +227,7 @@ class QuantumFunctionFactory(ABC):
227
227
  """
228
228
  Abstract method for providing the definition of the user function.
229
229
  The QuantumFunction object may be generated either directly, or using existing
230
- helper tools such as the @qfunc decorator.
230
+ helper tools such as the @quantum_function decorator.
231
231
  Instance attributes of the QuantumFunctionFactory may be used as parameters for the
232
232
  definition.
233
233