classiq 0.92.0__py3-none-any.whl → 0.99.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 (318) hide show
  1. classiq/__init__.py +11 -19
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +7 -7
  3. classiq/_analyzer_extras/interactive_hardware.py +19 -12
  4. classiq/_internals/api_wrapper.py +31 -142
  5. classiq/_internals/async_utils.py +4 -7
  6. classiq/_internals/authentication/auth0.py +41 -15
  7. classiq/_internals/authentication/authorization_code.py +9 -0
  8. classiq/_internals/authentication/authorization_flow.py +41 -0
  9. classiq/_internals/authentication/device.py +33 -52
  10. classiq/_internals/authentication/hybrid_flow.py +19 -0
  11. classiq/_internals/authentication/password_manager.py +13 -13
  12. classiq/_internals/authentication/token_manager.py +9 -9
  13. classiq/_internals/client.py +17 -44
  14. classiq/_internals/config.py +19 -5
  15. classiq/_internals/help.py +1 -2
  16. classiq/_internals/host_checker.py +3 -3
  17. classiq/_internals/jobs.py +14 -14
  18. classiq/_internals/type_validation.py +3 -3
  19. classiq/analyzer/analyzer.py +18 -18
  20. classiq/analyzer/rb.py +17 -8
  21. classiq/analyzer/show_interactive_hack.py +1 -1
  22. classiq/applications/__init__.py +2 -2
  23. classiq/applications/chemistry/__init__.py +0 -30
  24. classiq/applications/chemistry/op_utils.py +4 -4
  25. classiq/applications/chemistry/problems.py +3 -3
  26. classiq/applications/chemistry/ucc.py +1 -2
  27. classiq/applications/chemistry/z2_symmetries.py +4 -4
  28. classiq/applications/combinatorial_helpers/allowed_constraints.py +1 -3
  29. classiq/applications/combinatorial_helpers/arithmetic/arithmetic_expression.py +2 -1
  30. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +2 -2
  31. classiq/applications/combinatorial_helpers/encoding_mapping.py +2 -3
  32. classiq/applications/combinatorial_helpers/encoding_utils.py +2 -2
  33. classiq/applications/combinatorial_helpers/optimization_model.py +3 -4
  34. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +2 -2
  35. classiq/applications/combinatorial_helpers/pyomo_utils.py +8 -8
  36. classiq/applications/combinatorial_helpers/sympy_utils.py +1 -3
  37. classiq/applications/combinatorial_helpers/transformations/encoding.py +3 -3
  38. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +1 -2
  39. classiq/applications/combinatorial_optimization/combinatorial_optimization_config.py +2 -3
  40. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  41. classiq/applications/combinatorial_optimization/combinatorial_problem.py +15 -10
  42. classiq/applications/hamiltonian/pauli_decomposition.py +6 -4
  43. classiq/applications/iqae/iqae.py +14 -11
  44. classiq/applications/qnn/datasets/dataset_base_classes.py +6 -6
  45. classiq/applications/qnn/datasets/dataset_parity.py +6 -6
  46. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  47. classiq/applications/qnn/qlayer.py +9 -8
  48. classiq/applications/qnn/torch_utils.py +5 -6
  49. classiq/applications/qnn/types.py +2 -1
  50. classiq/applications/qsp/__init__.py +20 -2
  51. classiq/applications/qsp/qsp.py +239 -11
  52. classiq/applications/qsvm/qsvm_data_generation.py +1 -2
  53. classiq/evaluators/classical_expression.py +0 -4
  54. classiq/evaluators/parameter_types.py +20 -12
  55. classiq/evaluators/qmod_annotated_expression.py +31 -26
  56. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +14 -14
  57. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +2 -1
  58. classiq/evaluators/qmod_expression_visitors/sympy_wrappers.py +8 -8
  59. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +4 -4
  60. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +14 -4
  61. classiq/evaluators/qmod_node_evaluators/list_evaluation.py +2 -2
  62. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +3 -3
  63. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +9 -9
  64. classiq/evaluators/qmod_node_evaluators/utils.py +6 -6
  65. classiq/evaluators/qmod_type_inference/classical_type_inference.py +9 -10
  66. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +5 -5
  67. classiq/execution/__init__.py +0 -3
  68. classiq/execution/execution_session.py +28 -21
  69. classiq/execution/jobs.py +26 -26
  70. classiq/execution/qnn.py +1 -2
  71. classiq/execution/user_budgets.py +71 -37
  72. classiq/executor.py +1 -3
  73. classiq/interface/_version.py +1 -1
  74. classiq/interface/analyzer/analysis_params.py +4 -4
  75. classiq/interface/analyzer/cytoscape_graph.py +3 -3
  76. classiq/interface/analyzer/result.py +4 -4
  77. classiq/interface/ast_node.py +3 -3
  78. classiq/interface/backend/backend_preferences.py +26 -50
  79. classiq/interface/backend/ionq/ionq_quantum_program.py +5 -5
  80. classiq/interface/backend/provider_config/__init__.py +0 -0
  81. classiq/interface/backend/provider_config/provider_config.py +8 -0
  82. classiq/interface/backend/provider_config/providers/__init__.py +0 -0
  83. classiq/interface/backend/provider_config/providers/alice_bob.py +47 -0
  84. classiq/interface/backend/provider_config/providers/aqt.py +16 -0
  85. classiq/interface/backend/provider_config/providers/azure.py +37 -0
  86. classiq/interface/backend/provider_config/providers/braket.py +39 -0
  87. classiq/interface/backend/provider_config/providers/ibm.py +26 -0
  88. classiq/interface/backend/provider_config/providers/ionq.py +22 -0
  89. classiq/interface/backend/quantum_backend_providers.py +20 -2
  90. classiq/interface/chemistry/ansatz_library.py +3 -5
  91. classiq/interface/chemistry/operator.py +3 -3
  92. classiq/interface/combinatorial_optimization/examples/knapsack.py +2 -4
  93. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +1 -2
  94. classiq/interface/compression_utils.py +2 -3
  95. classiq/interface/debug_info/debug_info.py +8 -7
  96. classiq/interface/exceptions.py +10 -7
  97. classiq/interface/execution/primitives.py +6 -6
  98. classiq/interface/executor/estimate_cost.py +1 -1
  99. classiq/interface/executor/execution_preferences.py +3 -5
  100. classiq/interface/executor/execution_request.py +10 -10
  101. classiq/interface/executor/execution_result.py +1 -2
  102. classiq/interface/executor/quantum_code.py +8 -8
  103. classiq/interface/executor/result.py +28 -18
  104. classiq/interface/executor/user_budget.py +25 -17
  105. classiq/interface/executor/vqe_result.py +5 -6
  106. classiq/interface/generator/ansatz_library.py +6 -8
  107. classiq/interface/generator/application_apis/__init__.py +0 -3
  108. classiq/interface/generator/arith/arithmetic.py +2 -2
  109. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +2 -3
  110. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -5
  111. classiq/interface/generator/arith/arithmetic_expression_parser.py +11 -4
  112. classiq/interface/generator/arith/arithmetic_expression_validator.py +12 -15
  113. classiq/interface/generator/arith/arithmetic_operations.py +4 -6
  114. classiq/interface/generator/arith/arithmetic_param_getters.py +70 -107
  115. classiq/interface/generator/arith/arithmetic_result_builder.py +4 -4
  116. classiq/interface/generator/arith/ast_node_rewrite.py +8 -4
  117. classiq/interface/generator/arith/binary_ops.py +15 -40
  118. classiq/interface/generator/arith/logical_ops.py +2 -3
  119. classiq/interface/generator/arith/number_utils.py +2 -2
  120. classiq/interface/generator/arith/register_user_input.py +3 -3
  121. classiq/interface/generator/arith/unary_ops.py +2 -2
  122. classiq/interface/generator/circuit_code/circuit_code.py +8 -10
  123. classiq/interface/generator/circuit_code/types_and_constants.py +1 -1
  124. classiq/interface/generator/complex_type.py +2 -2
  125. classiq/interface/generator/copy.py +1 -3
  126. classiq/interface/generator/expressions/atomic_expression_functions.py +0 -5
  127. classiq/interface/generator/expressions/evaluated_expression.py +2 -3
  128. classiq/interface/generator/expressions/expression.py +2 -2
  129. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -7
  130. classiq/interface/generator/function_param_list.py +0 -40
  131. classiq/interface/generator/function_params.py +5 -6
  132. classiq/interface/generator/functions/classical_function_declaration.py +2 -2
  133. classiq/interface/generator/functions/classical_type.py +3 -3
  134. classiq/interface/generator/functions/type_modifier.py +0 -15
  135. classiq/interface/generator/functions/type_name.py +2 -2
  136. classiq/interface/generator/generated_circuit_data.py +14 -18
  137. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +2 -4
  138. classiq/interface/generator/hardware/hardware_data.py +8 -8
  139. classiq/interface/generator/hardware_efficient_ansatz.py +9 -9
  140. classiq/interface/generator/mcu.py +3 -3
  141. classiq/interface/generator/mcx.py +3 -3
  142. classiq/interface/generator/model/constraints.py +34 -5
  143. classiq/interface/generator/model/preferences/preferences.py +15 -21
  144. classiq/interface/generator/model/quantum_register.py +7 -10
  145. classiq/interface/generator/noise_properties.py +3 -7
  146. classiq/interface/generator/parameters.py +1 -1
  147. classiq/interface/generator/partitioned_register.py +1 -2
  148. classiq/interface/generator/preferences/qasm_to_qmod_params.py +11 -0
  149. classiq/interface/generator/quantum_function_call.py +9 -12
  150. classiq/interface/generator/quantum_program.py +10 -23
  151. classiq/interface/generator/range_types.py +3 -3
  152. classiq/interface/generator/slice_parsing_utils.py +4 -5
  153. classiq/interface/generator/standard_gates/standard_gates.py +2 -4
  154. classiq/interface/generator/synthesis_execution_parameter.py +1 -3
  155. classiq/interface/generator/synthesis_metadata/synthesis_duration.py +9 -0
  156. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +2 -3
  157. classiq/interface/generator/transpiler_basis_gates.py +12 -4
  158. classiq/interface/generator/types/builtin_enum_declarations.py +0 -145
  159. classiq/interface/generator/types/compilation_metadata.py +12 -1
  160. classiq/interface/generator/types/enum_declaration.py +2 -1
  161. classiq/interface/generator/validations/flow_graph.py +3 -3
  162. classiq/interface/generator/visitor.py +10 -12
  163. classiq/interface/hardware.py +2 -3
  164. classiq/interface/helpers/classproperty.py +2 -2
  165. classiq/interface/helpers/custom_encoders.py +2 -1
  166. classiq/interface/helpers/custom_pydantic_types.py +1 -1
  167. classiq/interface/helpers/text_utils.py +1 -4
  168. classiq/interface/ide/visual_model.py +6 -5
  169. classiq/interface/interface_version.py +1 -1
  170. classiq/interface/jobs.py +3 -3
  171. classiq/interface/model/allocate.py +4 -4
  172. classiq/interface/model/block.py +6 -2
  173. classiq/interface/model/bounds.py +3 -3
  174. classiq/interface/model/classical_if.py +4 -0
  175. classiq/interface/model/control.py +8 -1
  176. classiq/interface/model/inplace_binary_operation.py +2 -2
  177. classiq/interface/model/invert.py +4 -0
  178. classiq/interface/model/model.py +4 -4
  179. classiq/interface/model/model_visitor.py +40 -1
  180. classiq/interface/model/parameter.py +1 -3
  181. classiq/interface/model/port_declaration.py +1 -1
  182. classiq/interface/model/power.py +4 -0
  183. classiq/interface/model/quantum_expressions/quantum_expression.py +1 -2
  184. classiq/interface/model/quantum_function_call.py +3 -6
  185. classiq/interface/model/quantum_function_declaration.py +1 -0
  186. classiq/interface/model/quantum_lambda_function.py +4 -4
  187. classiq/interface/model/quantum_statement.py +11 -4
  188. classiq/interface/model/quantum_type.py +14 -14
  189. classiq/interface/model/repeat.py +4 -0
  190. classiq/interface/model/skip_control.py +4 -0
  191. classiq/interface/model/validation_handle.py +2 -3
  192. classiq/interface/model/variable_declaration_statement.py +2 -2
  193. classiq/interface/model/within_apply_operation.py +4 -0
  194. classiq/interface/pretty_print/expression_to_qmod.py +3 -4
  195. classiq/interface/server/routes.py +0 -16
  196. classiq/interface/source_reference.py +3 -4
  197. classiq/model_expansions/arithmetic.py +11 -7
  198. classiq/model_expansions/arithmetic_compute_result_attrs.py +30 -27
  199. classiq/model_expansions/capturing/captured_vars.py +3 -3
  200. classiq/model_expansions/capturing/mangling_utils.py +1 -2
  201. classiq/model_expansions/closure.py +12 -11
  202. classiq/model_expansions/function_builder.py +14 -6
  203. classiq/model_expansions/generative_functions.py +7 -12
  204. classiq/model_expansions/interpreters/base_interpreter.py +3 -7
  205. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +2 -1
  206. classiq/model_expansions/interpreters/generative_interpreter.py +8 -4
  207. classiq/model_expansions/quantum_operations/allocate.py +4 -4
  208. classiq/model_expansions/quantum_operations/assignment_result_processor.py +8 -4
  209. classiq/model_expansions/quantum_operations/call_emitter.py +31 -37
  210. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +2 -2
  211. classiq/model_expansions/quantum_operations/emitter.py +3 -5
  212. classiq/model_expansions/quantum_operations/expression_evaluator.py +3 -3
  213. classiq/model_expansions/quantum_operations/skip_control_verifier.py +1 -2
  214. classiq/model_expansions/quantum_operations/variable_decleration.py +61 -29
  215. classiq/model_expansions/scope.py +7 -7
  216. classiq/model_expansions/scope_initialization.py +4 -0
  217. classiq/model_expansions/visitors/symbolic_param_inference.py +6 -6
  218. classiq/model_expansions/visitors/uncomputation_signature_inference.py +328 -0
  219. classiq/model_expansions/visitors/variable_references.py +15 -14
  220. classiq/open_library/functions/__init__.py +28 -11
  221. classiq/open_library/functions/amplitude_loading.py +81 -0
  222. classiq/open_library/functions/discrete_sine_cosine_transform.py +19 -14
  223. classiq/open_library/functions/grover.py +8 -10
  224. classiq/open_library/functions/lcu.py +47 -18
  225. classiq/open_library/functions/modular_exponentiation.py +93 -8
  226. classiq/open_library/functions/qsvt.py +66 -79
  227. classiq/open_library/functions/qsvt_temp.py +536 -0
  228. classiq/open_library/functions/state_preparation.py +130 -27
  229. classiq/qmod/__init__.py +6 -4
  230. classiq/qmod/builtins/classical_execution_primitives.py +4 -23
  231. classiq/qmod/builtins/classical_functions.py +1 -42
  232. classiq/qmod/builtins/enums.py +15 -153
  233. classiq/qmod/builtins/functions/__init__.py +9 -18
  234. classiq/qmod/builtins/functions/allocation.py +25 -4
  235. classiq/qmod/builtins/functions/arithmetic.py +22 -27
  236. classiq/qmod/builtins/functions/exponentiation.py +51 -2
  237. classiq/qmod/builtins/functions/mcx_func.py +7 -0
  238. classiq/qmod/builtins/functions/standard_gates.py +46 -27
  239. classiq/qmod/builtins/operations.py +173 -79
  240. classiq/qmod/builtins/structs.py +24 -91
  241. classiq/qmod/cfunc.py +3 -2
  242. classiq/qmod/classical_function.py +2 -1
  243. classiq/qmod/classical_variable.py +4 -2
  244. classiq/qmod/cparam.py +2 -8
  245. classiq/qmod/create_model_function.py +7 -7
  246. classiq/qmod/declaration_inferrer.py +33 -30
  247. classiq/qmod/expression_query.py +7 -4
  248. classiq/qmod/model_state_container.py +2 -2
  249. classiq/qmod/native/pretty_printer.py +25 -14
  250. classiq/qmod/pretty_print/expression_to_python.py +5 -3
  251. classiq/qmod/pretty_print/pretty_printer.py +39 -17
  252. classiq/qmod/python_classical_type.py +40 -13
  253. classiq/qmod/qfunc.py +124 -19
  254. classiq/qmod/qmod_constant.py +2 -2
  255. classiq/qmod/qmod_parameter.py +5 -2
  256. classiq/qmod/qmod_variable.py +47 -46
  257. classiq/qmod/quantum_callable.py +18 -13
  258. classiq/qmod/quantum_expandable.py +33 -26
  259. classiq/qmod/quantum_function.py +84 -36
  260. classiq/qmod/semantics/annotation/call_annotation.py +5 -5
  261. classiq/qmod/semantics/error_manager.py +12 -14
  262. classiq/qmod/semantics/lambdas.py +1 -2
  263. classiq/qmod/semantics/validation/types_validation.py +1 -2
  264. classiq/qmod/symbolic.py +2 -4
  265. classiq/qmod/symbolic_expr.py +12 -4
  266. classiq/qmod/utilities.py +13 -20
  267. classiq/qmod/write_qmod.py +3 -4
  268. classiq/quantum_program.py +1 -3
  269. classiq/synthesis.py +11 -7
  270. {classiq-0.92.0.dist-info → classiq-0.99.0.dist-info}/METADATA +38 -37
  271. {classiq-0.92.0.dist-info → classiq-0.99.0.dist-info}/RECORD +273 -300
  272. classiq-0.99.0.dist-info/WHEEL +4 -0
  273. classiq-0.99.0.dist-info/licenses/LICENSE.txt +27 -0
  274. classiq/applications/chemistry/ansatz_parameters.py +0 -29
  275. classiq/applications/chemistry/chemistry_execution_parameters.py +0 -16
  276. classiq/applications/chemistry/chemistry_model_constructor.py +0 -532
  277. classiq/applications/chemistry/ground_state_problem.py +0 -42
  278. classiq/applications/qsvm/__init__.py +0 -8
  279. classiq/applications/qsvm/qsvm.py +0 -11
  280. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -129
  281. classiq/execution/iqcc.py +0 -128
  282. classiq/interface/applications/qsvm.py +0 -117
  283. classiq/interface/chemistry/elements.py +0 -120
  284. classiq/interface/chemistry/fermionic_operator.py +0 -208
  285. classiq/interface/chemistry/ground_state_problem.py +0 -132
  286. classiq/interface/chemistry/ground_state_result.py +0 -8
  287. classiq/interface/chemistry/molecule.py +0 -71
  288. classiq/interface/execution/iqcc.py +0 -44
  289. classiq/interface/generator/application_apis/chemistry_declarations.py +0 -69
  290. classiq/interface/generator/application_apis/entangler_declarations.py +0 -29
  291. classiq/interface/generator/application_apis/qsvm_declarations.py +0 -6
  292. classiq/interface/generator/chemistry_function_params.py +0 -50
  293. classiq/interface/generator/entangler_params.py +0 -72
  294. classiq/interface/generator/entanglers.py +0 -14
  295. classiq/interface/generator/hamiltonian_evolution/qdrift.py +0 -27
  296. classiq/interface/generator/hartree_fock.py +0 -26
  297. classiq/interface/generator/hva.py +0 -22
  298. classiq/interface/generator/linear_pauli_rotations.py +0 -92
  299. classiq/interface/generator/qft.py +0 -37
  300. classiq/interface/generator/qsvm.py +0 -96
  301. classiq/interface/generator/state_preparation/__init__.py +0 -14
  302. classiq/interface/generator/state_preparation/bell_state_preparation.py +0 -27
  303. classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +0 -28
  304. classiq/interface/generator/state_preparation/distributions.py +0 -53
  305. classiq/interface/generator/state_preparation/exponential_state_preparation.py +0 -14
  306. classiq/interface/generator/state_preparation/ghz_state_preparation.py +0 -14
  307. classiq/interface/generator/state_preparation/metrics.py +0 -41
  308. classiq/interface/generator/state_preparation/state_preparation.py +0 -113
  309. classiq/interface/generator/state_preparation/state_preparation_abc.py +0 -24
  310. classiq/interface/generator/state_preparation/uniform_distibution_state_preparation.py +0 -13
  311. classiq/interface/generator/state_preparation/w_state_preparation.py +0 -13
  312. classiq/interface/generator/ucc.py +0 -74
  313. classiq/interface/helpers/backward_compatibility.py +0 -9
  314. classiq/model_expansions/transformers/type_modifier_inference.py +0 -392
  315. classiq/open_library/functions/lookup_table.py +0 -58
  316. classiq/qmod/builtins/functions/chemistry.py +0 -123
  317. classiq/qmod/builtins/functions/qsvm.py +0 -24
  318. classiq-0.92.0.dist-info/WHEEL +0 -4
