classiq 0.37.0__py3-none-any.whl → 0.38.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. classiq/__init__.py +2 -2
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
  3. classiq/_analyzer_extras/interactive_hardware.py +3 -3
  4. classiq/_internals/api_wrapper.py +24 -16
  5. classiq/_internals/async_utils.py +1 -74
  6. classiq/_internals/authentication/device.py +9 -4
  7. classiq/_internals/authentication/password_manager.py +25 -10
  8. classiq/_internals/authentication/token_manager.py +2 -2
  9. classiq/_internals/client.py +13 -5
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +26 -28
  12. classiq/analyzer/analyzer_utilities.py +5 -5
  13. classiq/analyzer/rb.py +4 -5
  14. classiq/analyzer/show_interactive_hack.py +6 -6
  15. classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
  16. classiq/applications/combinatorial_optimization/__init__.py +5 -0
  17. classiq/applications/qnn/circuit_utils.py +2 -2
  18. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  19. classiq/applications/qnn/types.py +2 -2
  20. classiq/applications/qsvm/qsvm.py +4 -7
  21. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  22. classiq/applications_model_constructors/__init__.py +9 -1
  23. classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
  24. classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
  25. classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
  26. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
  27. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  28. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
  29. classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
  30. classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
  31. classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
  32. classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
  33. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
  34. classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
  35. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  36. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  37. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
  38. classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
  39. classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
  40. classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
  41. classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
  42. classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
  43. classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
  44. classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
  45. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
  46. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
  47. classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  48. classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
  49. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
  50. classiq/applications_model_constructors/finance_model_constructor.py +4 -17
  51. classiq/applications_model_constructors/grover_model_constructor.py +20 -91
  52. classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
  53. classiq/builtin_functions/standard_gates.py +1 -1
  54. classiq/exceptions.py +43 -1
  55. classiq/executor.py +10 -9
  56. classiq/interface/_version.py +1 -1
  57. classiq/interface/analyzer/analysis_params.py +6 -3
  58. classiq/interface/analyzer/result.py +12 -4
  59. classiq/interface/applications/qsvm.py +13 -1
  60. classiq/interface/backend/backend_preferences.py +4 -2
  61. classiq/interface/backend/pydantic_backend.py +3 -1
  62. classiq/interface/backend/quantum_backend_providers.py +1 -0
  63. classiq/interface/chemistry/fermionic_operator.py +15 -13
  64. classiq/interface/chemistry/ground_state_problem.py +18 -3
  65. classiq/interface/chemistry/molecule.py +8 -6
  66. classiq/interface/chemistry/operator.py +20 -14
  67. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  68. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  69. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  70. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  71. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
  73. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  74. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  75. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  76. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  77. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  78. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  79. classiq/interface/executor/aws_execution_cost.py +4 -3
  80. classiq/interface/executor/estimation.py +2 -2
  81. classiq/interface/executor/execution_preferences.py +5 -34
  82. classiq/interface/executor/execution_request.py +19 -17
  83. classiq/interface/executor/optimizer_preferences.py +22 -13
  84. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  85. classiq/interface/executor/quantum_instruction_set.py +2 -1
  86. classiq/interface/executor/register_initialization.py +1 -3
  87. classiq/interface/executor/result.py +41 -10
  88. classiq/interface/executor/vqe_result.py +1 -1
  89. classiq/interface/finance/function_input.py +17 -4
  90. classiq/interface/finance/gaussian_model_input.py +3 -1
  91. classiq/interface/finance/log_normal_model_input.py +3 -1
  92. classiq/interface/finance/model_input.py +2 -0
  93. classiq/interface/generator/amplitude_loading.py +6 -3
  94. classiq/interface/generator/application_apis/__init__.py +1 -0
  95. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  96. classiq/interface/generator/arith/argument_utils.py +14 -4
  97. classiq/interface/generator/arith/arithmetic.py +3 -1
  98. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  99. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  100. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  101. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  102. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  103. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  104. classiq/interface/generator/arith/binary_ops.py +202 -54
  105. classiq/interface/generator/arith/extremum_operations.py +5 -3
  106. classiq/interface/generator/arith/logical_ops.py +4 -2
  107. classiq/interface/generator/arith/machine_precision.py +3 -0
  108. classiq/interface/generator/arith/number_utils.py +34 -44
  109. classiq/interface/generator/arith/register_user_input.py +21 -1
  110. classiq/interface/generator/arith/unary_ops.py +16 -25
  111. classiq/interface/generator/chemistry_function_params.py +4 -4
  112. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  113. classiq/interface/generator/compiler_keywords.py +4 -0
  114. classiq/interface/generator/complex_type.py +3 -10
  115. classiq/interface/generator/control_state.py +5 -3
  116. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  117. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  118. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  119. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  120. classiq/interface/generator/expressions/expression.py +1 -1
  121. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  122. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  123. classiq/interface/generator/finance.py +1 -1
  124. classiq/interface/generator/function_params.py +7 -6
  125. classiq/interface/generator/functions/__init__.py +1 -1
  126. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
  127. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
  128. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  129. classiq/interface/generator/functions/function_implementation.py +8 -4
  130. classiq/interface/generator/functions/native_function_definition.py +4 -2
  131. classiq/interface/generator/functions/register.py +4 -2
  132. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  133. classiq/interface/generator/generated_circuit_data.py +2 -2
  134. classiq/interface/generator/grover_operator.py +5 -3
  135. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  136. classiq/interface/generator/hardware/hardware_data.py +6 -4
  137. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  138. classiq/interface/generator/hartree_fock.py +3 -1
  139. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  140. classiq/interface/generator/mcu.py +5 -3
  141. classiq/interface/generator/mcx.py +7 -5
  142. classiq/interface/generator/model/constraints.py +2 -1
  143. classiq/interface/generator/model/model.py +11 -19
  144. classiq/interface/generator/model/preferences/preferences.py +4 -3
  145. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  146. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  147. classiq/interface/generator/qpe.py +6 -4
  148. classiq/interface/generator/qsvm.py +5 -8
  149. classiq/interface/generator/quantum_function_call.py +21 -16
  150. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  151. classiq/interface/generator/range_types.py +3 -1
  152. classiq/interface/generator/slice_parsing_utils.py +8 -3
  153. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  154. classiq/interface/generator/state_preparation/metrics.py +2 -1
  155. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  156. classiq/interface/generator/state_propagator.py +16 -5
  157. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  158. classiq/interface/generator/types/struct_declaration.py +8 -3
  159. classiq/interface/generator/ucc.py +6 -4
  160. classiq/interface/generator/unitary_gate.py +7 -3
  161. classiq/interface/generator/validations/flow_graph.py +6 -4
  162. classiq/interface/generator/validations/validator_functions.py +6 -4
  163. classiq/interface/hardware.py +2 -2
  164. classiq/interface/helpers/custom_encoders.py +3 -0
  165. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  166. classiq/interface/helpers/validation_helpers.py +1 -1
  167. classiq/interface/helpers/versioned_model.py +4 -1
  168. classiq/interface/ide/show.py +2 -2
  169. classiq/interface/jobs.py +72 -3
  170. classiq/interface/model/bind_operation.py +18 -11
  171. classiq/interface/model/call_synthesis_data.py +68 -0
  172. classiq/interface/model/inplace_binary_operation.py +2 -2
  173. classiq/interface/model/model.py +27 -21
  174. classiq/interface/model/native_function_definition.py +3 -5
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  176. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  177. classiq/interface/model/quantum_function_call.py +25 -139
  178. classiq/interface/model/quantum_function_declaration.py +8 -0
  179. classiq/interface/model/quantum_if_operation.py +2 -3
  180. classiq/interface/model/quantum_lambda_function.py +64 -0
  181. classiq/interface/model/quantum_type.py +57 -56
  182. classiq/interface/model/quantum_variable_declaration.py +1 -1
  183. classiq/interface/model/statement_block.py +32 -0
  184. classiq/interface/model/validations/handles_validator.py +14 -12
  185. classiq/interface/model/within_apply_operation.py +11 -0
  186. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  187. classiq/interface/server/routes.py +5 -0
  188. classiq/model/function_handler.py +5 -9
  189. classiq/model/model.py +2 -19
  190. classiq/qmod/__init__.py +13 -6
  191. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  192. classiq/qmod/builtins/classical_functions.py +24 -14
  193. classiq/qmod/builtins/functions.py +162 -145
  194. classiq/qmod/builtins/operations.py +24 -35
  195. classiq/qmod/builtins/structs.py +15 -15
  196. classiq/qmod/cfunc.py +42 -0
  197. classiq/qmod/classical_function.py +6 -14
  198. classiq/qmod/declaration_inferrer.py +12 -21
  199. classiq/qmod/expression_query.py +23 -0
  200. classiq/qmod/model_state_container.py +2 -0
  201. classiq/qmod/native/__init__.py +0 -0
  202. classiq/qmod/native/expression_to_qmod.py +189 -0
  203. classiq/qmod/native/pretty_printer.py +311 -0
  204. classiq/qmod/qfunc.py +27 -0
  205. classiq/qmod/qmod_constant.py +76 -0
  206. classiq/qmod/qmod_parameter.py +34 -12
  207. classiq/qmod/qmod_struct.py +3 -3
  208. classiq/qmod/qmod_variable.py +102 -18
  209. classiq/qmod/quantum_expandable.py +16 -16
  210. classiq/qmod/quantum_function.py +37 -8
  211. classiq/qmod/symbolic.py +47 -4
  212. classiq/qmod/symbolic_expr.py +9 -0
  213. classiq/qmod/utilities.py +13 -0
  214. classiq/qmod/write_qmod.py +39 -0
  215. classiq/quantum_functions/__init__.py +2 -2
  216. classiq/quantum_functions/annotation_parser.py +9 -11
  217. classiq/quantum_functions/function_parser.py +1 -1
  218. classiq/quantum_functions/quantum_function.py +3 -3
  219. classiq/quantum_register.py +17 -9
  220. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
  221. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
  222. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
  223. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  224. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  225. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  226. classiq/interface/model/numeric_reinterpretation.py +0 -25
  227. classiq/interface/model/operator_synthesis_data.py +0 -48
  228. classiq/model/function_handler.pyi +0 -152
