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
@@ -0,0 +1,107 @@
1
+ import itertools
2
+ from dataclasses import dataclass, field
3
+ from typing import Dict, List, Optional, Union
4
+
5
+ import pyomo.environ as pyo
6
+ from pyomo.core.base import _GeneralVarData
7
+ from pyomo.core.expr.visitor import clone_expression, identify_variables
8
+
9
+ from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
10
+
11
+ from classiq.applications_model_constructors.combinatorial_helpers import pyomo_utils
12
+ from classiq.exceptions import ClassiqCombOptError
13
+
14
+
15
+ @dataclass
16
+ class VarExpressionMapping:
17
+ var: _GeneralVarData
18
+ expr: pyo.Expression
19
+ encodings_vars: List[_GeneralVarData] = field(default_factory=list)
20
+
21
+
22
+ class EncodingMapping:
23
+ def __init__(self, encoding_type: EncodingType) -> None:
24
+ self._data: List[VarExpressionMapping] = []
25
+ self.encoding_type = encoding_type
26
+
27
+ @property
28
+ def original_vars(self) -> List[_GeneralVarData]:
29
+ return [pair.var for pair in self._data]
30
+
31
+ @property
32
+ def encodings_vars(self) -> List[_GeneralVarData]:
33
+ return list(
34
+ itertools.chain.from_iterable(
35
+ var_mapping.encodings_vars for var_mapping in self._data
36
+ )
37
+ )
38
+
39
+ @property
40
+ def substitution_dict(self) -> Dict[int, pyo.Expression]:
41
+ return {id(mapping.var): mapping.expr for mapping in self._data}
42
+
43
+ def __len__(self) -> int:
44
+ return len(self._data)
45
+
46
+ def add(
47
+ self,
48
+ original_var: _GeneralVarData,
49
+ encoding_expr: pyo.Expression,
50
+ encodings_vars: Union[List[_GeneralVarData], None] = None,
51
+ ) -> None:
52
+ if encodings_vars is None:
53
+ encodings_vars = list(identify_variables(encoding_expr))
54
+
55
+ self._check_unique_encoding_vars(encodings_vars)
56
+ self._data.append(
57
+ VarExpressionMapping(
58
+ var=original_var, expr=encoding_expr, encodings_vars=encodings_vars
59
+ )
60
+ )
61
+
62
+ def _check_unique_encoding_vars(self, variables: List[_GeneralVarData]) -> None:
63
+ assert all(
64
+ not pyomo_utils.contains(var, self.encodings_vars) for var in variables
65
+ )
66
+
67
+ def get_var_expr_mapping(
68
+ self, original_var: _GeneralVarData
69
+ ) -> VarExpressionMapping:
70
+ for var_expr_mapping in self._data:
71
+ if var_expr_mapping.var is original_var:
72
+ return var_expr_mapping
73
+ raise ClassiqCombOptError("No variable expression mapping found.")
74
+
75
+ def get_encoding_vars(self, original_var: _GeneralVarData) -> List[_GeneralVarData]:
76
+ return self.get_var_expr_mapping(original_var).encodings_vars
77
+
78
+ def get_original_var(
79
+ self, encoding_var: _GeneralVarData
80
+ ) -> Optional[_GeneralVarData]:
81
+ for original_var in self.original_vars:
82
+ if pyomo_utils.contains(encoding_var, self.get_encoding_vars(original_var)):
83
+ return original_var
84
+ return None
85
+
86
+ def decode(self, solution: List[int]) -> List[int]:
87
+ idx = 0
88
+ decoded_solution = []
89
+ for var_mapping in self._data:
90
+ num_encoding_vars = len(var_mapping.encodings_vars)
91
+ encoding_vars_solution = solution[idx : idx + num_encoding_vars]
92
+ idx += num_encoding_vars
93
+
94
+ substitution_map = {
95
+ id(var): num
96
+ for var, num in zip(var_mapping.encodings_vars, encoding_vars_solution)
97
+ }
98
+ substituted_expr = clone_expression(
99
+ var_mapping.expr, substitute=substitution_map
100
+ )
101
+
102
+ if callable(substituted_expr):
103
+ substituted_expr = substituted_expr()
104
+
105
+ decoded_solution.append(substituted_expr)
106
+
107
+ return decoded_solution
@@ -0,0 +1,122 @@
1
+ import math
2
+ from itertools import filterfalse
3
+ from typing import Any, Dict, Union, cast
4
+
5
+ import numpy as np
6
+ import pyomo.environ as pyo
7
+ from pyomo.core.base import _GeneralVarData
8
+ from pyomo.core.base.component import ComponentData, _ComponentBase
9
+ from pyomo.core.base.constraint import _GeneralConstraintData
10
+ from pyomo.core.expr.numeric_expr import (
11
+ MonomialTermExpression,
12
+ ProductExpression,
13
+ clone_expression,
14
+ )
15
+ from sympy import Expr
16
+
17
+ from classiq.applications_model_constructors.combinatorial_helpers import pyomo_utils
18
+ from classiq.exceptions import ClassiqCombOptNoSolutionError
19
+
20
+ _INTEGER_TYPES = [pyo.NonNegativeIntegers, pyo.Integers, pyo.PositiveIntegers]
21
+
22
+
23
+ def is_model_encodable(model: pyo.ConcreteModel) -> bool:
24
+ variables = pyomo_utils.extract(model, pyo.Var)
25
+ return not all(is_var_binary(var) for var in variables)
26
+
27
+
28
+ def is_var_binary(var: _GeneralVarData) -> bool:
29
+ return var.domain == pyo.Binary or (
30
+ var.domain in _INTEGER_TYPES and var.lb == 0 and var.ub == 1
31
+ )
32
+
33
+
34
+ def is_var_span_power_of_2(var: _GeneralVarData) -> bool:
35
+ var_span = get_var_span(var)
36
+ return math.log2(var_span + 1).is_integer()
37
+
38
+
39
+ ENCODED_SUFFIX = "_encoded"
40
+ ONE_HOT_SUFFIX = "_one_hot"
41
+
42
+
43
+ def is_obj_encoded(var: _ComponentBase) -> bool:
44
+ return ENCODED_SUFFIX in var.name
45
+
46
+
47
+ def get_var_span(var: _GeneralVarData) -> int:
48
+ return var.ub - var.lb
49
+
50
+
51
+ def encoded_obj_name(name: str) -> str:
52
+ return name + ENCODED_SUFFIX
53
+
54
+
55
+ def get_encoded_var_index(var: _GeneralVarData) -> int:
56
+ indexed_var = var.parent_component()
57
+ index = [
58
+ index_temp for index_temp, var_temp in indexed_var.items() if var_temp is var
59
+ ][0]
60
+ return index[1]
61
+
62
+
63
+ def recursively_remove_monomial_expr(obj: Any) -> None:
64
+ # Due to pyomo bug. see: https://github.com/Pyomo/pyomo/issues/2174
65
+ for arg in getattr(obj, "args", []):
66
+ if isinstance(arg, MonomialTermExpression):
67
+ arg.__class__ = ProductExpression
68
+ recursively_remove_monomial_expr(arg)
69
+
70
+
71
+ def encode_expr(
72
+ expr: pyo.Expression, substitution_dict: Dict[int, pyo.Expression]
73
+ ) -> pyo.Expression:
74
+ encoded_expr = clone_expression(expr=expr, substitute=substitution_dict)
75
+ recursively_remove_monomial_expr(encoded_expr)
76
+ return encoded_expr
77
+
78
+
79
+ def encode_constraints(
80
+ model: pyo.ConcreteModel, substitution_dict: Dict[int, pyo.Expression]
81
+ ) -> None:
82
+ all_constraints = pyomo_utils.extract(model, _GeneralConstraintData)
83
+ constraints = filterfalse(is_obj_encoded, all_constraints)
84
+
85
+ for constraint in constraints:
86
+ constraint_expression = encode_expr(constraint.expr, substitution_dict)
87
+ if not isinstance(constraint_expression, bool) and not isinstance(
88
+ constraint_expression, np.bool_
89
+ ):
90
+ constraint.set_value(expr=constraint_expression)
91
+ continue
92
+
93
+ deal_with_trivial_boolean_constraint(constraint, constraint_expression, model)
94
+
95
+
96
+ def deal_with_trivial_boolean_constraint(
97
+ constraint: _ComponentBase,
98
+ constraint_expression: Union[bool, Expr],
99
+ model: pyo.ConcreteModel,
100
+ ) -> None:
101
+ # using '==' on purpose since comparing against sympy's True
102
+ if constraint_expression == True: # noqa: E712
103
+ pyomo_utils.delete_component(model, cast(ComponentData, constraint))
104
+ if constraint_expression == False: # noqa: E712
105
+ raise ClassiqCombOptNoSolutionError
106
+
107
+
108
+ def encode_objective(
109
+ model: pyo.ConcreteModel, substitution_dict: Dict[int, pyo.Expression]
110
+ ) -> None:
111
+ objective = next(model.component_objects(pyo.Objective))
112
+
113
+ encoded_objective = pyo.Objective(
114
+ expr=encode_expr(objective.expr, substitution_dict),
115
+ sense=objective.sense,
116
+ )
117
+ model.del_component(objective.name)
118
+ setattr(
119
+ model,
120
+ objective.name,
121
+ encoded_objective,
122
+ )
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, List, Tuple
4
+
5
+ from pyomo.core.base import _GeneralVarData
6
+
7
+ from classiq.interface.generator.function_params import IOName
8
+
9
+ from classiq.applications_model_constructors.combinatorial_helpers import encoding_utils
10
+ from classiq.applications_model_constructors.combinatorial_helpers.encoding_mapping import (
11
+ EncodingMapping,
12
+ )
13
+
14
+ AUXILIARY_NAME = "auxiliary"
15
+
16
+
17
+ class InternalQuantumReg:
18
+ def __init__(self, size: int, name: str) -> None:
19
+ self.name = name
20
+ self.size = size
21
+
22
+
23
+ class MemoryMapping:
24
+ def __init__(
25
+ self,
26
+ variables: List[_GeneralVarData],
27
+ vars_encoding_mapping: EncodingMapping | None = None,
28
+ ) -> None:
29
+ self.substitution_dict: Dict[int, InternalQuantumReg] = dict()
30
+ self.qubit_allocation: Dict[IOName, Tuple[int, int]] = dict()
31
+ self.vars_encoding_mapping: EncodingMapping | None = vars_encoding_mapping
32
+ self.vars: List[_GeneralVarData] = variables
33
+ self._allocate_memory()
34
+
35
+ def __len__(self) -> int:
36
+ return len(self.substitution_dict)
37
+
38
+ def _allocate_memory(self) -> None:
39
+ for var_data in self.vars:
40
+ if (
41
+ not self.vars_encoding_mapping
42
+ or encoding_utils.get_var_span(var_data) == 1
43
+ ):
44
+ num_qubits_var = 1
45
+ else:
46
+ num_qubits_var = len(
47
+ self.vars_encoding_mapping.get_encoding_vars(var_data)
48
+ )
49
+
50
+ self.substitution_dict[id(var_data)] = InternalQuantumReg(
51
+ num_qubits_var, "q"
52
+ )
53
+ var_name = get_var_name(var_data)
54
+ self.qubit_allocation[var_name] = (
55
+ self._variable_qubits_allocated,
56
+ num_qubits_var,
57
+ )
58
+
59
+ @property
60
+ def qregs(self) -> List[InternalQuantumReg]:
61
+ return list(self.substitution_dict.values())
62
+
63
+ @property
64
+ def _variable_qubits_allocated(self) -> int:
65
+ return sum(
66
+ num_qubits
67
+ for name, (_, num_qubits) in self.qubit_allocation.items()
68
+ if name != AUXILIARY_NAME
69
+ )
70
+
71
+
72
+ def get_var_name(var: _GeneralVarData) -> str:
73
+ return (
74
+ var.name.replace("[", "")
75
+ .replace("]", "")
76
+ .replace(",", "")
77
+ .replace("'", "")
78
+ .replace('"', "")
79
+ )
@@ -0,0 +1,34 @@
1
+ from classiq.interface.generator.function_params import DEFAULT_OUTPUT_NAME
2
+ from classiq.interface.generator.functions import SynthesisNativeFunctionDefinition
3
+ from classiq.interface.generator.state_preparation import (
4
+ ComputationalBasisStatePreparation,
5
+ )
6
+
7
+ from classiq import FunctionGenerator, QReg
8
+
9
+ BinaryStr = str
10
+ Name = str
11
+
12
+
13
+ def multiple_comp_basis_sp(
14
+ function_name: str, states: dict[Name, BinaryStr], call_name_frmt: str = "{}"
15
+ ) -> SynthesisNativeFunctionDefinition:
16
+ generator = FunctionGenerator(function_name=function_name)
17
+ qregs_dict = {
18
+ var_name: QReg[len(state)] # type:ignore[misc]
19
+ for var_name, state in states.items()
20
+ }
21
+ qregs = generator.create_inputs(qregs_dict)
22
+
23
+ for var_name, state in states.items():
24
+ params = ComputationalBasisStatePreparation(computational_state=state)
25
+ qregs[var_name] = generator.ComputationalBasisStatePreparation(
26
+ params,
27
+ in_wires=qregs[var_name],
28
+ strict_zero_ios=False,
29
+ call_name=call_name_frmt.format(var_name),
30
+ )[DEFAULT_OUTPUT_NAME]
31
+
32
+ generator.set_outputs(qregs)
33
+
34
+ return generator.to_function_definition()
@@ -0,0 +1,166 @@
1
+ import copy
2
+ from itertools import filterfalse
3
+ from typing import List, Optional, Union
4
+
5
+ import pyomo.environ as pyo
6
+ from pyomo.core import ConcreteModel
7
+ from pyomo.core.base import _GeneralVarData
8
+ from pyomo.core.base.constraint import _GeneralConstraintData
9
+ from pyomo.environ import Expression
10
+
11
+ from classiq.interface.chemistry.operator import PauliOperator
12
+ from classiq.interface.combinatorial_optimization import sense
13
+ from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
14
+ from classiq.interface.combinatorial_optimization.solver_types import QSolver
15
+
16
+ from classiq.applications_model_constructors.combinatorial_helpers import (
17
+ encoding_utils,
18
+ memory,
19
+ pyomo_utils,
20
+ )
21
+ from classiq.applications_model_constructors.combinatorial_helpers.encoding_mapping import (
22
+ EncodingMapping,
23
+ )
24
+ from classiq.applications_model_constructors.combinatorial_helpers.memory import (
25
+ InternalQuantumReg,
26
+ )
27
+ from classiq.applications_model_constructors.combinatorial_helpers.transformations import (
28
+ encoding,
29
+ ising_converter,
30
+ penalty,
31
+ slack_variables,
32
+ )
33
+ from classiq.applications_model_constructors.combinatorial_helpers.transformations.fixed_variables import (
34
+ add_fixed_variables_to_solution,
35
+ remove_fixed_variables,
36
+ )
37
+ from classiq.applications_model_constructors.combinatorial_helpers.transformations.penalty_support import (
38
+ is_model_penalty_supported,
39
+ )
40
+ from classiq.exceptions import ClassiqCombOptError
41
+
42
+
43
+ class OptimizationModel:
44
+ def __init__(
45
+ self,
46
+ model: ConcreteModel,
47
+ qsolver: QSolver,
48
+ penalty_energy: Optional[float],
49
+ encoding_type: Optional[EncodingType] = None,
50
+ ) -> None:
51
+ assert model.nobjectives() == 1, "model must have a single objective"
52
+ self._model_original = copy.deepcopy(model)
53
+ self._assigned_model = remove_fixed_variables(model)
54
+ self.qsolver = qsolver
55
+ self._encoding_type = encoding_type
56
+
57
+ self.is_encoded = encoding_utils.is_model_encodable(model)
58
+ if self.is_encoded:
59
+ if self._encoding_type is None:
60
+ self._encoding_type = EncodingType.BINARY
61
+ self._model_encoder = encoding.ModelEncoder(
62
+ model, qsolver, self._encoding_type
63
+ )
64
+ self._model = self._model_encoder.encoded_model
65
+ self._vars_encoding_mapping = self._model_encoder.vars_encoding_mapping
66
+ else:
67
+ self._model = model
68
+ # TODO How to handle encoding_type == None
69
+ self._vars_encoding_mapping = EncodingMapping(self._encoding_type) # type: ignore[arg-type]
70
+
71
+ self._slack_vars_convert()
72
+
73
+ self.memory_mapping = memory.MemoryMapping(
74
+ self.vars_not_encoded, self._vars_encoding_mapping
75
+ )
76
+
77
+ self.is_maximization = sense.is_maximization(model)
78
+ self.objective = next(self._model.component_objects(pyo.Objective))
79
+
80
+ self.penalty_energy = penalty_energy
81
+ self._add_penalty_term()
82
+
83
+ self.ising: PauliOperator = self._to_ising()
84
+
85
+ @property
86
+ def vars(self) -> List[_GeneralVarData]:
87
+ return pyomo_utils.extract(self._model, _GeneralVarData)
88
+
89
+ @property
90
+ def vars_not_encoded(self) -> List[_GeneralVarData]:
91
+ return list(filterfalse(encoding_utils.is_obj_encoded, self.vars))
92
+
93
+ @property
94
+ def _ising_vars(self) -> List[_GeneralVarData]:
95
+ if self.is_encoded:
96
+ return [
97
+ var
98
+ for var in self.vars
99
+ if encoding_utils.is_obj_encoded(var)
100
+ or slack_variables.is_obj_slacked(var)
101
+ ]
102
+ else:
103
+ return self.vars
104
+
105
+ @property
106
+ def constraints(self) -> List[_GeneralConstraintData]:
107
+ all_constraints = pyomo_utils.extract(self._model, _GeneralConstraintData)
108
+ return list(filterfalse(encoding_utils.is_obj_encoded, all_constraints))
109
+
110
+ @property
111
+ def qregs(self) -> List[InternalQuantumReg]:
112
+ return self.memory_mapping.qregs
113
+
114
+ @property
115
+ def num_qubits(self) -> int:
116
+ return sum(qreg.size for qreg in self.qregs)
117
+
118
+ @property
119
+ def sign(self) -> int:
120
+ return -1 if self.is_maximization else 1
121
+
122
+ def _add_penalty_term(self) -> None:
123
+ if self.qsolver == QSolver.QAOAPenalty:
124
+ self.objective.expr += self._get_penalty_term()
125
+
126
+ def _get_penalty_term(self) -> Union[int, Expression]:
127
+ normalized_penalty_term = penalty.get_penalty_expression(self.constraints)
128
+ penalty_term = self.penalty_energy * normalized_penalty_term * self.sign
129
+
130
+ if self.is_encoded:
131
+ penalty_term = self._model_encoder.encode_expr(penalty_term)
132
+ return penalty_term
133
+
134
+ def _to_ising(self) -> PauliOperator:
135
+ return (
136
+ ising_converter.convert_pyomo_to_hamiltonian(
137
+ self.objective.expr, self._ising_vars, self.qregs
138
+ )
139
+ * self.sign
140
+ )
141
+
142
+ def _remove_slack_variables_from_solution(self, solution: List[int]) -> List[int]:
143
+ variables = pyomo_utils.extract(self._model_original, pyo.Var)
144
+ return solution[: len(variables)]
145
+
146
+ def decode(self, solution: List[int]) -> List[int]:
147
+ if self.is_encoded:
148
+ solution = self._vars_encoding_mapping.decode(solution)
149
+
150
+ solution = add_fixed_variables_to_solution(self._model_original, solution)
151
+
152
+ return self._remove_slack_variables_from_solution(solution)
153
+
154
+ def get_operator(self) -> PauliOperator:
155
+ try:
156
+ return self.ising
157
+ except Exception as exc:
158
+ raise ClassiqCombOptError(
159
+ f"Could not convert optimization model to operator: {exc}"
160
+ ) from exc
161
+
162
+ def _slack_vars_convert(self) -> ConcreteModel:
163
+ if self.qsolver == QSolver.QAOAPenalty and not is_model_penalty_supported(
164
+ self._model
165
+ ):
166
+ return slack_variables.slack_vars_convert(self._model)
@@ -0,0 +1,31 @@
1
+ from typing import Any, Union
2
+
3
+ import numpy as np
4
+
5
+
6
+ class SparsePauliOp:
7
+ def __init__(self, paulis: Any, coeffs: Any) -> None:
8
+ assert len(paulis) == len(
9
+ coeffs
10
+ ), "Paulis and coefficients lists must have the same length."
11
+ self.paulis = np.array(paulis)
12
+ self.coeffs = np.array(coeffs, dtype=complex)
13
+
14
+ def __str__(self) -> str:
15
+ terms = [f"{coef}*{pauli}" for coef, pauli in zip(self.coeffs, self.paulis)]
16
+ return " + ".join(terms)
17
+
18
+ def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
19
+ """Add two SparsePauliOp objects."""
20
+ if not isinstance(other, SparsePauliOp):
21
+ raise ValueError("Can only add SparsePauliOp objects.")
22
+ new_paulis = np.concatenate([self.paulis, other.paulis])
23
+ new_coeffs = np.concatenate([self.coeffs, other.coeffs])
24
+ return SparsePauliOp(new_paulis, new_coeffs)
25
+
26
+ def __mul__(self, other: Union[int, float, complex]) -> "SparsePauliOp":
27
+ """Scalar multiplication of a SparsePauliOp."""
28
+ if not isinstance(other, (int, float, complex)):
29
+ raise ValueError("Can only multiply by scalar values.")
30
+ new_coeffs = self.coeffs * other
31
+ return SparsePauliOp(self.paulis, new_coeffs)
@@ -0,0 +1,65 @@
1
+ from typing import List, cast
2
+
3
+ from classiq.interface.chemistry.operator import PauliOperator
4
+ from classiq.interface.generator.expressions.enums.pauli import Pauli
5
+ from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
6
+ from classiq.interface.helpers.custom_pydantic_types import PydanticPauliList
7
+
8
+ from classiq.exceptions import (
9
+ ClassiqExecutorInvalidHamiltonianError,
10
+ ClassiqNonNumericCoefficientInPauliError,
11
+ ClassiqValueError,
12
+ )
13
+
14
+
15
+ def _pauli_str_to_enums(pauli_str: str) -> str:
16
+ return ", ".join(f"Pauli.{pauli_term}" for pauli_term in pauli_str)
17
+
18
+
19
+ def pauli_integers_to_str(paulis: List[Pauli]) -> str:
20
+ return "".join([Pauli(pauli).name for pauli in paulis])
21
+
22
+
23
+ def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> List[QmodPyStruct]:
24
+ pauli_struct_list: List[QmodPyStruct] = []
25
+ for pauli_term in pauli_list:
26
+ if not isinstance(pauli_term[1], complex) or pauli_term[1].imag != 0:
27
+ raise ClassiqNonNumericCoefficientInPauliError(
28
+ "Coefficient is not a number."
29
+ )
30
+
31
+ pauli_struct_list.append(
32
+ {
33
+ "pauli": [Pauli[p] for p in pauli_term[0]],
34
+ "coefficient": pauli_term[1].real,
35
+ }
36
+ )
37
+ return pauli_struct_list
38
+
39
+
40
+ def get_pauli_operator(pauli_operator: List[QmodPyStruct]) -> PauliOperator:
41
+ pauli_list = [
42
+ (
43
+ pauli_integers_to_str(elem["pauli"]),
44
+ elem["coefficient"],
45
+ )
46
+ for elem in pauli_operator
47
+ ]
48
+
49
+ try:
50
+ pauli = PauliOperator(pauli_list=pauli_list)
51
+ except ValueError:
52
+ raise ClassiqExecutorInvalidHamiltonianError() from None
53
+
54
+ return pauli
55
+
56
+
57
+ def _pauli_operator_to_qmod(hamiltonian: PauliOperator) -> str:
58
+ if not all(isinstance(summand[1], complex) for summand in hamiltonian.pauli_list):
59
+ raise ClassiqValueError(
60
+ "Supporting only Hamiltonian with numeric coefficients."
61
+ )
62
+ return ", ".join(
63
+ f"struct_literal(PauliTerm, pauli=[{_pauli_str_to_enums(pauli)}], coefficient={cast(complex, coeff).real})"
64
+ for pauli, coeff in hamiltonian.pauli_list
65
+ )