classiq 0.37.1__py3-none-any.whl → 0.39.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 (280) hide show
  1. classiq/__init__.py +23 -24
  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 +37 -17
  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 +24 -6
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +29 -29
  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/__init__.py +1 -8
  16. classiq/applications/chemistry/__init__.py +6 -0
  17. classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +9 -16
  18. classiq/applications/combinatorial_helpers/allowed_constraints.py +20 -0
  19. classiq/applications/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  20. classiq/applications/combinatorial_helpers/arithmetic/isolation.py +42 -0
  21. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +150 -0
  22. classiq/applications/combinatorial_helpers/encoding_mapping.py +107 -0
  23. classiq/applications/combinatorial_helpers/encoding_utils.py +122 -0
  24. classiq/applications/combinatorial_helpers/memory.py +77 -0
  25. classiq/applications/combinatorial_helpers/optimization_model.py +162 -0
  26. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  27. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +75 -0
  28. classiq/applications/combinatorial_helpers/py.typed +0 -0
  29. classiq/applications/combinatorial_helpers/pyomo_utils.py +245 -0
  30. classiq/applications/combinatorial_helpers/solvers/__init__.py +0 -0
  31. classiq/applications/combinatorial_helpers/sympy_utils.py +22 -0
  32. classiq/applications/combinatorial_helpers/transformations/__init__.py +0 -0
  33. classiq/applications/combinatorial_helpers/transformations/encoding.py +187 -0
  34. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +142 -0
  35. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +122 -0
  36. classiq/applications/combinatorial_helpers/transformations/penalty.py +32 -0
  37. classiq/applications/combinatorial_helpers/transformations/penalty_support.py +37 -0
  38. classiq/applications/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  39. classiq/applications/combinatorial_helpers/transformations/slack_variables.py +88 -0
  40. classiq/applications/combinatorial_optimization/__init__.py +13 -2
  41. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +134 -0
  42. classiq/applications/finance/__init__.py +3 -2
  43. classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +27 -30
  44. classiq/applications/grover/__init__.py +11 -0
  45. classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +20 -91
  46. classiq/applications/libraries/__init__.py +0 -0
  47. classiq/applications/libraries/qmci_library.py +35 -0
  48. classiq/applications/qnn/circuit_utils.py +2 -2
  49. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  50. classiq/applications/qnn/types.py +2 -2
  51. classiq/applications/qsvm/__init__.py +5 -1
  52. classiq/applications/qsvm/qsvm.py +4 -7
  53. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  54. classiq/exceptions.py +43 -1
  55. classiq/execution/all_hardware_devices.py +13 -0
  56. classiq/executor.py +12 -10
  57. classiq/interface/_version.py +1 -1
  58. classiq/interface/analyzer/analysis_params.py +6 -3
  59. classiq/interface/analyzer/result.py +12 -8
  60. classiq/interface/applications/qsvm.py +17 -3
  61. classiq/interface/ast_node.py +23 -0
  62. classiq/interface/backend/backend_preferences.py +4 -2
  63. classiq/interface/backend/pydantic_backend.py +3 -1
  64. classiq/interface/backend/quantum_backend_providers.py +1 -0
  65. classiq/interface/chemistry/fermionic_operator.py +15 -13
  66. classiq/interface/chemistry/ground_state_problem.py +18 -3
  67. classiq/interface/chemistry/molecule.py +8 -6
  68. classiq/interface/chemistry/operator.py +20 -14
  69. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  70. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  71. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  73. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  74. classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
  75. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  76. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  77. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  78. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  79. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  80. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  81. classiq/interface/executor/aws_execution_cost.py +4 -3
  82. classiq/interface/executor/estimation.py +2 -2
  83. classiq/interface/executor/execution_preferences.py +5 -34
  84. classiq/interface/executor/execution_request.py +15 -48
  85. classiq/interface/executor/optimizer_preferences.py +22 -13
  86. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  87. classiq/interface/executor/quantum_instruction_set.py +2 -1
  88. classiq/interface/executor/register_initialization.py +1 -3
  89. classiq/interface/executor/result.py +41 -10
  90. classiq/interface/executor/vqe_result.py +2 -2
  91. classiq/interface/finance/function_input.py +17 -4
  92. classiq/interface/finance/gaussian_model_input.py +3 -1
  93. classiq/interface/finance/log_normal_model_input.py +3 -1
  94. classiq/interface/finance/model_input.py +2 -0
  95. classiq/interface/generator/amplitude_loading.py +6 -3
  96. classiq/interface/generator/application_apis/__init__.py +1 -0
  97. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  98. classiq/interface/generator/arith/argument_utils.py +14 -4
  99. classiq/interface/generator/arith/arithmetic.py +3 -1
  100. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  101. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  102. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  103. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  104. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  105. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  106. classiq/interface/generator/arith/binary_ops.py +202 -54
  107. classiq/interface/generator/arith/extremum_operations.py +5 -3
  108. classiq/interface/generator/arith/logical_ops.py +4 -2
  109. classiq/interface/generator/arith/machine_precision.py +3 -0
  110. classiq/interface/generator/arith/number_utils.py +34 -44
  111. classiq/interface/generator/arith/register_user_input.py +21 -1
  112. classiq/interface/generator/arith/unary_ops.py +16 -25
  113. classiq/interface/generator/builtin_api_builder.py +0 -5
  114. classiq/interface/generator/chemistry_function_params.py +4 -4
  115. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  116. classiq/interface/generator/compiler_keywords.py +4 -0
  117. classiq/interface/generator/complex_type.py +3 -10
  118. classiq/interface/generator/constant.py +2 -3
  119. classiq/interface/generator/control_state.py +5 -3
  120. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  121. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  122. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  123. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  124. classiq/interface/generator/expressions/expression.py +3 -5
  125. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  126. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  127. classiq/interface/generator/finance.py +1 -1
  128. classiq/interface/generator/function_params.py +7 -6
  129. classiq/interface/generator/functions/__init__.py +2 -2
  130. classiq/interface/generator/functions/builtins/__init__.py +15 -0
  131. classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
  132. classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
  133. classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
  134. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +612 -219
  135. classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
  136. classiq/interface/generator/functions/classical_type.py +2 -4
  137. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  138. classiq/interface/generator/functions/function_declaration.py +2 -2
  139. classiq/interface/generator/functions/function_implementation.py +8 -4
  140. classiq/interface/generator/functions/native_function_definition.py +4 -2
  141. classiq/interface/generator/functions/register.py +4 -2
  142. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  143. classiq/interface/generator/generated_circuit_data.py +2 -2
  144. classiq/interface/generator/grover_operator.py +5 -3
  145. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  146. classiq/interface/generator/hardware/hardware_data.py +6 -4
  147. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  148. classiq/interface/generator/hartree_fock.py +13 -3
  149. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  150. classiq/interface/generator/mcu.py +5 -3
  151. classiq/interface/generator/mcx.py +7 -5
  152. classiq/interface/generator/model/classical_main_validator.py +1 -1
  153. classiq/interface/generator/model/constraints.py +2 -1
  154. classiq/interface/generator/model/model.py +12 -20
  155. classiq/interface/generator/model/preferences/preferences.py +4 -3
  156. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  157. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  158. classiq/interface/generator/qpe.py +6 -4
  159. classiq/interface/generator/qsvm.py +5 -8
  160. classiq/interface/generator/quantum_function_call.py +21 -16
  161. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  162. classiq/interface/generator/range_types.py +3 -1
  163. classiq/interface/generator/slice_parsing_utils.py +8 -3
  164. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  165. classiq/interface/generator/state_preparation/metrics.py +2 -1
  166. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  167. classiq/interface/generator/state_propagator.py +16 -5
  168. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  169. classiq/interface/generator/types/struct_declaration.py +10 -7
  170. classiq/interface/generator/ucc.py +6 -4
  171. classiq/interface/generator/unitary_gate.py +7 -3
  172. classiq/interface/generator/validations/flow_graph.py +6 -4
  173. classiq/interface/generator/validations/validator_functions.py +6 -4
  174. classiq/interface/hardware.py +2 -2
  175. classiq/interface/helpers/custom_encoders.py +3 -0
  176. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  177. classiq/interface/helpers/validation_helpers.py +1 -1
  178. classiq/interface/helpers/versioned_model.py +4 -1
  179. classiq/interface/ide/show.py +2 -2
  180. classiq/interface/jobs.py +72 -3
  181. classiq/interface/model/bind_operation.py +18 -11
  182. classiq/interface/model/call_synthesis_data.py +68 -0
  183. classiq/interface/model/classical_if.py +13 -0
  184. classiq/interface/model/classical_parameter_declaration.py +2 -3
  185. classiq/interface/model/control.py +16 -0
  186. classiq/interface/model/handle_binding.py +3 -2
  187. classiq/interface/model/inplace_binary_operation.py +2 -2
  188. classiq/interface/model/invert.py +10 -0
  189. classiq/interface/model/model.py +29 -22
  190. classiq/interface/model/native_function_definition.py +3 -5
  191. classiq/interface/model/power.py +12 -0
  192. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  193. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  194. classiq/interface/model/quantum_function_call.py +33 -142
  195. classiq/interface/model/quantum_function_declaration.py +8 -0
  196. classiq/interface/model/quantum_if_operation.py +4 -5
  197. classiq/interface/model/quantum_lambda_function.py +58 -0
  198. classiq/{quantum_register.py → interface/model/quantum_register.py} +17 -9
  199. classiq/interface/model/quantum_statement.py +3 -2
  200. classiq/interface/model/quantum_type.py +58 -59
  201. classiq/interface/model/quantum_variable_declaration.py +3 -3
  202. classiq/interface/model/repeat.py +13 -0
  203. classiq/interface/model/resolvers/function_call_resolver.py +26 -0
  204. classiq/interface/model/statement_block.py +49 -0
  205. classiq/interface/model/validations/handles_validator.py +16 -18
  206. classiq/interface/model/within_apply_operation.py +11 -0
  207. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  208. classiq/interface/server/routes.py +5 -4
  209. classiq/qmod/__init__.py +13 -6
  210. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  211. classiq/qmod/builtins/classical_functions.py +22 -12
  212. classiq/qmod/builtins/functions.py +272 -328
  213. classiq/qmod/builtins/operations.py +171 -35
  214. classiq/qmod/builtins/structs.py +15 -15
  215. classiq/qmod/cfunc.py +42 -0
  216. classiq/qmod/classical_function.py +6 -14
  217. classiq/qmod/declaration_inferrer.py +12 -21
  218. classiq/qmod/expression_query.py +23 -0
  219. classiq/qmod/model_state_container.py +2 -0
  220. classiq/qmod/native/__init__.py +0 -0
  221. classiq/qmod/native/expression_to_qmod.py +189 -0
  222. classiq/qmod/native/pretty_printer.py +340 -0
  223. classiq/qmod/qfunc.py +27 -0
  224. classiq/qmod/qmod_constant.py +100 -0
  225. classiq/qmod/qmod_parameter.py +36 -13
  226. classiq/qmod/qmod_struct.py +3 -3
  227. classiq/qmod/qmod_variable.py +148 -31
  228. classiq/qmod/quantum_callable.py +1 -0
  229. classiq/qmod/quantum_expandable.py +18 -19
  230. classiq/qmod/quantum_function.py +41 -8
  231. classiq/qmod/symbolic.py +48 -5
  232. classiq/qmod/symbolic_expr.py +9 -0
  233. classiq/qmod/utilities.py +13 -0
  234. classiq/qmod/write_qmod.py +39 -0
  235. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/METADATA +2 -1
  236. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/RECORD +244 -225
  237. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/WHEEL +1 -1
  238. classiq/applications/benchmarking/__init__.py +0 -9
  239. classiq/applications/benchmarking/mirror_benchmarking.py +0 -67
  240. classiq/applications/numpy_utils.py +0 -37
  241. classiq/applications_model_constructors/__init__.py +0 -17
  242. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +0 -178
  243. classiq/applications_model_constructors/libraries/qmci_library.py +0 -109
  244. classiq/builtin_functions/__init__.py +0 -43
  245. classiq/builtin_functions/amplitude_loading.py +0 -3
  246. classiq/builtin_functions/binary_ops.py +0 -1
  247. classiq/builtin_functions/exponentiation.py +0 -5
  248. classiq/builtin_functions/qpe.py +0 -4
  249. classiq/builtin_functions/qsvm.py +0 -7
  250. classiq/builtin_functions/range_types.py +0 -5
  251. classiq/builtin_functions/standard_gates.py +0 -1
  252. classiq/builtin_functions/state_preparation.py +0 -6
  253. classiq/builtin_functions/suzuki_trotter.py +0 -3
  254. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  255. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
  256. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -169
  257. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  258. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  259. classiq/interface/model/numeric_reinterpretation.py +0 -25
  260. classiq/interface/model/operator_synthesis_data.py +0 -48
  261. classiq/model/__init__.py +0 -14
  262. classiq/model/composite_function_generator.py +0 -33
  263. classiq/model/function_handler.py +0 -466
  264. classiq/model/function_handler.pyi +0 -152
  265. classiq/model/logic_flow.py +0 -149
  266. classiq/model/logic_flow_change_handler.py +0 -71
  267. classiq/model/model.py +0 -246
  268. classiq/quantum_functions/__init__.py +0 -17
  269. classiq/quantum_functions/annotation_parser.py +0 -207
  270. classiq/quantum_functions/decorators.py +0 -22
  271. classiq/quantum_functions/function_library.py +0 -181
  272. classiq/quantum_functions/function_parser.py +0 -74
  273. classiq/quantum_functions/quantum_function.py +0 -236
  274. /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers}/__init__.py +0 -0
  275. /classiq/{interface/generator/functions/core_lib_declarations → applications/combinatorial_helpers/arithmetic}/__init__.py +0 -0
  276. /classiq/{interface/generator/functions/core_lib_declarations/quantum_functions/chemistry_functions.py → applications/combinatorial_helpers/pauli_helpers/__init__.py} +0 -0
  277. /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
  278. /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
  279. /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +0 -0
  280. /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +0 -0