classiq/__init__.py CHANGED
@@ -13,7 +13,7 @@ from classiq.interface.generator.control_state import ControlState
13
13
  from classiq.interface.generator.expressions.enums.pauli import Pauli
14
14
  from classiq.interface.generator.functions import * # noqa: F403
15
15
  from classiq.interface.generator.functions import __all__ as _ifunc_all
16
- from classiq.interface.generator.generated_circuit import GeneratedCircuit
16
+ from classiq.interface.generator.quantum_program import QuantumProgram
17
17
  from classiq.interface.ide.show import show
18
18
 
19
19
  from classiq import (
@@ -82,7 +82,7 @@ __all__ = (
82
82
  "RegisterArithmeticInfo",
83
83
  "ControlState",
84
84
  "Analyzer",
85
- "GeneratedCircuit",
85
+ "QuantumProgram",
86
86
  "authenticate",
87
87
  "synthesize",
88
88
  "synthesize_async",
@@ -33,7 +33,7 @@ def widget_callback(widget_name: WidgetName) -> Callable[[WidgetFunc], Callable]
33
33
  interactive_loop = _create_interactive_loop_async(
34
34
  function=function, self=self, widget=widget
35
35
  )
36
- asyncio.ensure_future(interactive_loop)
36
+ asyncio.ensure_future(interactive_loop) # noqa: RUF006
37
37
  await asyncio.sleep(SleepTime)
38
38
 
39
39
  return wrapper
@@ -6,7 +6,7 @@ from ipywidgets import Combobox, HBox, VBox # type: ignore[import]
6
6
 
7
7
  from classiq.interface.analyzer import analysis_params
8
8
  from classiq.interface.backend.quantum_backend_providers import AnalyzerProviderVendor
9
- from classiq.interface.generator.generated_circuit import GeneratedCircuit
9
+ from classiq.interface.generator.quantum_program import QuantumProgram
10
10
 
11
11
  from classiq._analyzer_extras._ipywidgets_async_extension import widget_callback
12
12
  from classiq.analyzer.analyzer_utilities import (
@@ -22,7 +22,7 @@ class InteractiveHardware(AnalyzerUtilities):
22
22
  def __init__(
23
23
  self,
24
24
  params: analysis_params.AnalysisParams,
25
- circuit: GeneratedCircuit,
25
+ circuit: QuantumProgram,
26
26
  available_devices: ProviderAvailableDevices,
27
27
  hardware_graphs: HardwareGraphs,
28
28
  ) -> None:
@@ -70,7 +70,7 @@ class InteractiveHardware(AnalyzerUtilities):
70
70
  ) -> None:
71
71
  if not provider:
72
72
  return
73
- await self.request_available_devices_async(providers=[provider])
73
+ await self._request_available_devices_async(providers=[provider])
74
74
  self.devices_combobox.options = self._filter_devices_by_qubits_count(provider)
75
75
  self.devices_combobox.disabled = False
76
76
  self.devices_combobox.placeholder = "Choose device"
@@ -13,8 +13,14 @@ from classiq.interface.execution.jobs import (
13
13
  ExecutionJobsQueryResultsV1,
14
14
  )
15
15
  from classiq.interface.executor import execution_request, result as execute_result
16
- from classiq.interface.generator import generated_circuit as generator_result
17
- from classiq.interface.jobs import JobDescription, JobID, JobStatus, JSONObject
16
+ from classiq.interface.generator import quantum_program as generator_result
17
+ from classiq.interface.jobs import (
18
+ JobDescriptionBase,
19
+ JobDescriptionFailure,
20
+ JobDescriptionSuccess,
21
+ JobID,
22
+ JSONObject,
23
+ )
18
24
  from classiq.interface.model.common_model_types import ModelInput
19
25
  from classiq.interface.server import routes
20
26
 
@@ -42,13 +48,15 @@ class StatusType(Protocol):
42
48
 
43
49
 
44
50
  def _parse_job_response(
45
- job_result: JobDescription[JSONObject],
51
+ job_result: JobDescriptionBase[JSONObject],
46
52
  output_type: Type[ResultType],
47
53
  ) -> ResultType:
48
- description = job_result.description
49
- if job_result.status != JobStatus.COMPLETED:
50
- raise ClassiqAPIError(description["details"])
51
- return output_type.parse_obj(description)
54
+ if isinstance(job_result, JobDescriptionSuccess):
55
+ return output_type.parse_obj(job_result.description)
56
+ if isinstance(job_result, JobDescriptionFailure):
57
+ raise ClassiqAPIError(job_result.description.details)
58
+
59
+ raise ClassiqAPIError("Unexpected response from server")
52
60
 
53
61
 
54
62
  class ApiWrapper:
@@ -93,14 +101,14 @@ class ApiWrapper:
93
101
  @classmethod
94
102
  async def call_generation_task(
95
103
  cls, model: ModelInput
96
- ) -> generator_result.GeneratedCircuit:
104
+ ) -> generator_result.QuantumProgram:
97
105
  poller = JobPoller(base_url=routes.TASKS_GENERATE_FULL_PATH)
98
106
  result = await poller.run_pydantic(model, timeout_sec=None)
99
- return _parse_job_response(result, generator_result.GeneratedCircuit)
107
+ return _parse_job_response(result, generator_result.QuantumProgram)
100
108
 
101
109
  @classmethod
102
110
  async def call_execute_generated_circuit(
103
- cls, circuit: generator_result.GeneratedCircuit
111
+ cls, circuit: generator_result.QuantumProgram
104
112
  ) -> execution_request.ExecutionJobDetails:
105
113
  execution_input = await cls._call_task_pydantic(
106
114
  http_method=HTTPMethod.POST,
@@ -125,7 +133,7 @@ class ApiWrapper:
125
133
  cls,
126
134
  job_id: JobID,
127
135
  ) -> execution_request.ExecutionJobDetails:
128
- headers = {_CONTENT_TYPE_HEADER: "v1"}
136
+ headers = {_ACCEPT_HEADER: "v1"}
129
137
  data = await cls._call_task(
130
138
  http_method=HTTPMethod.GET,
131
139
  headers=headers,
@@ -215,7 +223,7 @@ class ApiWrapper:
215
223
 
216
224
  @classmethod
217
225
  async def call_analyzer_app(
218
- cls, params: generator_result.GeneratedCircuit
226
+ cls, params: generator_result.QuantumProgram
219
227
  ) -> analysis_result.DataID:
220
228
  data = await cls._call_task_pydantic(
221
229
  http_method=HTTPMethod.POST,
@@ -227,23 +235,23 @@ class ApiWrapper:
227
235
  @classmethod
228
236
  async def get_generated_circuit_from_qasm(
229
237
  cls, params: analysis_result.QasmCode
230
- ) -> generator_result.GeneratedCircuit:
238
+ ) -> generator_result.QuantumProgram:
231
239
  data = await cls._call_task_pydantic(
232
240
  http_method=HTTPMethod.POST,
233
241
  url=routes.IDE_QASM_FULL_PATH,
234
242
  model=params,
235
243
  )
236
- return generator_result.GeneratedCircuit.parse_obj(data)
244
+ return generator_result.QuantumProgram.parse_obj(data)
237
245
 
238
246
  @classmethod
239
247
  async def get_analyzer_app_data(
240
248
  cls, params: analysis_result.DataID
241
- ) -> generator_result.GeneratedCircuit:
249
+ ) -> generator_result.QuantumProgram:
242
250
  data = await cls._call_task(
243
251
  http_method=HTTPMethod.GET,
244
252
  url=f"{routes.ANALYZER_DATA_FULL_PATH}/{params.id}",
245
253
  )
246
- return generator_result.GeneratedCircuit.parse_obj(data)
254
+ return generator_result.QuantumProgram.parse_obj(data)
247
255
 
248
256
  @classmethod
249
257
  async def call_rb_analysis_task(
@@ -1,11 +1,8 @@
1
- import abc
2
1
  import asyncio
3
2
  import functools
4
- import inspect
5
3
  import itertools
6
4
  import logging
7
5
  import time
8
- import types
9
6
  from typing import (
10
7
  Any,
11
8
  AsyncGenerator,
@@ -18,8 +15,6 @@ from typing import (
18
15
  Union,
19
16
  )
20
17
 
21
- from classiq.exceptions import ClassiqValueError
22
-
23
18
  T = TypeVar("T")
24
19
  ASYNC_SUFFIX = "_async"
25
20
 
@@ -58,74 +53,6 @@ def syncify_function(async_func: Callable[..., Awaitable[T]]) -> Callable[..., T
58
53
  return async_wrapper
59
54
 
60
55
 
61
- def maybe_syncify_property_function(obj: Any) -> Any:
62
- """
63
- The object is the input to `property` (or to `property.setter`)
64
- Thus, we expect it to be either a function, or a function-that-returns-a-coroutine, or None
65
- The only thing that should be syncified is a function-that-returns-a-coroutine
66
- """
67
- if inspect.iscoroutinefunction(obj):
68
- return syncify_function(obj)
69
- elif isinstance(obj, types.FunctionType):
70
- return obj
71
- elif obj is None:
72
- return obj
73
- else:
74
- raise ClassiqValueError(f"Invalid type: {obj.__class__.__name__}")
75
-
76
-
77
- def syncify_property(async_prop: property) -> property:
78
- if inspect.iscoroutinefunction(async_prop.fset):
79
- raise ClassiqValueError(f"Setter cannot be `async def` (in {async_prop}")
80
- if inspect.iscoroutinefunction(async_prop.fdel):
81
- raise ClassiqValueError(f"Deleter cannot be `async def` (in {async_prop}")
82
-
83
- return property(
84
- maybe_syncify_property_function(async_prop.fget),
85
- async_prop.fset,
86
- async_prop.fdel,
87
- async_prop.__doc__,
88
- )
89
-
90
-
91
- # Explanation about metaclasses
92
- # https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
93
-
94
-
95
- class Asyncify(type):
96
- def __new__(
97
- mcls, name: str, bases: tuple, class_dict: dict # noqa: N804
98
- ) -> "Asyncify":
99
- new_attrs = {}
100
-
101
- for attr_name, attr_value in class_dict.items():
102
- if attr_name.endswith(ASYNC_SUFFIX):
103
- new_attr_name = attr_name[: -len(ASYNC_SUFFIX)]
104
- if new_attr_name in class_dict:
105
- raise ClassiqValueError(f"Method name collision: {new_attr_name}")
106
- else:
107
- new_attrs[new_attr_name] = attr_value
108
-
109
- new_class = super().__new__(mcls, name, bases, class_dict)
110
-
111
- for attr_name, attr_value in new_attrs.items():
112
- if isinstance(attr_value, property):
113
- setattr(new_class, attr_name, syncify_property(attr_value))
114
- elif isinstance(attr_value, types.FunctionType):
115
- setattr(new_class, attr_name, syncify_function(attr_value))
116
- else:
117
- raise ClassiqValueError(
118
- f"Invalid async type: {attr_value.__class__.__name__}"
119
- )
120
-
121
- return new_class
122
-
123
-
124
- # Used for resolving metaclass collision
125
- class AsyncifyABC(Asyncify, abc.ABCMeta):
126
- pass
127
-
128
-
129
56
  def enable_jupyter_notebook() -> None:
130
57
  import nest_asyncio # type: ignore[import]
131
58
 
@@ -172,7 +99,7 @@ def is_notebook() -> bool:
172
99
  return True # Jupyter notebook or qtconsole
173
100
  elif shell == "TerminalInteractiveShell":
174
101
  return False # Terminal running IPython
175
- elif "google.colab" in str(local_ipython):
102
+ elif "google.colab" in str(local_ipython): # noqa: SIM103
176
103
  return True
177
104
  else:
178
105
  return False # Other type (?)
@@ -26,9 +26,9 @@ class DeviceRegistrar:
26
26
  get_refresh_token=get_refresh_token
27
27
  )
28
28
 
29
- print(f"Your user code: {data['user_code']}")
29
+ print(f"Your user code: {data['user_code']}") # noqa: T201
30
30
  verification_url = data["verification_uri_complete"]
31
- print(
31
+ print( # noqa: T201
32
32
  f"If a browser doesn't automatically open, please visit this URL from any trusted device: {verification_url}"
33
33
  )
34
34
  if not text_only:
@@ -68,7 +68,7 @@ class DeviceRegistrar:
68
68
  timeout: float,
69
69
  get_refresh_token: bool = True,
70
70
  ) -> Tokens:
71
- async def poller():
71
+ async def poller() -> Dict[str, Any]:
72
72
  nonlocal device_code
73
73
  return await auth0_client.poll_tokens(device_code=device_code)
74
74
 
@@ -92,7 +92,12 @@ class DeviceRegistrar:
92
92
  elif error_code == "expired_token":
93
93
  raise ClassiqExpiredTokenError(cls._TIMEOUT_ERROR)
94
94
  elif error_code == "access_denied":
95
- error_description: str = data.get("error_description")
95
+ error_description = data.get("error_description")
96
+ if error_description is None:
97
+ raise ClassiqAuthenticationError(
98
+ "Failed authenticating to Classiq, missing error description"
99
+ )
100
+
96
101
  raise ClassiqAuthenticationError(error_description)
97
102
  else:
98
103
  raise ClassiqAuthenticationError(
@@ -9,30 +9,41 @@ from typing import Dict, Optional
9
9
 
10
10
  import keyring
11
11
  from keyring.backends import fail
12
+ from pydantic import BaseSettings, Field
12
13
 
13
14
  _logger = logging.getLogger(__name__)
14
15
 
15
16
 
17
+ class PasswordManagerSettings(BaseSettings):
18
+ ACCESS_TOKEN_KEY: str = Field(
19
+ default="classiqTokenAccount", env="CLASSIQ_ACCESS_TOKEN_ACCOUNT"
20
+ )
21
+ REFRESH_TOKEN_KEY: str = Field(
22
+ default="classiqRefershTokenAccount", env="CLASSIQ_REFRESH_TOKEN_ACCOUNT"
23
+ )
24
+
25
+
16
26
  class PasswordManager(abc.ABC):
17
27
  _SERVICE_NAME: str = "classiqTokenService"
18
- _ACCESS_TOKEN_KEY: str = "classiqTokenAccount"
19
- _REFRESH_TOKEN_KEY: str = "classiqRefershTokenAccount"
28
+
29
+ def __init__(self) -> None:
30
+ self._settings = PasswordManagerSettings()
20
31
 
21
32
  @property
22
33
  def access_token(self) -> Optional[str]:
23
- return self._get(key=self._ACCESS_TOKEN_KEY)
34
+ return self._get(key=self._settings.ACCESS_TOKEN_KEY)
24
35
 
25
36
  @access_token.setter
26
37
  def access_token(self, access_token: Optional[str]) -> None:
27
- self._set(key=self._ACCESS_TOKEN_KEY, value=access_token)
38
+ self._set(key=self._settings.ACCESS_TOKEN_KEY, value=access_token)
28
39
 
29
40
  @property
30
41
  def refresh_token(self) -> Optional[str]:
31
- return self._get(key=self._REFRESH_TOKEN_KEY)
42
+ return self._get(key=self._settings.REFRESH_TOKEN_KEY)
32
43
 
33
44
  @refresh_token.setter
34
45
  def refresh_token(self, refresh_token: Optional[str]) -> None:
35
- self._set(key=self._REFRESH_TOKEN_KEY, value=refresh_token)
46
+ self._set(key=self._settings.REFRESH_TOKEN_KEY, value=refresh_token)
36
47
 
37
48
  @abc.abstractmethod
38
49
  def _get(self, key: str) -> Optional[str]:
@@ -46,8 +57,9 @@ class PasswordManager(abc.ABC):
46
57
  def _clear(self, key: str) -> None:
47
58
  pass
48
59
 
60
+ @staticmethod
49
61
  @abc.abstractmethod
50
- def is_supported(self) -> bool:
62
+ def is_supported() -> bool:
51
63
  pass
52
64
 
53
65
 
@@ -71,7 +83,8 @@ class KeyringPasswordManager(PasswordManager):
71
83
  username=key,
72
84
  )
73
85
 
74
- def is_supported(self) -> bool:
86
+ @staticmethod
87
+ def is_supported() -> bool:
75
88
  return not isinstance(keyring.get_keyring(), fail.Keyring)
76
89
 
77
90
 
@@ -85,7 +98,8 @@ class DummyPasswordManager(PasswordManager):
85
98
  def _clear(self, key: str) -> None:
86
99
  return
87
100
 
88
- def is_supported(self) -> bool:
101
+ @staticmethod
102
+ def is_supported() -> bool:
89
103
  return True
90
104
 
91
105
 
@@ -125,5 +139,6 @@ class FilePasswordManager(PasswordManager):
125
139
  token_dict.pop(key)
126
140
  self._update_file(token_dict)
127
141
 
128
- def is_supported(self) -> bool:
142
+ @staticmethod
143
+ def is_supported() -> bool:
129
144
  return "windows" not in platform.platform().lower()
@@ -13,7 +13,7 @@ from classiq.exceptions import (
13
13
  ClassiqPasswordManagerSelectionError,
14
14
  )
15
15
 
16
- PASSWORD_MANAGERS = [pm.KeyringPasswordManager(), pm.FilePasswordManager()]
16
+ PASSWORD_MANAGERS = [pm.KeyringPasswordManager, pm.FilePasswordManager]
17
17
  _logger = logging.getLogger(__name__)
18
18
 
19
19
 
@@ -57,7 +57,7 @@ class TokenManager:
57
57
  return pm.FilePasswordManager()
58
58
  for password_manager in PASSWORD_MANAGERS:
59
59
  if password_manager.is_supported():
60
- return password_manager
60
+ return password_manager()
61
61
  raise ClassiqPasswordManagerSelectionError(
62
62
  "Password Manager not found, we could not store your credentials securely. Please contact support@classiq.io"
63
63
  )
@@ -6,10 +6,11 @@ import os
6
6
  import platform
7
7
  import ssl
8
8
  import sys
9
- from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, Union
9
+ from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, TypeVar, Union
10
10
 
11
11
  import httpx
12
12
  from packaging.version import Version
13
+ from typing_extensions import ParamSpec
13
14
 
14
15
  from classiq.interface._version import VERSION as _VERSION
15
16
 
@@ -80,9 +81,13 @@ def _get_user_agent_header() -> Headers:
80
81
  }
81
82
 
82
83
 
84
+ Ret = TypeVar("Ret")
85
+ P = ParamSpec("P")
86
+
87
+
83
88
  def try_again_on_failure(
84
- func: Callable[..., Awaitable[Any]]
85
- ) -> Callable[..., Awaitable[Any]]:
89
+ func: Callable[P, Awaitable[Ret]]
90
+ ) -> Callable[P, Awaitable[Ret]]:
86
91
  def check_approved_api_error(error_message: str) -> bool:
87
92
  for approved_api_error in APPROVED_API_ERROR_MESSAGES_FOR_RESTART:
88
93
  if approved_api_error in error_message:
@@ -93,7 +98,7 @@ def try_again_on_failure(
93
98
  raise TypeError("Must decorate a coroutine function")
94
99
 
95
100
  @functools.wraps(func)
96
- async def wrapper(*args, **kwargs):
101
+ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> Ret:
97
102
  for i in range(_RETRY_COUNT):
98
103
  try:
99
104
  return await func(*args, **kwargs)
@@ -123,6 +128,9 @@ def try_again_on_failure(
123
128
  "There is problem with the connection to Classiq's server. Trying again"
124
129
  )
125
130
  await asyncio.sleep(API_ERROR_SLEEP_TIME)
131
+ raise ClassiqAPIError(
132
+ "Reached max retries when trying to connect to Classiq's server"
133
+ )
126
134
 
127
135
  return wrapper
128
136
 
@@ -186,7 +194,7 @@ class Client:
186
194
  try:
187
195
  detail = response.json()["detail"]
188
196
  message += f": {detail}"
189
- except Exception: # nosec B110
197
+ except Exception: # noqa: S110
190
198
  pass
191
199
  raise ClassiqAPIError(message)
192
200
 
@@ -5,7 +5,12 @@ from typing import Callable, Dict, Iterable, Optional, Set, TypeVar
5
5
  import httpx
6
6
  import pydantic
7
7
 
8
- from classiq.interface.jobs import JobDescription, JobID, JSONObject
8
+ from classiq.interface.jobs import (
9
+ JobDescriptionBase,
10
+ JobDescriptionUnion,
11
+ JobID,
12
+ JSONObject,
13
+ )
9
14
 
10
15
  from classiq._internals.async_utils import poll_for
11
16
  from classiq._internals.client import client, try_again_on_failure
@@ -13,7 +18,7 @@ from classiq._internals.config import SDKMode
13
18
  from classiq.exceptions import ClassiqAPIError
14
19
 
15
20
  _URL_PATH_SEP = "/"
16
- GeneralJobDescription = JobDescription[JSONObject]
21
+ GeneralJobDescription = JobDescriptionBase[JSONObject]
17
22
  _logger = logging.getLogger(__name__)
18
23
  T = TypeVar("T")
19
24
 
@@ -37,11 +42,9 @@ def _join_url_path(*parts: str) -> str:
37
42
  def _general_job_description_parser(
38
43
  json_response: JSONObject,
39
44
  ) -> Optional[GeneralJobDescription]:
40
- job_description: GeneralJobDescription = GeneralJobDescription.parse_obj(
41
- json_response
42
- )
43
- if job_description.status.is_final():
44
- return job_description
45
+ job_description = JobDescriptionUnion[JSONObject].parse_obj(json_response)
46
+ if job_description.__root__.status.is_final():
47
+ return job_description.__root__
45
48
  return None
46
49
 
47
50
 
@@ -10,16 +10,13 @@ import plotly.graph_objects as go
10
10
 
11
11
  from classiq.interface.analyzer import analysis_params
12
12
  from classiq.interface.backend.quantum_backend_providers import AnalyzerProviderVendor
13
- from classiq.interface.generator import generated_circuit as generator_result
13
+ from classiq.interface.generator import quantum_program as generator_result
14
14
 
15
- from classiq._internals import client
15
+ from classiq._internals import async_utils, client
16
16
  from classiq._internals.api_wrapper import ApiWrapper
17
- from classiq._internals.async_utils import Asyncify
18
17
  from classiq.analyzer.analyzer_utilities import (
19
18
  AnalyzerUtilities,
20
19
  DeviceName,
21
- HardwareGraphs,
22
- ProviderAvailableDevices,
23
20
  ProviderNameEnum,
24
21
  )
25
22
  from classiq.analyzer.url_utils import circuit_page_uri, client_ide_base_url
@@ -34,10 +31,10 @@ if find_ipywidgets is not None:
34
31
  from classiq._analyzer_extras.interactive_hardware import InteractiveHardware
35
32
 
36
33
 
37
- class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
34
+ class Analyzer(AnalyzerUtilities):
38
35
  """Analyzer is the wrapper object for all analysis capabilities."""
39
36
 
40
- def __init__(self, circuit: generator_result.GeneratedCircuit) -> None:
37
+ def __init__(self, circuit: generator_result.QuantumProgram) -> None:
41
38
  """Init self.
42
39
 
43
40
  Args:
@@ -47,26 +44,31 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
47
44
  raise ClassiqAnalyzerError(
48
45
  "Analysis requires a circuit with valid QASM code"
49
46
  )
50
- self._params: analysis_params.AnalysisParams = analysis_params.AnalysisParams(
47
+ params: analysis_params.AnalysisParams = analysis_params.AnalysisParams(
51
48
  qasm=circuit.qasm
52
49
  )
53
- self.circuit: generator_result.GeneratedCircuit = circuit
50
+ super().__init__(
51
+ params=params,
52
+ circuit=circuit,
53
+ available_devices=dict(),
54
+ hardware_graphs=dict(),
55
+ )
56
+
54
57
  self.hardware_comparison_table: Optional[go.Figure] = None
55
- self.available_devices: ProviderAvailableDevices = dict()
56
- self.hardware_graphs: HardwareGraphs = dict()
57
58
 
58
59
  self.transpilation_params = analysis_params.AnalysisHardwareTranspilationParams(
59
60
  hardware_data=self.circuit.hardware_data,
60
- model_preferences=self.circuit.model.preferences,
61
+ random_seed=self.circuit.model.execution_preferences.random_seed,
62
+ transpilation_option=self.circuit.model.execution_preferences.transpile_to_hardware,
61
63
  )
62
64
 
63
- async def analyzer_app_async(self) -> None:
65
+ def analyzer_app(self) -> None:
64
66
  """Opens the analyzer app with synthesis interactive results.
65
67
 
66
68
  Returns:
67
69
  None.
68
70
  """
69
- result = await ApiWrapper.call_analyzer_app(self.circuit)
71
+ result = async_utils.run(ApiWrapper.call_analyzer_app(self.circuit))
70
72
  webbrowser.open_new_tab(
71
73
  urljoin(
72
74
  client_ide_base_url(),
@@ -76,7 +78,7 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
76
78
  )
77
79
  )
78
80
 
79
- async def get_available_devices_async(
81
+ def get_available_devices(
80
82
  self, providers: Optional[List[ProviderNameEnum]] = None
81
83
  ) -> Dict[ProviderNameEnum, List[DeviceName]]:
82
84
  """Returns dict of the available devices by the providers. only devices
@@ -90,13 +92,13 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
90
92
  """
91
93
  if providers is None:
92
94
  providers = list(AnalyzerProviderVendor)
93
- await self.request_available_devices_async(providers=providers)
95
+ async_utils.run(self._request_available_devices_async(providers=providers))
94
96
  return {
95
97
  provider: self._filter_devices_by_qubits_count(provider)
96
98
  for provider in providers
97
99
  }
98
100
 
99
- async def plot_hardware_connectivity_async(
101
+ def plot_hardware_connectivity(
100
102
  self,
101
103
  provider: Optional[ProviderNameEnum] = None,
102
104
  device: Optional[DeviceName] = None,
@@ -118,7 +120,7 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
118
120
  available_devices=self.available_devices,
119
121
  hardware_graphs=self.hardware_graphs,
120
122
  )
121
- await interactive_hardware.enable_interactivity_async()
123
+ async_utils.run(interactive_hardware.enable_interactivity_async())
122
124
  if provider is not None:
123
125
  interactive_hardware.providers_combobox.value = provider
124
126
  if device is not None:
@@ -126,7 +128,7 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
126
128
 
127
129
  return interactive_hardware.show_interactive_graph()
128
130
 
129
- async def get_hardware_comparison_table_async(
131
+ def get_hardware_comparison_table(
130
132
  self,
131
133
  providers: Optional[Sequence[Union[str, AnalyzerProviderVendor]]] = None,
132
134
  devices: Optional[List[str]] = None,
@@ -148,10 +150,10 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
148
150
  devices=devices,
149
151
  transpilation_params=self.transpilation_params,
150
152
  )
151
- result = await ApiWrapper.call_table_graphs_task(params=params)
153
+ result = async_utils.run(ApiWrapper.call_table_graphs_task(params=params))
152
154
  self.hardware_comparison_table = go.Figure(json.loads(result.details))
153
155
 
154
- async def plot_hardware_comparison_table_async(
156
+ def plot_hardware_comparison_table(
155
157
  self,
156
158
  providers: Optional[List[Union[str, AnalyzerProviderVendor]]] = None,
157
159
  devices: Optional[List[str]] = None,
@@ -162,12 +164,10 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
162
164
  Returns:
163
165
  None.
164
166
  """
165
- await self._hardware_comparison_condition_async(
166
- providers=providers, devices=devices
167
- )
167
+ self._hardware_comparison_condition(providers=providers, devices=devices)
168
168
  self.hardware_comparison_table.show() # type: ignore[union-attr]
169
169
 
170
- async def _hardware_comparison_condition_async(
170
+ def _hardware_comparison_condition(
171
171
  self,
172
172
  providers: Optional[Sequence[Union[str, AnalyzerProviderVendor]]] = None,
173
173
  devices: Optional[List[str]] = None,
@@ -177,9 +177,7 @@ class Analyzer(AnalyzerUtilities, metaclass=Asyncify):
177
177
  or devices is not None
178
178
  or self.hardware_comparison_table is None
179
179
  ):
180
- await self.get_hardware_comparison_table_async(
181
- providers=providers, devices=devices
182
- )
180
+ self.get_hardware_comparison_table(providers=providers, devices=devices)
183
181
 
184
182
  @staticmethod
185
183
  def _open_route(path: str) -> None: