classiq 0.37.1__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.1.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
  221. {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
  222. {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
  223. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  224. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  225. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  226. classiq/interface/model/numeric_reinterpretation.py +0 -25
  227. classiq/interface/model/operator_synthesis_data.py +0 -48
  228. classiq/model/function_handler.pyi +0 -152
@@ -1,4 +1,4 @@
1
- from typing import List
1
+ from typing import Any, Dict, List
2
2
 
3
3
  import numpy as np
4
4
  import pydantic
@@ -11,6 +11,8 @@ from classiq.interface.generator.function_params import (
11
11
  DEFAULT_OUTPUT_NAME,
12
12
  )
13
13
 
14
+ from classiq.exceptions import ClassiqValueError
15
+
14
16
 
15
17
  class StatePropagator(function_params.FunctionParams):
16
18
  """
@@ -33,16 +35,25 @@ class StatePropagator(function_params.FunctionParams):
33
35
  )
34
36
 
35
37
  @pydantic.validator("start_state_vector", always=True)
36
- def validate_start_state(cls, start_state_vector, values):
38
+ def validate_start_state(
39
+ cls, start_state_vector: List[Complex], values: Dict[str, Any]
40
+ ) -> List[Complex]:
37
41
  end_state_vector = values.get("end_state_vector")
42
+ if end_state_vector is None:
43
+ raise ClassiqValueError(
44
+ "Cannot validate start_start_vector without end_state_vector"
45
+ )
46
+
38
47
  num_qubits = cls._num_qubits(end_state_vector)
39
48
  if len(start_state_vector) == 0:
40
- default_start_state_vector = [0.0 for _ in range(2**num_qubits)]
41
- default_start_state_vector[0] = 1.0
49
+ default_start_state_vector = [Complex(0.0) for _ in range(2**num_qubits)]
50
+ default_start_state_vector[0] = Complex(1.0)
42
51
  start_state_vector = default_start_state_vector
43
52
 
44
53
  if len(start_state_vector) != len(end_state_vector):
45
- raise ValueError("Start and end state vectors are of non-equal length")
54
+ raise ClassiqValueError(
55
+ "Start and end state vectors are of non-equal length"
56
+ )
46
57
 
47
58
  return start_state_vector
48
59
 
@@ -1,2 +1 @@
1
1
  from .pauli_struct_declarations import * # noqa: F403
2
- from .qaoa_declarations import * # noqa: F403
@@ -15,11 +15,16 @@ class StructDeclaration(HashablePydanticBaseModel):
15
15
 
16
16
  variables: Dict[str, ConcreteClassicalType] = pydantic.Field(
17
17
  default_factory=dict,
18
- description="Dictionary of variable names and their classical type",
18
+ description="Dictionary of variable names and their classical types",
19
19
  )
20
20
 
21
21
  BUILTIN_STRUCT_DECLARATIONS: ClassVar[Dict[str, "StructDeclaration"]] = {}
22
22
 
23
23
  def validate_fields(self, fields: Mapping[str, Any]) -> None:
24
- if set(self.variables.keys()) != {field for field in fields.keys()}:
25
- raise ClassiqValueError(f"Invalid fields for {self.name} instance")
24
+ expected_field_names = list(self.variables.keys())
25
+ received_field_names = list(fields.keys())
26
+ if set(expected_field_names) != set(received_field_names):
27
+ raise ClassiqValueError(
28
+ f"Invalid fields for {self.name} instance. Expected fields "
29
+ f"{expected_field_names}, got {received_field_names}"
30
+ )
@@ -10,6 +10,8 @@ from classiq.interface.generator.excitations import (
10
10
  EXCITATIONS_TYPE_EXACT,
11
11
  )
12
12
 
13
+ from classiq.exceptions import ClassiqValueError
14
+
13
15
  _EXCITATIONS_DICT = {"s": 1, "d": 2, "t": 3, "q": 4}
14
16
 
15
17
  DEFAULT_EXCITATIONS = [1, 2]
@@ -44,7 +46,7 @@ class UCC(ChemistryFunctionParams):
44
46
  def _validate_excitations(cls, excitations: EXCITATIONS_TYPE) -> EXCITATIONS_TYPE:
45
47
  if isinstance(excitations, int):
46
48
  if excitations not in _EXCITATIONS_DICT.values():
47
- raise ValueError(
49
+ raise ClassiqValueError(
48
50
  f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
49
51
  )
50
52
  excitations = [excitations]
@@ -53,19 +55,19 @@ class UCC(ChemistryFunctionParams):
53
55
  excitations = list(excitations) # type: ignore[assignment]
54
56
  if all(isinstance(idx, int) for idx in excitations):
55
57
  if any(idx not in _EXCITATIONS_DICT.values() for idx in excitations):
56
- raise ValueError(
58
+ raise ClassiqValueError(
57
59
  f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
58
60
  )
59
61
 
60
62
  elif all(isinstance(idx, str) for idx in excitations):
61
63
  if any(idx not in _EXCITATIONS_DICT.keys() for idx in excitations):
62
- raise ValueError(
64
+ raise ClassiqValueError(
63
65
  f"possible values of excitations are {list(_EXCITATIONS_DICT.keys())}"
64
66
  )
65
67
  excitations = sorted(_EXCITATIONS_DICT[idx] for idx in excitations) # type: ignore[index]
66
68
 
67
69
  else:
68
- raise ValueError(
70
+ raise ClassiqValueError(
69
71
  "excitations must be of the same type (all str or all int)"
70
72
  )
71
73
  return excitations
@@ -6,6 +6,8 @@ import pydantic
6
6
  from classiq.interface.generator import complex_type, function_params
7
7
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
8
8
 
9
+ from classiq.exceptions import ClassiqValueError
10
+
9
11
  DataNumber = Union[complex_type.Complex, float, int]
10
12
  DataArray = List[List[DataNumber]]
11
13
 
@@ -28,11 +30,13 @@ class UnitaryGate(function_params.FunctionParams):
28
30
  def validate_data(cls, data: DataArray) -> DataArray:
29
31
  data_np = np.array(data, dtype=object)
30
32
  if data_np.ndim != 2:
31
- raise ValueError("Data must me two dimensional")
33
+ raise ClassiqValueError("Data must me two dimensional")
32
34
  if data_np.shape[0] != data_np.shape[1]:
33
- raise ValueError("Matrix must be square")
35
+ raise ClassiqValueError("Matrix must be square")
34
36
  if not np.mod(np.log2(data_np.shape[0]), 1) == 0:
35
- raise ValueError("Matrix dimensions must be an integer exponent of 2")
37
+ raise ClassiqValueError(
38
+ "Matrix dimensions must be an integer exponent of 2"
39
+ )
36
40
  return data
37
41
 
38
42
  @property
@@ -11,6 +11,8 @@ from classiq.interface.generator.quantum_function_call import (
11
11
  )
12
12
  from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
13
13
 
14
+ from classiq.exceptions import ClassiqValueError
15
+
14
16
  IO_MULTI_USE_ERROR_MSG = "Input and output names can only be used once"
15
17
  UNCONNECTED_WIRES_ERROR_MSG = "Wires connected only on one end"
16
18
  UNCONNECTED_FLOW_IO_ERROR_MSG = "Flow IOs not connected to inner calls"
@@ -40,7 +42,7 @@ def _parse_call_inputs(
40
42
  wire = wires[wire_name]
41
43
 
42
44
  if wire.end:
43
- raise ValueError(
45
+ raise ClassiqValueError(
44
46
  IO_MULTI_USE_ERROR_MSG
45
47
  + f". The name {wire_name} is used multiple times."
46
48
  )
@@ -62,7 +64,7 @@ def _parse_call_outputs(
62
64
  wire = wires[wire_name]
63
65
 
64
66
  if wire.start:
65
- raise ValueError(
67
+ raise ClassiqValueError(
66
68
  IO_MULTI_USE_ERROR_MSG
67
69
  + f". The name {wire_name} is used multiple times."
68
70
  )
@@ -148,7 +150,7 @@ def validate_legal_wiring(
148
150
  if unconnected_wires:
149
151
  error_messages.append(f"{UNCONNECTED_WIRES_ERROR_MSG}: {unconnected_wires}")
150
152
 
151
- raise ValueError(_join_errors(error_messages))
153
+ raise ClassiqValueError(_join_errors(error_messages))
152
154
 
153
155
 
154
156
  def _join_errors(error_messages: List[str]) -> str:
@@ -180,7 +182,7 @@ def validate_acyclic_logic_flow(
180
182
 
181
183
  if not nx.algorithms.is_directed_acyclic_graph(graph):
182
184
  cycles = list(nx.algorithms.simple_cycles(graph))
183
- raise ValueError(CYCLE_ERROR_MSG + ". Cycles are: " + str(cycles))
185
+ raise ClassiqValueError(CYCLE_ERROR_MSG + ". Cycles are: " + str(cycles))
184
186
 
185
187
  return graph
186
188
 
@@ -4,6 +4,8 @@ import numpy as np
4
4
 
5
5
  from classiq.interface.helpers.custom_pydantic_types import PydanticProbabilityFloat
6
6
 
7
+ from classiq.exceptions import ClassiqValueError
8
+
7
9
  NOT_SUM_TO_ONE_ERROR = "Probabilities do not sum to 1"
8
10
 
9
11
  SUM_TO_ONE_SENSITIVITY = 8
@@ -26,9 +28,9 @@ def is_probabilities_sum_to_one(pro: Iterable[PydanticProbabilityFloat]) -> bool
26
28
 
27
29
  def validate_amplitudes(amp: Amplitude) -> Amplitude:
28
30
  if not is_amplitudes_sum_to_one(amp):
29
- raise ValueError("Amplitudes do not sum to 1")
31
+ raise ClassiqValueError("Amplitudes do not sum to 1")
30
32
  if not _is_power_of_two(amp):
31
- raise ValueError("Amplitudes length must be power of 2")
33
+ raise ClassiqValueError("Amplitudes length must be power of 2")
32
34
  return amp
33
35
 
34
36
 
@@ -36,7 +38,7 @@ def validate_probabilities(
36
38
  cls: type, pmf: Sequence[PydanticProbabilityFloat]
37
39
  ) -> Sequence[PydanticProbabilityFloat]:
38
40
  if not is_probabilities_sum_to_one(pmf):
39
- raise ValueError(NOT_SUM_TO_ONE_ERROR)
41
+ raise ClassiqValueError(NOT_SUM_TO_ONE_ERROR)
40
42
  if not _is_power_of_two(pmf):
41
- raise ValueError("Probabilities length must be power of 2")
43
+ raise ClassiqValueError("Probabilities length must be power of 2")
42
44
  return pmf
@@ -17,8 +17,8 @@ class Provider(StrEnum):
17
17
  OQC = "OQC"
18
18
 
19
19
  @property
20
- def id(self):
21
- return self.value.replace(" ", "-").lower()
20
+ def id(self) -> "ProviderIDEnum":
21
+ return self.value.replace(" ", "-").lower() # type: ignore[return-value]
22
22
 
23
23
 
24
24
  ProviderIDEnum = StrEnum("ProviderIDEnum", {p.id: p.id for p in Provider}) # type: ignore[misc]
@@ -0,0 +1,3 @@
1
+ from typing import Any, Callable, Dict
2
+
3
+ CUSTOM_ENCODERS: Dict[type, Callable[[Any], Any]] = {complex: str}
@@ -1,11 +1,5 @@
1
1
  from typing import Any, Dict, Protocol, Sequence, TypeVar
2
2
 
3
- import pydantic
4
-
5
-
6
- def get_discriminator_field(default: str, **kwargs: Any) -> Any:
7
- return pydantic.Field(default_factory=lambda: default, **kwargs)
8
-
9
3
 
10
4
  def values_with_discriminator(
11
5
  values: Dict[str, Any], discriminator: str, discriminator_value: Any
@@ -12,7 +12,7 @@ def is_list_unique(lst: List[Hashable]) -> bool:
12
12
  def validate_nameables_mapping(
13
13
  nameables_dict: Mapping[str, Nameable], declaration_type: str
14
14
  ) -> None:
15
- if not all([name == nameable.name for (name, nameable) in nameables_dict.items()]):
15
+ if not all(name == nameable.name for (name, nameable) in nameables_dict.items()):
16
16
  raise ClassiqValueError(
17
17
  f"{declaration_type} declaration names should match the keys of their names."
18
18
  )
@@ -1,7 +1,10 @@
1
1
  import pydantic
2
2
 
3
3
  from classiq.interface._version import VERSION
4
+ from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
4
5
 
5
6
 
6
- class VersionedModel(pydantic.BaseModel, extra=pydantic.Extra.forbid):
7
+ class VersionedModel(
8
+ pydantic.BaseModel, extra=pydantic.Extra.forbid, json_encoders=CUSTOM_ENCODERS
9
+ ):
7
10
  version: str = pydantic.Field(default=VERSION)
@@ -4,7 +4,7 @@ import pydantic
4
4
 
5
5
  from classiq.interface.analyzer.result import QasmCode
6
6
 
7
- from classiq import GeneratedCircuit
7
+ from classiq import QuantumProgram
8
8
  from classiq._internals.api_wrapper import ApiWrapper
9
9
  from classiq._internals.async_utils import syncify_function
10
10
  from classiq.exceptions import ClassiqValueError
@@ -28,7 +28,7 @@ CANT_PARSE_QUANTUM_PROGRAM_MSG = (
28
28
 
29
29
  def show(quantum_program: SerializedQuantumProgram) -> None:
30
30
  try:
31
- circuit = GeneratedCircuit.parse_raw(quantum_program)
31
+ circuit = QuantumProgram.parse_raw(quantum_program)
32
32
  except pydantic.error_wrappers.ValidationError as exc:
33
33
  raise ClassiqValueError(CANT_PARSE_QUANTUM_PROGRAM_MSG) from exc
34
34
  circuit.show() # type: ignore[attr-defined]
classiq/interface/jobs.py CHANGED
@@ -1,9 +1,11 @@
1
- from typing import Any, Dict, Generic, TypeVar, Union
1
+ from typing import Any, Dict, Generic, Literal, TypeVar, Union
2
2
 
3
3
  import pydantic
4
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, Field
5
5
  from pydantic.generics import GenericModel
6
6
 
7
+ from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
8
+
7
9
  from classiq._internals.enum_utils import StrEnum
8
10
 
9
11
  JSONObject = Dict[str, Any]
@@ -29,6 +31,73 @@ class JobStatus(StrEnum):
29
31
  return self in (self.COMPLETED, self.FAILED, self.CANCELLED)
30
32
 
31
33
 
32
- class JobDescription(GenericModel, Generic[T]):
34
+ """
35
+ A job can be in either of 3 states: ongoing, completed successfully or completed
36
+ unsuccessfully. Each job status belongs to one of the 3 states
37
+ The class JobDescriptionBase represents a job description, regardless of its state
38
+ JobDescriptionSuccess represents a job that was completed successfully. It contains the
39
+ job result in the description field. The type of the result depends on the route, and
40
+ so it's defined as a generic class
41
+ JobDescriptionFailure represents a job that was completed unsuccessfully. It contains
42
+ the failure details (i.e., error message) in the description field
43
+ JobDescriptionNonFinal represents a job that has not terminated yet. It does not contain
44
+ any additional information
45
+ JobDescriptionUnion is used to define a discriminator field between the 3 states. Since
46
+ JobDescriptionSuccess is generic, so is the union. This means it cannot be defined
47
+ as an annotated type alias (that is, we cannot define
48
+ JobDescriptionUnion = Annotated[Union[...], Field(discriminator="kind")])
49
+ """
50
+
51
+ SuccessStatus = Literal[JobStatus.COMPLETED]
52
+ FailureStatus = Union[
53
+ Literal[JobStatus.FAILED],
54
+ Literal[JobStatus.CANCELLED],
55
+ ]
56
+ NonFinalStatus = Union[
57
+ Literal[JobStatus.QUEUED],
58
+ Literal[JobStatus.RUNNING],
59
+ Literal[JobStatus.READY],
60
+ Literal[JobStatus.CANCELLING],
61
+ Literal[JobStatus.UNKNOWN],
62
+ ]
63
+
64
+
65
+ class FailureDetails(BaseModel):
66
+ details: str
67
+
68
+
69
+ class JobDescriptionBase(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
70
+ kind: str
33
71
  status: JobStatus
72
+ description: Union[T, FailureDetails, Dict]
73
+
74
+
75
+ class JobDescriptionSuccess(JobDescriptionBase[T], Generic[T]):
76
+ kind: Literal["success"] = pydantic.Field(default="success")
77
+ status: SuccessStatus = Field(default=JobStatus.COMPLETED)
34
78
  description: T
79
+
80
+
81
+ class JobDescriptionFailure(JobDescriptionBase[Any]):
82
+ kind: Literal["failure"] = pydantic.Field(default="failure")
83
+ status: FailureStatus
84
+ description: FailureDetails
85
+
86
+
87
+ class JobDescriptionNonFinal(JobDescriptionBase[Any]):
88
+ kind: Literal["non_final"] = pydantic.Field(default="non_final")
89
+ status: NonFinalStatus
90
+ description: Dict = Field(default_factory=dict)
91
+
92
+ @pydantic.validator("description")
93
+ def validate_empty_description(cls, description: Dict) -> Dict:
94
+ if description:
95
+ raise ValueError("Non-final job description must be empty")
96
+
97
+ return description
98
+
99
+
100
+ class JobDescriptionUnion(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
101
+ __root__: Union[
102
+ JobDescriptionSuccess[T], JobDescriptionFailure, JobDescriptionNonFinal
103
+ ] = Field(discriminator="kind")
@@ -1,4 +1,4 @@
1
- from typing import Mapping
1
+ from typing import List, Mapping
2
2
 
3
3
  import pydantic
4
4
 
@@ -12,19 +12,26 @@ BIND_OUTPUT_NAME = "bind_output"
12
12
 
13
13
 
14
14
  class BindOperation(QuantumOperation):
15
- in_handle: HandleBinding
16
- out_handle: HandleBinding
15
+ in_handles: List[HandleBinding]
16
+ out_handles: List[HandleBinding]
17
17
 
18
18
  @property
19
19
  def wiring_inputs(self) -> Mapping[str, HandleBinding]:
20
- return {BIND_INPUT_NAME: self.in_handle}
20
+ return {
21
+ f"{BIND_INPUT_NAME}_{i}": handle for i, handle in enumerate(self.in_handles)
22
+ }
21
23
 
22
24
  @property
23
25
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
24
- return {BIND_OUTPUT_NAME: self.out_handle}
25
-
26
- @pydantic.validator("in_handle", "out_handle")
27
- def validate_handle(cls, handle: HandleBinding) -> HandleBinding:
28
- if not handle.is_bindable():
29
- raise ClassiqValueError(f"Cannot bind '{handle}'") # noqa: B907
30
- return handle
26
+ return {
27
+ f"{BIND_OUTPUT_NAME}_{i}": handle
28
+ for i, handle in enumerate(self.out_handles)
29
+ }
30
+
31
+ @pydantic.validator("in_handles", "out_handles")
32
+ def validate_handle(cls, handles: List[HandleBinding]) -> List[HandleBinding]:
33
+ for handle in handles:
34
+ if not handle.is_bindable():
35
+ raise ClassiqValueError(f"Cannot bind '{handle}'")
36
+
37
+ return handles
@@ -0,0 +1,68 @@
1
+ import dataclasses
2
+ from typing import Any, List, Mapping
3
+
4
+ from sympy import sympify
5
+
6
+ from classiq.interface.generator.control_state import ControlState
7
+ from classiq.interface.generator.synthesis_execution_parameter import PydanticPowerType
8
+
9
+ from classiq.exceptions import ClassiqValueError
10
+
11
+ ILLEGAL_NESTED_POWER_ERROR = "Nested power calls with a parametric power and an integer power are unsupported: {a}, {b}"
12
+
13
+
14
+ def _merge_power(a: PydanticPowerType, b: PydanticPowerType) -> PydanticPowerType:
15
+ symbolic_res = sympify(a) * sympify(b)
16
+ if symbolic_res.is_Integer:
17
+ return int(symbolic_res)
18
+ elif symbolic_res.is_symbol:
19
+ return str(symbolic_res)
20
+ else:
21
+ raise ClassiqValueError(ILLEGAL_NESTED_POWER_ERROR.format(a=a, b=b))
22
+
23
+
24
+ @dataclasses.dataclass
25
+ class CallSynthesisData(Mapping):
26
+ power: PydanticPowerType = 1
27
+ is_inverse: bool = False
28
+ control_states: List[ControlState] = dataclasses.field(default_factory=list)
29
+ should_control: bool = True
30
+
31
+ def merge(self, other: "CallSynthesisData") -> "CallSynthesisData":
32
+ return CallSynthesisData(
33
+ power=_merge_power(self.power, other.power),
34
+ is_inverse=self.is_inverse != other.is_inverse,
35
+ control_states=self.control_states + other.control_states,
36
+ should_control=self.should_control and other.should_control,
37
+ )
38
+
39
+ def set_control(self, ctrl_name: str, ctrl_size: int) -> None:
40
+ self.control_states = [
41
+ ControlState(
42
+ name=ctrl_name,
43
+ num_ctrl_qubits=ctrl_size,
44
+ )
45
+ ]
46
+
47
+ def update_control_state(self, ctrl_size: int, ctrl_state: str) -> None:
48
+ prev_ctrl_state = self.control_states.pop()
49
+ self.control_states.append(
50
+ ControlState(
51
+ name=prev_ctrl_state.name,
52
+ num_ctrl_qubits=ctrl_size,
53
+ ctrl_state=ctrl_state,
54
+ )
55
+ )
56
+
57
+ @property
58
+ def has_control(self) -> bool:
59
+ return bool(self.control_states)
60
+
61
+ def __getitem__(self, key: str) -> Any:
62
+ return dataclasses.asdict(self)[key]
63
+
64
+ def __iter__(self):
65
+ return iter(dataclasses.asdict(self))
66
+
67
+ def __len__(self):
68
+ return len(dataclasses.asdict(self))
@@ -11,8 +11,8 @@ from classiq.exceptions import ClassiqValueError
11
11
 
12
12
 
13
13
  class BinaryOperation(StrEnum):
14
- Addition = "addition"
15
- Xor = "xor"
14
+ Addition = "inplace_add"
15
+ Xor = "inplace_xor"
16
16
 
17
17
  @property
18
18
  def internal_function(self) -> str:
@@ -1,4 +1,5 @@
1
- from typing import Any, Dict, List, Literal, NewType, Optional, Set, Union
1
+ from collections import Counter
2
+ from typing import Any, Dict, List, Literal, NewType, Optional, Set
2
3
 
3
4
  import pydantic
4
5
 
@@ -10,23 +11,17 @@ from classiq.interface.generator.functions.port_declaration import (
10
11
  from classiq.interface.generator.model.constraints import Constraints
11
12
  from classiq.interface.generator.model.preferences.preferences import Preferences
12
13
  from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
13
- from classiq.interface.generator.types.combinatorial_problem import (
14
- CombinatorialOptimizationStructDeclaration,
15
- )
16
14
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
17
- from classiq.interface.helpers.pydantic_model_helpers import (
18
- get_discriminator_field,
19
- nameables_to_dict,
20
- )
15
+ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
16
  from classiq.interface.helpers.versioned_model import VersionedModel
22
17
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
23
- from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
24
18
  from classiq.interface.model.quantum_function_declaration import (
25
19
  QuantumFunctionDeclaration,
26
20
  )
27
21
  from classiq.interface.model.resolvers.function_call_resolver import (
28
22
  resolve_function_calls,
29
23
  )
24
+ from classiq.interface.model.statement_block import StatementBlock
30
25
 
31
26
  from classiq.exceptions import ClassiqValueError
32
27
 
@@ -39,10 +34,6 @@ DEFAULT_PORT_SIZE = 1
39
34
 
40
35
  SerializedModel = NewType("SerializedModel", str)
41
36
 
42
- ConcreteStructDeclaration = Union[
43
- CombinatorialOptimizationStructDeclaration, StructDeclaration
44
- ]
45
-
46
37
  TYPE_NAME_CONFLICT_BUILTIN = (
47
38
  "Type '{name}' conflicts with a builtin type with the same name"
48
39
  )
@@ -65,7 +56,7 @@ class Model(VersionedModel):
65
56
  All the relevant data for generating quantum circuit in one place.
66
57
  """
67
58
 
68
- kind: Literal["user"] = get_discriminator_field(USER_MODEL_MARKER)
59
+ kind: Literal["user"] = pydantic.Field(default=USER_MODEL_MARKER)
69
60
 
70
61
  # Must be validated before logic_flow
71
62
  functions: List[NativeFunctionDefinition] = pydantic.Field(
@@ -73,7 +64,7 @@ class Model(VersionedModel):
73
64
  description="The user-defined custom type library.",
74
65
  )
75
66
 
76
- types: List[ConcreteStructDeclaration] = pydantic.Field(
67
+ types: List[StructDeclaration] = pydantic.Field(
77
68
  default_factory=list,
78
69
  description="The user-defined custom function library.",
79
70
  )
@@ -98,7 +89,7 @@ class Model(VersionedModel):
98
89
  return self.function_dict[MAIN_FUNCTION_NAME] # type:ignore[return-value]
99
90
 
100
91
  @property
101
- def body(self) -> List[ConcreteQuantumStatement]:
92
+ def body(self) -> StatementBlock:
102
93
  return self.main_func.body
103
94
 
104
95
  @pydantic.validator("preferences", always=True)
@@ -142,15 +133,15 @@ class Model(VersionedModel):
142
133
  return values
143
134
 
144
135
  @pydantic.validator("types")
145
- def types_validator(
146
- cls, types: List[ConcreteStructDeclaration]
147
- ) -> List[ConcreteStructDeclaration]:
136
+ def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
148
137
  user_defined_types: Set[str] = set()
149
138
  for ctype in types:
150
139
  if ctype.name in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS:
151
- raise ValueError(TYPE_NAME_CONFLICT_BUILTIN.format(name=ctype.name))
140
+ raise ClassiqValueError(
141
+ TYPE_NAME_CONFLICT_BUILTIN.format(name=ctype.name)
142
+ )
152
143
  if ctype.name in user_defined_types:
153
- raise ValueError(TYPE_NAME_CONFLICT_USER.format(name=ctype.name))
144
+ raise ClassiqValueError(TYPE_NAME_CONFLICT_USER.format(name=ctype.name))
154
145
  user_defined_types.add(ctype.name)
155
146
 
156
147
  return types
@@ -174,3 +165,18 @@ class Model(VersionedModel):
174
165
  raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
175
166
 
176
167
  return functions
168
+
169
+ @pydantic.validator("constants")
170
+ def _validate_constants(cls, constants: List[Constant]) -> List[Constant]:
171
+ constant_definition_counts = Counter(
172
+ [constant.name for constant in constants]
173
+ ).items()
174
+ multiply_defined_constants = {
175
+ constant for constant, count in constant_definition_counts if count > 1
176
+ }
177
+ if len(multiply_defined_constants) > 0:
178
+ raise ClassiqValueError(
179
+ f"The following constants were defined more than once: "
180
+ f"{multiply_defined_constants}"
181
+ )
182
+ return constants
@@ -1,11 +1,9 @@
1
- from typing import List
2
-
3
1
  import pydantic
4
2
 
5
- from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
6
3
  from classiq.interface.model.quantum_function_declaration import (
7
4
  QuantumFunctionDeclaration,
8
5
  )
6
+ from classiq.interface.model.statement_block import StatementBlock
9
7
  from classiq.interface.model.validations.handles_validator import HandleValidator
10
8
  from classiq.interface.model.variable_declaration_statement import (
11
9
  VariableDeclarationStatement,
@@ -22,7 +20,7 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
22
20
  objects from other classes.
23
21
  """
24
22
 
25
- body: List[ConcreteQuantumStatement] = pydantic.Field(
23
+ body: StatementBlock = pydantic.Field(
26
24
  default_factory=list, description="List of function calls to perform."
27
25
  )
28
26
 
@@ -33,6 +31,6 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
33
31
  if isinstance(statement, VariableDeclarationStatement):
34
32
  handle_validator.handle_variable_declaration(statement)
35
33
  else:
36
- handle_validator.handle_call(statement)
34
+ handle_validator.handle_operation(statement)
37
35
 
38
36
  handle_validator.report_errored_handles(ClassiqValueError)
@@ -36,9 +36,14 @@ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
36
36
  ) -> Mapping[
37
37
  str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
38
38
  ]:
39
- if len(self.var_handles) == 0:
40
- return dict()
41
- return {AMPLITUDE_IO_NAME: self.var_handles[0]}
39
+ inouts = {self.result_name(): self.result_var}
40
+ if len(self.var_handles) == 1:
41
+ inouts[AMPLITUDE_IO_NAME] = self.var_handles[0]
42
+ return inouts
43
+
44
+ @property
45
+ def wiring_outputs(self) -> Mapping[str, HandleBinding]:
46
+ return {}
42
47
 
43
48
  def initialize_var_types(
44
49
  self,
@@ -50,7 +55,7 @@ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
50
55
  var_type = var_types[self.var_handles[0].name]
51
56
  if not (
52
57
  isinstance(var_type, QuantumNumeric)
53
- and var_type.fraction_digits == var_type.size_in_bits
58
+ and var_type.fraction_digits_value == var_type.size_in_bits
54
59
  ):
55
60
  raise ClassiqValueError(VAR_DOMAIN_ILLEGAL)
56
61
  super().initialize_var_types(var_types, machine_precision)