@@ -1,6 +1,12 @@
1
1
  import sys
2
- from typing import _GenericAlias # type: ignore[attr-defined]
3
- from typing import Any, Generic, TypeVar, Union
2
+ from typing import ( # type: ignore[attr-defined]
3
+ TYPE_CHECKING,
4
+ Any,
5
+ Generic,
6
+ TypeVar,
7
+ Union,
8
+ _GenericAlias,
9
+ )
4
10
 
5
11
  from typing_extensions import ParamSpec
6
12
 
@@ -19,8 +25,15 @@ from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
19
25
  _T = TypeVar("_T")
20
26
 
21
27
 
22
- class QParam(Symbolic, Generic[_T]):
23
- pass
28
+ if TYPE_CHECKING:
29
+
30
+ class QParam(SymbolicExpr, Generic[_T]):
31
+ pass
32
+
33
+ else:
34
+
35
+ class QParam(Symbolic, Generic[_T]):
36
+ pass
24
37
 
25
38
 
26
39
  class QParamScalar(QParam, SymbolicExpr):
@@ -46,10 +59,11 @@ class QParamList(QParam):
46
59
  )
47
60
 
48
61
  def __len__(self) -> int:
49
- raise ValueError(
50
- "len(<expr>) is not supported for QMod lists - use <expr>.len() instead"
62
+ raise ClassiqValueError(
63
+ "len(<expr>) is not supported for QMod lists - use <expr>.len instead"
51
64
  )
52
65
 
66
+ @property
53
67
  def len(self) -> QParamScalar:
54
68
  return QParamScalar(f"len({self})")
55
69
 
@@ -63,22 +77,31 @@ class QParamStruct(QParam):
63
77
  self._struct_type = struct_type
64
78
 
65
79
  def __getattr__(self, field_name: str) -> QParam:
80
+ return QParamStruct.get_field(
81
+ self._qmodule, str(self), self._struct_type.name, field_name
82
+ )
83
+
84
+ @staticmethod
85
+ def get_field(
86
+ qmodule: ModelStateContainer,
87
+ variable_name: str,
88
+ struct_name: str,
89
+ field_name: str,
90
+ ) -> QParam:
66
91
  struct_decl = StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.get(
67
- self._struct_type.name
92
+ struct_name, qmodule.type_decls.get(struct_name)
68
93
  )
69
- if struct_decl is None:
70
- struct_decl = self._qmodule.type_decls.get(self._struct_type.name)
71
94
  assert struct_decl is not None
72
95
  field_type = struct_decl.variables.get(field_name)
73
96
  if field_type is None:
74
97
  raise ClassiqValueError(
75
- f"Struct {self._struct_type.name!r} doesn't have field {field_name!r}"
98
+ f"Struct {struct_name!r} doesn't have field {field_name!r}"
76
99
  )
77
100
 
78
101
  return create_param(
79
- f"get_field({self},{field_name!r})",
102
+ f"get_field({variable_name},{field_name!r})",
80
103
  field_type,
81
- qmodule=self._qmodule,
104
+ qmodule=qmodule,
82
105
  )
83
106
 
84
107
 
@@ -100,7 +123,7 @@ class Array(ArrayBase[_P]):
100
123
  def create_param(
101
124
  expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
102
125
  ) -> QParam:
103
- if isinstance(ctype, ClassicalList) or isinstance(ctype, ClassicalArray):
126
+ if isinstance(ctype, (ClassicalList, ClassicalArray)):
104
127
  return QParamList(expr_str, ctype, qmodule=qmodule)
105
128
  elif isinstance(ctype, Struct):
106
129
  return QParamStruct(expr_str, ctype, qmodule=qmodule)
@@ -1,5 +1,5 @@
1
1
  import dataclasses
2
- from typing import Any, List, Type
2
+ from typing import Any, Type
3
3
 
4
4
  from typing_extensions import dataclass_transform
5
5
 
@@ -16,7 +16,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
16
16
  )