@@ -1,10 +1,9 @@
1
1
  import builtins
2
2
  import importlib.util
3
+ from collections.abc import Callable
3
4
  from functools import wraps
4
- from typing import Callable, Optional
5
5
 
6
6
  import numpy as np
7
- from matplotlib import pyplot as plt
8
7
  from numpy.polynomial import Chebyshev, Polynomial
9
8
  from numpy.polynomial.chebyshev import cheb2poly, poly2cheb
10
9
 
@@ -42,7 +41,7 @@ def _pyqsp_get_phases(
42
41
 
43
42
  parity = (len(poly_coeffs) + 1) % 2
44
43
  reduced_coefs = poly_coeffs[parity::2]
45
- phases, err, tot_iter, opt = newton_solver(reduced_coefs, parity, crit=tol)
44
+ _phases, err, _tot_iter, opt = newton_solver(reduced_coefs, parity, crit=tol)
46
45
  return opt.full_phases, err
47
46
 
48
47
 
@@ -51,7 +50,7 @@ def qsvt_phases(
51
50
  ) -> np.ndarray:
52
51
  r"""
53
52
  Get QSVT phases that will generate the given Chebyshev polynomial.
54
- The phases are ready to be used in `qsvt` and `qsvt_lcu` functions in the classiq library. The convetion
53
+ The phases are ready to be used in `qsvt` and `qsvt_lcu` functions in the classiq library. The convention
55
54
  is the reflection signal operator, and the measurement basis is the hadamard basis (see https://arxiv.org/abs/2105.02859
56
55
  APPENDIX A.).
57
56
  The current implementation is using the pyqsp package, based on techniques in https://arxiv.org/abs/2003.02831.
@@ -119,13 +118,15 @@ def qsvt_phases(
119
118
 
120
119
  def _plot_qsp_approx(
121
120
  poly_cheb: np.ndarray,
122
- f_target: Callable[[np.ndarray], np.ndarray],
121
+ f_target: Callable[[float], complex],
123
122
  interval: tuple[float, float] = (-1, 1),
124
123
  ) -> None:
124
+ from matplotlib import pyplot as plt
125
+
125
126
  grid_full = np.linspace(-1, 1, 3000)
126
127
  grid_interval = np.linspace(interval[0], interval[1], 3000)
127
128
 
128
- y_target = f_target(grid_interval)
129
+ y_target = np.vectorize(f_target, otypes=[float])(grid_interval)
129
130
  y_approx = np.polynomial.Chebyshev(poly_cheb)(grid_full)
130
131
 
131
132
  # Plot
@@ -152,12 +153,12 @@ def _plot_qsp_approx(
152
153
 
153
154
 
154
155
  def qsp_approximate(
155
- f_target: Callable[[np.ndarray], np.ndarray],
156
+ f_target: Callable[[float], complex],
156
157
  degree: int,
157
- parity: Optional[int] = None,
158
+ parity: int | None = None,
158
159
  interval: tuple[float, float] = (-1, 1),
159
160
  bound: float = 0.99,
160
- num_grid_points: Optional[int] = None,
161
+ num_grid_points: int | None = None,
161
162
  plot: bool = False,
162
163
  ) -> tuple[np.ndarray, float]:
163
164
  """
@@ -192,7 +193,8 @@ def qsp_approximate(
192
193
  # Select grid points for the objective in [w_min, w_max]
193
194
  xj_obj = xj_full[(xj_full >= interval[0]) & (xj_full <= interval[1])]
194
195
 
195
- yj_obj = f_target(xj_obj)
196
+ yj_obj = np.vectorize(f_target, otypes=[float])(xj_obj)
197
+
196
198
  # heuristic verification
197
199
  bound = min(1, bound)
198
200
  assert (
@@ -313,7 +315,7 @@ def gqsp_phases(
313
315
  - Laurent polynomials are supported by degree shifting. If
314
316
  P(z) = sum_{k=m}^n c_k * z^k with m < 0, the phases correspond to the
315
317
  degree-shifted polynomial z^{-m} * P(z) (so the minimal degree is zero).
316
- - The phase finiding works in the monomial basis. If the a Chebyshev basis polynomial is provided,
318
+ - The phase finiding works in the monomial basis. If a Chebyshev basis polynomial is provided,
317
319
  it will be converted to the monomial basis (and introduce an additional overhead).
318
320
 
319
321
  Args:
@@ -364,3 +366,229 @@ def gqsp_phases(
364
366
  s = np.array([s[0][1 : i + 1], s[1][:i]])
365
367
 
366
368
  return thetas, phis, lambdas
369
+
370
+
371
+ def poly_jacobi_anger_cos(degree: int, t: float) -> np.ndarray:
372
+ r"""
373
+ Gets the Chebyshev polynomial coefficients approximating cos(t*x) using the Jacobi-Anger expansion.
374
+ $$\cos(xt) = J_0(t) + 2\sum_{k=1}^{d/2} (-1)^k J_{2k}(t)\, T_{2k}(x) $$
375
+
376
+ Args:
377
+ degree: the degree of the approximating polynomial.
378
+ t: the parameter in cos(t*x). Can be negative.
379
+ Returns:
380
+ coeffs: The Chebyshev approximating coefficients.
381
+ """
382
+ from scipy.special import jv
383
+
384
+ coeffs = np.zeros(degree + 1 if degree % 2 == 0 else degree)
385
+ for k in range(degree // 2 + 1):
386
+ coeffs[2 * k] = (2 if k > 0 else 1) * (-1) ** k * jv(2 * k, t)
387
+
388
+ return coeffs
389
+
390
+
391
+ def poly_jacobi_anger_sin(degree: int, t: float) -> np.ndarray:
392
+ r"""
393
+ Gets the Chebyshev polynomial coefficients approximating sin(t*x) using the Jacobi-Anger expansion.
394
+ $$\sin(xt) = 2\sum_{k=0}^{d/2} (-1)^k J_{2k+1}(t)\, T_{2k+1}(x)$$
395
+
396
+ Args:
397
+ degree: the degree of the approximating polynomial.
398
+ t: the parameter in sin(t*x).
399
+ Returns:
400
+ coeffs: The Chebyshev approximating coefficients.
401
+ """
402
+ from scipy.special import jv
403
+
404
+ coeffs = np.zeros(degree + 1 if degree % 2 == 1 else degree)
405
+ for k in range(len(coeffs) // 2):
406
+ idx = 2 * k + 1
407
+ coeffs[idx] = 2 * (-1) ** k * jv(idx, t)
408
+ return coeffs
409
+
410
+
411
+ def poly_jacobi_anger_exp_sin(degree: int, t: float) -> np.ndarray:
412
+ r"""
413
+ Gets the Chebyshev polynomial coefficients approximating exp(i*t*sin(x)) using the Jacobi-Anger expansion:
414
+ $$e^{it\sin(x)} = \sum_{k=-d}^{d} J_{k}(t) e^{ikx}$$
415
+
416
+ Args:
417
+ degree: the maximum degree of the approximating polynomial (negative and positive).
418
+ t: the parameter in exp(i*t*sin(x)).
419
+ Returns:
420
+ coeffs: The approximating coefficients in monomial basis, starting from the negative ones to the positive ones (length is 2*degree+1).
421
+ """
422
+ from scipy.special import jv
423
+
424
+ coeffs = [jv(k, t) for k in range(-degree, degree + 1)]
425
+ return np.array(coeffs)
426
+
427
+
428
+ def poly_jacobi_anger_exp_cos(degree: int, t: float) -> np.ndarray:
429
+ r"""
430
+ Gets the Chebyshev polynomial coefficients approximating exp(i*t*cos(x)) using the Jacobi-Anger expansion:
431
+ $$e^{it\cos(x)} = \sum_{k=-d}^{d} i^k J_{k}(t) e^{ikx}$$
432
+
433
+ Args:
434
+ degree: the maximum degree of the approximating polynomial (negative and positive).
435
+ t: the parameter in exp(i*t*cos(x)).
436
+ Returns:
437
+ coeffs: The approximating coefficients in monomial basis, starting from the negative ones to the positive ones (length is 2*degree+1).
438
+ """
439
+ from scipy.special import jv
440
+
441
+ coeffs = [jv(k, t) * (1j**k) for k in range(-degree, degree + 1)]
442
+ return np.array(coeffs)
443
+
444
+
445
+ def poly_jacobi_anger_degree(eps: float, t: float) -> int:
446
+ r"""
447
+ Get needed degree based on the maximum allowed uniform error on the interval [-1, 1]
448
+ for the Jacobi-Anger approximation. relevant for all poly_*_jacobi_anger functions.
449
+ Args:
450
+ eps: maximum allowed uniform error
451
+ t: the parameter in exp(i*t*cos(x))\exp(i*t*sin(x))\cos(t*x)\sin(t*x)
452
+ Returns:
453
+ degree: the minimal degree such that the polynomial approximation
454
+ using Jacobi-Anger expansion is within eps uniform error. relevant
455
+ """
456
+ # based on the approximation in https://arxiv.org/pdf/1806.01838 eq. (56)
457
+ from scipy.optimize import fsolve
458
+
459
+ deg = np.ceil(
460
+ fsolve(lambda r: (np.e * np.abs(t) / (2 * r)) ** r - 4 / 5 * eps, t)[0]
461
+ )
462
+ return int(max(deg, 1) + 1)
463
+
464
+
465
+ def poly_jacobi_anger_error(degree: int, t: float) -> float:
466
+ r"""
467
+ Get the uniform error on the interval [-1, 1] for the Jacobi-Anger approximation,
468
+ based on the truncation degree. Relevant for all poly_*_jacobi_anger functions.
469
+ Args:
470
+ degree: truncation maximal degree
471
+ t: the parameter in exp(i*t*cos(x))\exp(i*t*sin(x))\cos(t*x)\sin(t*x)
472
+ Returns:
473
+ eps: a bound on the approximation uniform error
474
+ """
475
+ # based on the approximation in https://arxiv.org/pdf/1806.01838 eq. (56)
476
+ eps = 5 / 4 * (np.e * np.abs(t) / (2 * degree)) ** degree
477
+ return eps
478
+
479
+
480
+ def poly_inversion(degree: int, kappa: float) -> tuple[np.ndarray, float]:
481
+ """
482
+ Gets the Chebyshev odd polynomial coefficients approximating 1/x on [1/kappa, 1] and bounded by 1 in magnitude on [-1, 1].
483
+ Based on the paper https://arxiv.org/pdf/2507.15537.
484
+
485
+ Note: To be used within the QSVT framework, the polynomial should be scaled to be bounded by 1 on [-1, 1].
486
+ The maximum absolute value of the returned polynomial on [-1, 1] can be larger than kappa, so the user
487
+ should use the returned max_value to scale down the polynomial accordingly.
488
+
489
+ Args:
490
+ degree: The degree of the approximating polynomial.
491
+ kappa: The number defining the interval [1/kappa, 1], usually represents the condition number in the setting of matrix inversion.
492
+
493
+ Returns:
494
+ coeffs: The Chebyshev polynomial coefficients approximating 1/x on [1/kappa, 1]
495
+ using the optimal polynomial of given degree.
496
+ max_value: An upper bound on the maximum absolute value of the polynomial on [-1, 1]. The value
497
+ can be used to scale down the polynomial for the usage within QSVT.
498
+ """
499
+
500
+ def _evaluate_l_n(x: np.ndarray, n: int, a: float) -> np.ndarray:
501
+ """
502
+ L_n(x; a) = 2^{-(n-1)} [ T_n(x) + ((1-a)/(1+a)) T_{n-1}(x)]. (eq. 6)
503
+ """
504
+ x = np.asarray(x, dtype=float)
505
+ assert n >= 1, "n must be >= 1"
506
+ assert 0.0 < a < 1.0, "1/kappa must be in (0, 1)"
507
+
508
+ alpha = (1 + a) / (2 * (1 - a))
509
+ l1 = (x + (1 - a) / (1 + a)) / alpha
510
+ l2 = (x**2 + (1 - a) / (1 + a) * x / 2 - 1 / 2) / alpha**2
511
+ if n == 1:
512
+ return l1
513
+ for _ in range(3, n + 1):
514
+ l1, l2 = l2, x * l2 / alpha - l1 / (4 * alpha**2)
515
+ return l2
516
+
517
+ def _substitute_x(x: np.ndarray, a: float) -> np.ndarray:
518
+ """
519
+ helper function for the variable substitution in eq. (5).
520
+ """
521
+ return (2.0 * x * x - (1.0 + a * a)) / (1.0 - a * a)
522
+
523
+ def _evaluate_p(x: np.ndarray, d: int, a: float) -> np.ndarray:
524
+ """
525
+ Evaluate P_{2n-1}(x; a) with d odd, n=(d+1)//2.
526
+ Uses Eq. (5) with L_n from Eq. (6).
527
+ """
528
+ n = (d + 1) // 2
529
+ x = np.asarray(x, dtype=float)
530
+
531
+ # main branch
532
+ l_n = _evaluate_l_n(_substitute_x(x, a), n, a)
533
+ l_0 = _evaluate_l_n(_substitute_x(0.0, a), n, a) # type: ignore[arg-type]
534
+ return (1.0 - (l_n / l_0)) / x
535
+
536
+ a = 1 / kappa
537
+ assert degree % 2 == 1, "degree must be odd"
538
+
539
+ coeffs = np.polynomial.chebyshev.chebinterpolate(
540
+ _evaluate_p, degree, args=(degree, a)
541
+ )
542
+ # enforce odd parity numerically:
543
+ coeffs[0::2] = 0.0
544
+
545
+ # calculate maximum value on [-1, 1]:
546
+ sampling_ratio = 25
547
+ grid = np.linspace(0, 1, degree * 25)
548
+ max_sampled = np.max(np.abs(np.polynomial.chebyshev.Chebyshev(coeffs)(grid)))
549
+ max_value = (
550
+ max_sampled * 1 / (np.cos(np.pi / (4 * sampling_ratio)))
551
+ ) # according to (25) in the paper
552
+
553
+ return coeffs, max_value
554
+
555
+
556
+ def poly_inversion_degree(eps: float, kappa: float) -> int:
557
+ """
558
+ Get the needed degree based on the target uniform error on the interval [1/kappa, 1],
559
+ for the polynomial approximation of 1/x using the function poly_inversion.
560
+ Based on the paper https://arxiv.org/pdf/2507.15537.
561
+
562
+ Args:
563
+ eps: The target uniform approximation error.
564
+ kappa: The number defining the interval [1/kappa, 1], usually represents the condition number in the setting of matrix inversion.
565
+ Returns:
566
+ degree: The minimal degree such that the polynomial approximation of 1/x on [1/kappa, 1]
567
+ is within eps uniform error.
568
+ """
569
+ a = 1 / kappa
570
+ n = int(
571
+ np.ceil(
572
+ (np.log(1 / eps) + np.log(1 / a) + np.log(1 + a))
573
+ / np.log((1 + a) / (1 - a))
574
+ )
575
+ )
576
+ return 2 * n - 1
577
+
578
+
579
+ def poly_inversion_error(degree: int, kappa: float) -> float:
580
+ """
581
+ Calculates uniform error on the interval [1/kappa, 1] for the polynomial
582
+ approximation of 1/x given by poly_inversion.
583
+ Based on the paper https://arxiv.org/pdf/2507.15537
584
+
585
+ Args:
586
+ degree: Inversion polynomial degree.
587
+ kappa: The number defining the interval [1/kappa, 1], usually represents the condition number in the setting of matrix inversion.
588
+
589
+ Returns:
590
+ eps: Uniform approximation error on [1/kappa, 1].
591
+ """
592
+ a = 1 / kappa
593
+ n = (degree + 1) // 2
594
+ return (1 - a) ** n / (a * (1 + a) ** (n - 1))
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  from collections import defaultdict
3
- from typing import Optional
4
3
 
5
4
  import numpy as np
6
5
 
@@ -14,7 +13,7 @@ def generate_data(
14
13
  range_min: float = 0,
15
14
  range_max: float = 2 * np.pi,
16
15
  std: float = 0.5,
17
- sources: Optional[np.ndarray] = None,
16
+ sources: np.ndarray | None = None,
18
17
  ) -> dict[int, np.ndarray]:
19
18
  if sources is None:
20
19
  sources = np.random.uniform(
@@ -17,9 +17,6 @@ from classiq.interface.generator.expressions.proxies.classical.utils import (
17
17
  from classiq.interface.generator.functions.classical_type import ClassicalArray, Integer
18
18
 
19
19
  from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
20
- from classiq.evaluators.qmod_expression_visitors.qmod_expression_bwc import (
21
- QmodExpressionBwc,
22
- )
23
20
  from classiq.evaluators.qmod_expression_visitors.qmod_expression_evaluator import (
24
21
  evaluate_qmod_expression,
25
22
  )
@@ -59,7 +56,6 @@ def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
59
56
  if expr.is_evaluated():
60
57
  return Evaluated(value=expr.value.value)
61
58
  expr_ast = ast.parse(expr.expr)
62
- expr_ast = QmodExpressionBwc().visit(expr_ast)
63
59
  expr_val = evaluate_qmod_expression(
64
60
  ast.unparse(expr_ast),
65
61
  classical_struct_declarations=list(QMODULE.type_decls.values()),
@@ -25,7 +25,6 @@ from classiq.interface.generator.functions.port_declaration import (
25
25
  from classiq.interface.generator.functions.type_name import (
26
26
  TypeName,
27
27
  )
28
- from classiq.interface.helpers.backward_compatibility import zip_strict
29
28
  from classiq.interface.model.classical_parameter_declaration import (
30
29
  ClassicalParameterDeclaration,
31
30
  )
@@ -80,7 +79,7 @@ def evaluate_parameter_types_from_args(
80
79
  f"{len(arguments)} were given"
81
80
  )
82
81
 
83
- for parameter, argument in zip_strict(parameters, arguments, strict=True):
82
+ for parameter, argument in zip(parameters, arguments, strict=True):
84
83
  if isinstance(parameter, ClassicalParameterDeclaration):
85
84
  arg_val = argument.value
86
85
  if not isinstance(arg_val, QmodAnnotatedExpression):
@@ -88,11 +87,11 @@ def evaluate_parameter_types_from_args(
88
87
 
89
88
  evaluated_params = [
90
89
  _evaluate_type_from_arg(parameter, argument, closure)
91
- for parameter, argument in zip_strict(parameters, arguments, strict=True)
90
+ for parameter, argument in zip(parameters, arguments, strict=True)
92
91
  ]
93
92
 
94
93
  parameter_names = {parameter.name for parameter in parameters}
95
- for parameter, argument in zip_strict(parameters, arguments, strict=True):
94
+ for parameter, argument in zip(parameters, arguments, strict=True):
96
95
  if isinstance(parameter, QuantumOperandDeclaration):
97
96
  _update_operand_signature_environment(
98
97
  argument.value, parameter_names, closure
@@ -134,6 +133,9 @@ def _evaluate_type_from_arg(
134
133
  # FIXME: Remove suzuki_trotter overloading (CLS-2912)
135
134
  if closure.name == "suzuki_trotter" and parameter.name == "pauli_operator":
136
135
  return parameter
136
+ # FIXME: Remove qdrift overloading (CLS-4347)
137
+ if closure.name == "qdrift" and parameter.name == "pauli_operator":
138
+ return parameter
137
139
  if isinstance(parameter, ClassicalParameterDeclaration):
138
140
  return _evaluate_classical_type_from_arg(parameter, argument, closure)
139
141
  if isinstance(parameter, PortDeclaration):
@@ -271,9 +273,9 @@ def _evaluate_op_type_from_arg(
271
273
 
272
274
  def _raise_argument_type_error(
273
275
  arg_val: Any,
274
- arg_type: Union[QmodType, AnonQuantumOperandDeclaration],
276
+ arg_type: QmodType | AnonQuantumOperandDeclaration,
275
277
  param_name: str,
276
- param_type: Union[QmodType, AnonQuantumOperandDeclaration],
278
+ param_type: QmodType | AnonQuantumOperandDeclaration,
277
279
  ) -> NoReturn:
278
280
  raise ClassiqExpansionError(
279
281
  f"Argument {str(arg_val)!r} of type "
@@ -377,15 +379,21 @@ def _eval_expr(
377
379
  val = get_sympy_val(val)
378
380
  if expected_type is int and isinstance(val, float) and int(val) == val:
379
381
  val = int(val)
380
- if not isinstance(val, expected_type) and (
381
- not isinstance(val, QmodAnnotatedExpression)
382
- or not isinstance(val.get_type(val.root), expected_qmod_type)
383
- ):
382
+
383
+ failing_type: str | None = None
384
+ if isinstance(val, QmodAnnotatedExpression):
385
+ val_type = val.get_type(val.root)
386
+ if not isinstance(val_type, expected_qmod_type):
387
+ failing_type = val_type.raw_qmod_type_name
388
+ elif not isinstance(val, expected_type):
389
+ failing_type = type(val).__name__
390
+ if failing_type is not None:
384
391
  raise ClassiqExpansionError(
385
392
  f"When inferring the type of parameter {param_name!r}: "
386
- f"{type_name} {attr_name} must be {expected_qmod_type.__name__}, got "
387
- f"{str(val)!r}"
393
+ f"{type_name} {attr_name} must be {expected_qmod_type().qmod_type_name}, "
394
+ f"got {str(val)!r} of type {failing_type}"
388
395
  )
396
+
389
397
  expr = Expression(expr=str(val))
390
398
  expr._evaluated_expr = EvaluatedExpression(value=val)
391
399
  return expr
@@ -2,7 +2,7 @@ import ast
2
2
  from collections.abc import Mapping, Sequence
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
- from typing import Any, Union, cast
5
+ from typing import Any, cast
6
6
 
7
7
  import sympy
8
8
 
@@ -98,26 +98,24 @@ class QmodAnnotatedExpression:
98
98
  def get_node(self, node_id: QmodExprNodeId) -> ast.AST:
99
99
  return self._node_mapping[node_id]
100
100
 
101
- def set_value(self, node: Union[ast.AST, QmodExprNodeId], value: Any) -> None:
101
+ def set_value(self, node: ast.AST | QmodExprNodeId, value: Any) -> None:
102
102
  if self._locked:
103
103
  raise ClassiqInternalExpansionError("QAE is locked")
104
104
  if isinstance(node, ast.AST):
105
105
  node = id(node)
106
106
  self._values[node] = value
107
107
 
108
- def get_value(self, node: Union[ast.AST, QmodExprNodeId]) -> Any:
108
+ def get_value(self, node: ast.AST | QmodExprNodeId) -> Any:
109
109
  if isinstance(node, ast.AST):
110
110
  node = id(node)
111
111
  return self._values[node]
112
112
 
113
- def has_value(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
113
+ def has_value(self, node: ast.AST | QmodExprNodeId) -> bool:
114
114
  if isinstance(node, ast.AST):
115
115
  node = id(node)
116
116
  return node in self._values
117
117
 
118
- def set_type(
119
- self, node: Union[ast.AST, QmodExprNodeId], qmod_type: QmodType
120
- ) -> None:
118
+ def set_type(self, node: ast.AST | QmodExprNodeId, qmod_type: QmodType) -> None:
121
119
  if self._locked:
122
120
  raise ClassiqInternalExpansionError("QAE is locked")
123
121
  if isinstance(node, ast.AST):
@@ -126,12 +124,12 @@ class QmodAnnotatedExpression:
126
124
  node = id(node)
127
125
  self._types[node] = qmod_type
128
126
 
129
- def get_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QmodType:
127
+ def get_type(self, node: ast.AST | QmodExprNodeId) -> QmodType:
130
128
  if isinstance(node, ast.AST):
131
129
  node = id(node)
132
130
  return self._types[node]
133
131
 
134
- def get_quantum_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QuantumType:
132
+ def get_quantum_type(self, node: ast.AST | QmodExprNodeId) -> QuantumType:
135
133
  if isinstance(node, ast.AST):
136
134
  node = id(node)
137
135
  qmod_type = self._types[node]
@@ -139,7 +137,7 @@ class QmodAnnotatedExpression:
139
137
  raise ClassiqInternalExpansionError
140
138
  return cast(QuantumType, qmod_type)
141
139
 
142
- def get_classical_type(self, node: Union[ast.AST, QmodExprNodeId]) -> ClassicalType:
140
+ def get_classical_type(self, node: ast.AST | QmodExprNodeId) -> ClassicalType:
143
141
  if isinstance(node, ast.AST):
144
142
  node = id(node)
145
143
  qmod_type = self._types[node]
@@ -147,7 +145,7 @@ class QmodAnnotatedExpression:
147
145
  raise ClassiqInternalExpansionError
148
146
  return cast(ClassicalType, qmod_type)
149
147
 
150
- def set_var(self, node: Union[ast.AST, QmodExprNodeId], var: HandleBinding) -> None:
148
+ def set_var(self, node: ast.AST | QmodExprNodeId, var: HandleBinding) -> None:
151
149
  if self._locked:
152
150
  raise ClassiqInternalExpansionError("QAE is locked")
153
151
  var = var.collapse()
@@ -158,25 +156,25 @@ class QmodAnnotatedExpression:
158
156
  else:
159
157
  self._quantum_vars[node] = var
160
158
 
161
- def get_var(self, node: Union[ast.AST, QmodExprNodeId]) -> HandleBinding:
159
+ def get_var(self, node: ast.AST | QmodExprNodeId) -> HandleBinding:
162
160
  if isinstance(node, ast.AST):
163
161
  node = id(node)
164
162
  return (self._classical_vars | self._quantum_vars)[node]
165
163
 
166
- def has_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
164
+ def has_var(self, node: ast.AST | QmodExprNodeId) -> bool:
167
165
  return self.has_classical_var(node) or self.has_quantum_var(node)
168
166
 
169
- def has_classical_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
167
+ def has_classical_var(self, node: ast.AST | QmodExprNodeId) -> bool:
170
168
  if isinstance(node, ast.AST):
171
169
  node = id(node)
172
170
  return node in self._classical_vars
173
171
 
174
- def has_quantum_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
172
+ def has_quantum_var(self, node: ast.AST | QmodExprNodeId) -> bool:
175
173
  if isinstance(node, ast.AST):
176
174
  node = id(node)
177
175
  return node in self._quantum_vars
178
176
 
179
- def remove_var(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
177
+ def remove_var(self, node: ast.AST | QmodExprNodeId) -> None:
180
178
  if self._locked:
181
179
  raise ClassiqInternalExpansionError("QAE is locked")
182
180
  if isinstance(node, ast.AST):
@@ -188,9 +186,9 @@ class QmodAnnotatedExpression:
188
186
 
189
187
  def set_quantum_subscript(
190
188
  self,
191
- node: Union[ast.AST, QmodExprNodeId],
192
- value: Union[ast.AST, QmodExprNodeId],
193
- index: Union[ast.AST, QmodExprNodeId],
189
+ node: ast.AST | QmodExprNodeId,
190
+ value: ast.AST | QmodExprNodeId,
191
+ index: ast.AST | QmodExprNodeId,
194
192
  ) -> None:
195
193
  if self._locked:
196
194
  raise ClassiqInternalExpansionError("QAE is locked")
@@ -204,11 +202,18 @@ class QmodAnnotatedExpression:
204
202
  value=value, index=index
205
203
  )
206
204
 
207
- def has_quantum_subscript(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
205
+ def has_quantum_subscript(self, node: ast.AST | QmodExprNodeId) -> bool:
208
206
  if isinstance(node, ast.AST):
209
207
  node = id(node)
210
208
  return node in self._quantum_subscripts
211
209
 
210
+ def get_quantum_subcript(
211
+ self, node: ast.AST | QmodExprNodeId
212
+ ) -> QuantumSubscriptAnnotation:
213
+ if isinstance(node, ast.AST):
214
+ node = id(node)
215
+ return self._quantum_subscripts[node]
216
+
212
217
  def get_quantum_subscripts(
213
218
  self,
214
219
  ) -> Mapping[QmodExprNodeId, QuantumSubscriptAnnotation]:
@@ -216,7 +221,7 @@ class QmodAnnotatedExpression:
216
221
 
217
222
  def set_quantum_type_attr(
218
223
  self,
219
- node: Union[ast.AST, QmodExprNodeId],
224
+ node: ast.AST | QmodExprNodeId,
220
225
  value: HandleBinding,
221
226
  attr: str,
222
227
  ) -> None:
@@ -228,7 +233,7 @@ class QmodAnnotatedExpression:
228
233
  value=value, attr=attr
229
234
  )
230
235
 
231
- def has_quantum_type_attribute(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
236
+ def has_quantum_type_attribute(self, node: ast.AST | QmodExprNodeId) -> bool:
232
237
  if isinstance(node, ast.AST):
233
238
  node = id(node)
234
239
  return node in self._quantum_type_attrs
@@ -240,8 +245,8 @@ class QmodAnnotatedExpression:
240
245
 
241
246
  def set_concatenation(
242
247
  self,
243
- node: Union[ast.AST, QmodExprNodeId],
244
- elements: Sequence[Union[ast.AST, QmodExprNodeId]],
248
+ node: ast.AST | QmodExprNodeId,
249
+ elements: Sequence[ast.AST | QmodExprNodeId],
245
250
  ) -> None:
246
251
  if self._locked:
247
252
  raise ClassiqInternalExpansionError("QAE is locked")
@@ -257,7 +262,7 @@ class QmodAnnotatedExpression:
257
262
  inlined_elements.extend(self._concatenations.pop(element).elements)
258
263
  self._concatenations[node] = ConcatenationAnnotation(elements=inlined_elements)
259
264
 
260
- def has_concatenation(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
265
+ def has_concatenation(self, node: ast.AST | QmodExprNodeId) -> bool:
261
266
  if isinstance(node, ast.AST):
262
267
  node = id(node)
263
268
  return node in self._concatenations
@@ -271,7 +276,7 @@ class QmodAnnotatedExpression:
271
276
  def get_quantum_vars(self) -> Mapping[QmodExprNodeId, HandleBinding]:
272
277
  return self._quantum_vars
273
278
 
274
- def clear_node_data(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
279
+ def clear_node_data(self, node: ast.AST | QmodExprNodeId) -> None:
275
280
  if isinstance(node, ast.AST):
276
281
  node = id(node)
277
282
  self._node_mapping.pop(node, None)
@@ -1,7 +1,7 @@
1
1
  import ast
2
- from collections.abc import Mapping, Sequence
2
+ from collections.abc import Callable, Mapping, Sequence
3
3
  from enum import IntEnum
4
- from typing import Any, Callable, Optional, cast
4
+ from typing import Any, cast
5
5
 
6
6
  from classiq.interface.exceptions import (
7
7
  ClassiqExpansionError,
@@ -79,13 +79,13 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
79
79
  *,
80
80
  treat_qnum_as_float: bool = False,
81
81
  machine_precision: int = DEFAULT_MACHINE_PRECISION,
82
- classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
83
- enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
84
- classical_function_declarations: Optional[
82
+ classical_struct_declarations: Sequence[StructDeclaration] | None = None,
83
+ enum_declarations: Sequence[EnumDeclaration] | None = None,
84
+ classical_function_declarations: None | (
85
85
  Sequence[ClassicalFunctionDeclaration]
86
- ] = None,
87
- classical_function_callables: Optional[Mapping[str, Callable]] = None,
88
- scope: Optional[Mapping[str, Any]] = None,
86
+ ) = None,
87
+ classical_function_callables: Mapping[str, Callable] | None = None,
88
+ scope: Mapping[str, Any] | None = None,
89
89
  ) -> None:
90
90
  self._expr_val = expr_val
91
91
  self._treat_qnum_as_float = treat_qnum_as_float
@@ -258,13 +258,13 @@ def evaluate_qmod_expression(
258
258
  *,
259
259
  treat_qnum_as_float: bool = False,
260
260
  machine_precision: int = DEFAULT_MACHINE_PRECISION,
261
- classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
262
- enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
263
- classical_function_declarations: Optional[
261
+ classical_struct_declarations: Sequence[StructDeclaration] | None = None,
262
+ enum_declarations: Sequence[EnumDeclaration] | None = None,
263
+ classical_function_declarations: None | (
264
264
  Sequence[ClassicalFunctionDeclaration]
265
- ] = None,
266
- classical_function_callables: Optional[Mapping[str, Callable]] = None,
267
- scope: Optional[Mapping[str, Any]] = None,
265
+ ) = None,
266
+ classical_function_callables: Mapping[str, Callable] | None = None,
267
+ scope: Mapping[str, Any] | None = None,
268
268
  ) -> QmodAnnotatedExpression:
269
269
  expr_ast = ast.parse(expr, mode="eval").body
270
270
  expr_value = QmodAnnotatedExpression(expr_ast)
@@ -1,5 +1,6 @@
1
1
  import ast
2
- from typing import Any, Callable, TypeVar, cast
2
+ from collections.abc import Callable
3
+ from typing import Any, TypeVar, cast
3
4
 
4
5
  import sympy
5
6