17
17
  return f"struct_literal({type(val).__name__}, {kwargs_str})"
18
18
 
19
- if isinstance(val, List):
19
+ if isinstance(val, list):
20
20
  elements_str = ", ".join([_qmod_val_to_expr_str(elem) for elem in val])
21
21
  return f"[{elements_str}]"
22
22
 
@@ -24,7 +24,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
24
24
 
25
25
 
26
26
  @dataclass_transform()
27
- def QStruct(user_class: Type) -> Type: # noqa: N802 - for consistency with 'QFunc'
27
+ def struct(user_class: Type) -> Type:
28
28
  def _new_repr(self: Any) -> str:
29
29
  return _qmod_val_to_expr_str(self)
30
30
 
@@ -1,6 +1,7 @@
1
1
  import abc
2
+ import sys
2
3
  from contextlib import contextmanager
3
- from typing import (
4
+ from typing import ( # type: ignore[attr-defined]
4
5
  TYPE_CHECKING,
5
6
  Any,
6
7
  ForwardRef,
@@ -12,8 +13,10 @@ from typing import (
12
13
  Type,
13
14
  TypeVar,
14
15
  Union,
16
+ _GenericAlias,
15
17
  get_args,
16
18
  get_origin,
19
+ overload,
17
20
  )
18
21
 
19
22
  from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
@@ -22,7 +25,11 @@ from classiq.interface.generator.expressions.expression import Expression
22
25
  from classiq.interface.generator.functions.port_declaration import (
23
26
  PortDeclarationDirection,
24
27
  )
25
- from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
28
+ from classiq.interface.model.handle_binding import (
29
+ HandleBinding,
30
+ SlicedHandleBinding,
31
+ SubscriptHandleBinding,
32
+ )
26
33
  from classiq.interface.model.port_declaration import PortDeclaration
27
34
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
28
35
  AmplitudeLoadingOperation,
@@ -40,11 +47,14 @@ from classiq.interface.model.quantum_type import (
40
47
  from classiq.exceptions import ClassiqValueError
41
48
  from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
42
49
  from classiq.qmod.quantum_callable import QCallable
43
- from classiq.qmod.symbolic_expr import SymbolicExpr
50
+ from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
44
51
  from classiq.qmod.symbolic_type import SymbolicTypes
52
+ from classiq.qmod.utilities import version_portable_get_args
45
53
 
46
54
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
47
55
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
56
+ UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
57
+ QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
48
58
 
49
59
 
50
60
  def _is_input_output_typehint(type_hint: Any) -> bool:
@@ -183,22 +193,72 @@ class QBit(QScalar):
183
193
  return QuantumBit()
184
194
 
185
195
 
186
- _T = TypeVar("_T")
196
+ _P = ParamSpec("_P")
187
197
 
188
198
 
189
- class QNum(Generic[_T], QScalar):
199
+ class QNum(Generic[_P], QScalar):
190
200
  QMOD_TYPE = QuantumNumeric
191
201
 
202
+ @overload
203
+ def __init__(self, name: str):
204
+ pass
205
+
206
+ @overload
207
+ def __init__(
208
+ self,
209
+ name: str,
210
+ size: Union[int, QParam[int]],
211
+ is_signed: Union[bool, QParam[bool]],
212
+ fraction_digits: Union[int, QParam[int]],
213
+ ):
214
+ pass
215
+
216
+ def __init__(
217
+ self,
218
+ name: str,
219
+ size: Union[int, QParam[int], None] = None,
220
+ is_signed: Union[bool, QParam[bool], None] = None,
221
+ fraction_digits: Union[int, QParam[int], None] = None,
222
+ ):
223
+ if (
224
+ size is None
225
+ and (is_signed is not None or fraction_digits is not None)
226
+ or size is not None
227
+ and (is_signed is None or fraction_digits is None)
228
+ ):
229
+ raise ClassiqValueError(
230
+ "Assign none or all of size, is_signed, and fraction_digits"
231
+ )
232
+ self._size = None if size is None else Expression(expr=str(size))
233
+ self._is_signed = None if is_signed is None else Expression(expr=str(is_signed))
234
+ self._fraction_digits = (
235
+ None if fraction_digits is None else Expression(expr=str(fraction_digits))
236
+ )
237
+ super().__init__(name)
238
+
192
239
  @classmethod
193
240
  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)
241
+ type_args = get_args(type_hint)
242
+ if len(type_args) == 0:
243
+ return cls.QMOD_TYPE()
244
+ type_args = type_args[0]
245
+ if len(type_args) != 3:
246
+ raise ClassiqValueError(
247
+ "QNum receives three type arguments: QNum[size: int | QParam[int], "
248
+ "is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
249
+ )
250
+ return cls.QMOD_TYPE(
251
+ size=Expression(expr=get_type_hint_expr(type_args[0])),
252
+ is_signed=Expression(expr=get_type_hint_expr(type_args[1])),
253
+ fraction_digits=Expression(expr=get_type_hint_expr(type_args[2])),
254
+ )
199
255
 
200
256
  def get_qmod_type(self) -> QuantumType:
201
- return self.QMOD_TYPE()
257
+ return self.QMOD_TYPE(
258
+ size=self._size,
259
+ is_signed=self._is_signed,
260
+ fraction_digits=self._fraction_digits,
261
+ )
202
262
 
203
263
  @property
204
264
  def size(self) -> QParamScalar:
@@ -212,62 +272,119 @@ class QNum(Generic[_T], QScalar):
212
272
  def is_signed(self) -> QParamScalar:
213
273
  return QParamScalar(f"is_signed({self._name})")
214
274
 
275
+ # Support comma-separated generic args in older Python versions
276
+ if sys.version_info[0:2] < (3, 10):
215
277
 
216
- _P = ParamSpec("_P")
278
+ def __class_getitem__(cls, args) -> _GenericAlias:
279
+ return _GenericAlias(cls, args)
217
280
 
218
281
 
219
282
  class QArray(ArrayBase[_P], QVar):
220
- def __init__(self, name: str, slice_: Optional[Tuple[int, int]] = None) -> None:
221
- super().__init__(name)
283
+ def __init__(
284
+ self,
285
+ name: str,
286
+ element_type: _GenericAlias = QBit,
287
+ length: Optional[Union[int, QParam[int]]] = None,
288
+ # TODO [CAD-18620]: improve type hints
289
+ slice_: Optional[Tuple[int, int]] = None,
290
+ index_: Optional[Union[int, QParam[int]]] = None,
291
+ ) -> None:
292
+ if element_type is not QBit:
293
+ raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
294
+ self._element_type = element_type
295
+ self._length = length
222
296
  self._slice = slice_
297
+ self._index = index_
298
+ super().__init__(name)
223
299
 
224
300
  def get_handle_binding(self) -> HandleBinding:
225
- if self._slice is None:
226
- return HandleBinding(name=self._name)
227
- return SlicedHandleBinding(
228
- name=self._name,
229
- start=Expression(expr=str(self._slice[0])),
230
- end=Expression(expr=str(self._slice[1])),
231
- )
301
+ if self._index is not None:
302
+ return SubscriptHandleBinding(
303
+ name=self._name,
304
+ index=Expression(expr=str(self._index)),
305
+ )
306
+
307
+ if self._slice is not None:
308
+ return SlicedHandleBinding(
309
+ name=self._name,
310
+ start=Expression(expr=str(self._slice[0])),
311
+ end=Expression(expr=str(self._slice[1])),
312
+ )
313
+
314
+ return HandleBinding(name=self._name)
232
315
 
233
316
  def __getitem__(self, key: Union[slice, int, QParam]) -> "QArray":
234
- offset = self._slice[0] if self._slice is not None else 0
317
+ if self._index is not None:
318
+ raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
319
+
320
+ # TODO [CAD-18620]: improve type hints
321
+ new_index: Optional[Any] = None
322
+
235
323
  if isinstance(key, slice):
236
324
  if key.step is not None:
237
- raise NotImplementedError(ILLEGAL_SLICING_STEP_MSG)
238
- new_slice = (offset + key.start, offset + key.stop)
325
+ raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
326
+ new_slice = self._get_new_slice(key.start, key.stop)
327
+
239
328
  else:
240
329
  if isinstance(key, QParam) and not isinstance(key, QParamScalar):
241
330
  raise ClassiqValueError("Non-classical parameter for slicing")
242
- new_slice = (offset + key, offset + key + 1)
243
- if self._slice is not None and new_slice[1] > self._slice[1]:
331
+ new_slice = self._get_new_slice(key, key + 1)
332
+ new_index = new_slice[0]
333
+
334
+ if (
335
+ self._slice is not None
336
+ and not isinstance(new_slice[1], Symbolic)
337
+ and not isinstance(self._slice[1], Symbolic)
338
+ and new_slice[1] > self._slice[1]
339
+ ) or (
340
+ self._length is not None
341
+ and not isinstance(new_slice[1], Symbolic)
342
+ and not isinstance(self._length, Symbolic)
343
+ and new_slice[1] > self._length
344
+ ):
244
345
  raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
245
346
  # prevent addition to local handles, since this is used for slicing existing local handles
246
347
  with _no_current_expandable():
247
- return QArray(self._name, slice_=new_slice)
348
+ return QArray(
349
+ self._name, length=self._length, slice_=new_slice, index_=new_index
350
+ )
351
+
352
+ # TODO [CAD-18620]: improve type hints
353
+ def _get_new_slice(self, start: Any, end: Any) -> Tuple[Any, Any]:
354
+ if self._slice is not None:
355
+ return (self._slice[0] + start, self._slice[0] + end)
356
+ return (start, end)
248
357
 
249
358
  def __len__(self) -> int:
250
- raise ValueError(
251
- "len(<var>) is not supported for quantum variables - use <var>.len() instead"
359
+ raise ClassiqValueError(
360
+ "len(<var>) is not supported for quantum variables - use <var>.len instead"
252
361
  )
253
362
 
254
363
  if TYPE_CHECKING:
255
364
 
365
+ @property
256
366
  def len(self) -> int: ...
257
367
 
258
368
  else:
259
369
 
370
+ @property
260
371
  def len(self) -> QParamScalar:
372
+ if self._length is not None:
373
+ return QParamScalar(f"{self._length}")
261
374
  return QParamScalar(f"len({self._name})")
262
375
 
263
376
  @classmethod
264
377
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
265
378
  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]))
379
+ if len(version_portable_get_args(type_hint)) == 2:
380
+ length_expr = Expression(
381
+ expr=get_type_hint_expr(version_portable_get_args(type_hint)[1])
382
+ )
268
383
  return QuantumBitvector(length=length_expr)
269
384
 
270
385
  def get_qmod_type(self) -> QuantumType:
386
+ if self._length is not None:
387
+ return QuantumBitvector(length=Expression(expr=str(self._length)))
271
388
  return QuantumBitvector()
272
389
 
273
390
 
@@ -68,6 +68,7 @@ class QCallable(Generic[P], ABC):
68
68
  class QCallableList(QCallable, Generic[P], ABC):
69
69
  if TYPE_CHECKING:
70
70
 
71
+ @property
71
72
  def len(self) -> int: ...
72
73
 
73
74
  def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
@@ -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,13 @@ 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 QuantumLambdaFunction
36
36
  from classiq.interface.model.quantum_statement import QuantumStatement
37
37
  from classiq.interface.model.quantum_type import QuantumType
38
38
  from classiq.interface.model.variable_declaration_statement import (
@@ -41,6 +41,7 @@ from classiq.interface.model.variable_declaration_statement import (
41
41
 
42
42
  from classiq.exceptions import ClassiqValueError
43
43
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
44
+ from classiq.qmod.qmod_constant import QConstant
44
45
  from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
45
46
  from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
46
47
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
@@ -96,9 +97,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
96
97
  for arg in self.func_decl.get_positional_arg_decls():
97
98
  if isinstance(arg, ClassicalParameterDeclaration):
98
99
  rename_dict = self.infer_rename_params()
99
- actual_name = (
100
- rename_dict[arg.name] if arg.name in rename_dict else arg.name
101
- )
100
+ actual_name = rename_dict.get(arg.name, arg.name)
102
101
  result.append(
103
102
  create_param(actual_name, arg.classical_type, self._qmodule)
104
103
  )
@@ -126,17 +125,12 @@ class QLambdaFunction(QExpandable):
126
125
  return self._decl
127
126
 
128
127
  def infer_rename_params(self) -> Dict[str, str]:
129
- if not self._py_callable.__annotations__:
130
- return {}
128
+ py_params = inspect.getfullargspec(self._py_callable)
129
+ decl_params = self.func_decl.param_decls.keys()
131
130
  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
131
+ decl_param: py_param
132
+ for decl_param, py_param in zip(decl_params, py_params.args)
133
+ if decl_param != py_param
140
134
  }
141
135
 
142
136
 
@@ -163,16 +157,18 @@ class QTerminalCallable(QCallable):
163
157
  return QTerminalCallable(self._decl, key)
164
158
 
165
159
  def __len__(self) -> int:
166
- raise ValueError(
167
- "len(<func>) is not supported for quantum callables - use <func>.len() instead (Only if it is an operand list)"
160
+ raise ClassiqValueError(
161
+ "len(<func>) is not supported for quantum callables - use <func>.len instead (Only if it is an operand list)"
168
162
  )
169
163
 
170
164
  if TYPE_CHECKING:
171
165
 
166
+ @property
172
167
  def len(self) -> int: ...
173
168
 
174
169
  else:
175
170
 
171
+ @property
176
172
  def len(self) -> QParamScalar:
177
173
  if not self.is_list:
178
174
  raise ClassiqValueError("Cannot get length of a non-list operand")
@@ -196,8 +192,8 @@ class QTerminalCallable(QCallable):
196
192
 
197
193
  @overload
198
194
  def prepare_arg(
199
- arg_decl: PositionalArg, val: Union[QCallable, Callable[[Any], None]]
200
- ) -> QuantumOperand: ...
195
+ arg_decl: PositionalArg, val: Union[QCallable, Callable[..., None]]
196
+ ) -> QuantumLambdaFunction: ...
201
197
 
202
198
 
203
199
  @overload
@@ -205,6 +201,9 @@ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
205
201
 
206
202
 
207
203
  def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
204
+ if isinstance(val, QConstant):
205
+ val.add_to_model()
206
+ return Expression(expr=str(val.name))
208
207
  if isinstance(arg_decl, ClassicalParameterDeclaration):
209
208
  return Expression(expr=str(val))
210
209
  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
@@ -32,6 +34,10 @@ def create_model(
32
34
  preferences: Optional[Preferences] = None,
33
35
  classical_execution_function: Optional[CFunc] = None,
34
36
  ) -> SerializedModel:
37
+ if entry_point.func_decl.name != "main":
38
+ raise ClassiqError(
39
+ f"The entry point function must be named 'main', got '{entry_point.func_decl.name}'"
40
+ )
35
41
  return entry_point.create_model(
36
42
  constraints, execution_preferences, preferences, classical_execution_function
37
43
  ).get_model()
@@ -63,21 +69,23 @@ class QFunc(QExpandable):
63
69
  ) -> Model:
64
70
  self._qmodule.type_decls = dict()
65
71
  self._qmodule.native_defs = dict()
72
+ self._qmodule.constants = dict()
73
+ QConstant.set_current_model(self._qmodule)
66
74
  self._add_native_func_def()
67
75
  model_extra_settings: List[Tuple[str, Any]] = [
68
76
  ("constraints", constraints),
69
77
  ("execution_preferences", execution_preferences),
70
78
  ("preferences", preferences),
71
79
  ]
72
- classical_execution_function = (
73
- CFunc.default_cmain()
74
- if classical_execution_function is None
75
- else classical_execution_function
76
- )
80
+ if classical_execution_function is not None:
81
+ self._add_constants_from_classical_code(classical_execution_function)
82
+ model_extra_settings.append(
83
+ ("classical_execution_code", classical_execution_function.code)
84
+ )
77
85
  return Model(
86
+ constants=list(self._qmodule.constants.values()),
78
87
  functions=list(self._qmodule.native_defs.values()),
79
88
  types=list(self._qmodule.type_decls.values()),
80
- classical_execution_code=classical_execution_function.code,
81
89
  **{key: value for key, value in model_extra_settings if value},
82
90
  )
83
91
 
@@ -89,18 +97,43 @@ class QFunc(QExpandable):
89
97
  **self.func_decl.__dict__, body=self.body
90
98
  )
91
99
 
100
+ def _add_constants_from_classical_code(
101
+ self, classical_execution_function: CFunc
102
+ ) -> None:
103
+ # FIXME: https://classiq.atlassian.net/browse/CAD-18050
104
+ # We use this visitor to add the constants that were used in the classical
105
+ # execution code to the model. In the future, if we will have a better notion
106
+ # of "QModule" and a "QConstant" will be a part of it then we may be able to
107
+ # remove the handling of the QConstants from this visitor, but I think we will
108
+ # need similar logic to allow using python variables in the classical execution
109
+ # code
110
+ class IdentifierVisitor(ast.NodeVisitor):
111
+ def visit_Name(self, node: ast.Name) -> None:
112
+ if (
113
+ node.id in classical_execution_function._caller_constants
114
+ and isinstance(
115
+ classical_execution_function._caller_constants[node.id],
116
+ QConstant,
117
+ )
118
+ ):
119
+ classical_execution_function._caller_constants[
120
+ node.id
121
+ ].add_to_model()
122
+
123
+ IdentifierVisitor().visit(ast.parse(classical_execution_function.code))
124
+
92
125
 
93
126
  class ExternalQFunc(QTerminalCallable):
94
127
  def __init__(self, py_callable: Callable) -> None:
95
128
  decl = _lookup_qfunc(unmangle_keyword(py_callable.__name__))
96
129
  if decl is None:
97
- raise ValueError(f"Definition of {py_callable.__name__!r} not found")
130
+ raise ClassiqError(f"Definition of {py_callable.__name__!r} not found")
98
131
 
99
132
  py_callable.__annotations__.pop("return", None)
100
133
  if py_callable.__annotations__.keys() != {
101
134
  mangle_keyword(arg.name) for arg in decl.get_positional_arg_decls()
102
135
  }:
103
- raise ValueError(
136
+ raise ClassiqError(
104
137
  f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
105
138
  )
106
139
  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 List, 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,8 +342,9 @@ __all__ = [
300
342
  "logical_and",
301
343
  "logical_or",
302
344
  "logical_not",
345
+ "mod_inverse",
303
346
  ]
304
347
 
305
348
 
306
- def __dir__():
349
+ def __dir__() -> List[str]:
307
350
  return __all__