classiq 0.37.1__py3-none-any.whl → 0.39.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. classiq/__init__.py +23 -24
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
  3. classiq/_analyzer_extras/interactive_hardware.py +3 -3
  4. classiq/_internals/api_wrapper.py +37 -17
  5. classiq/_internals/async_utils.py +1 -74
  6. classiq/_internals/authentication/device.py +9 -4
  7. classiq/_internals/authentication/password_manager.py +25 -10
  8. classiq/_internals/authentication/token_manager.py +2 -2
  9. classiq/_internals/client.py +24 -6
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +29 -29
  12. classiq/analyzer/analyzer_utilities.py +5 -5
  13. classiq/analyzer/rb.py +4 -5
  14. classiq/analyzer/show_interactive_hack.py +6 -6
  15. classiq/applications/__init__.py +1 -8
  16. classiq/applications/chemistry/__init__.py +6 -0
  17. classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +9 -16
  18. classiq/applications/combinatorial_helpers/allowed_constraints.py +20 -0
  19. classiq/applications/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  20. classiq/applications/combinatorial_helpers/arithmetic/isolation.py +42 -0
  21. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +150 -0
  22. classiq/applications/combinatorial_helpers/encoding_mapping.py +107 -0
  23. classiq/applications/combinatorial_helpers/encoding_utils.py +122 -0
  24. classiq/applications/combinatorial_helpers/memory.py +77 -0
  25. classiq/applications/combinatorial_helpers/optimization_model.py +162 -0
  26. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  27. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +75 -0
  28. classiq/applications/combinatorial_helpers/py.typed +0 -0
  29. classiq/applications/combinatorial_helpers/pyomo_utils.py +245 -0
  30. classiq/applications/combinatorial_helpers/solvers/__init__.py +0 -0
  31. classiq/applications/combinatorial_helpers/sympy_utils.py +22 -0
  32. classiq/applications/combinatorial_helpers/transformations/__init__.py +0 -0
  33. classiq/applications/combinatorial_helpers/transformations/encoding.py +187 -0
  34. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +142 -0
  35. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +122 -0
  36. classiq/applications/combinatorial_helpers/transformations/penalty.py +32 -0
  37. classiq/applications/combinatorial_helpers/transformations/penalty_support.py +37 -0
  38. classiq/applications/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  39. classiq/applications/combinatorial_helpers/transformations/slack_variables.py +88 -0
  40. classiq/applications/combinatorial_optimization/__init__.py +13 -2
  41. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +134 -0
  42. classiq/applications/finance/__init__.py +3 -2
  43. classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +27 -30
  44. classiq/applications/grover/__init__.py +11 -0
  45. classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +20 -91
  46. classiq/applications/libraries/__init__.py +0 -0
  47. classiq/applications/libraries/qmci_library.py +35 -0
  48. classiq/applications/qnn/circuit_utils.py +2 -2
  49. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  50. classiq/applications/qnn/types.py +2 -2
  51. classiq/applications/qsvm/__init__.py +5 -1
  52. classiq/applications/qsvm/qsvm.py +4 -7
  53. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  54. classiq/exceptions.py +43 -1
  55. classiq/execution/all_hardware_devices.py +13 -0
  56. classiq/executor.py +12 -10
  57. classiq/interface/_version.py +1 -1
  58. classiq/interface/analyzer/analysis_params.py +6 -3
  59. classiq/interface/analyzer/result.py +12 -8
  60. classiq/interface/applications/qsvm.py +17 -3
  61. classiq/interface/ast_node.py +23 -0
  62. classiq/interface/backend/backend_preferences.py +4 -2
  63. classiq/interface/backend/pydantic_backend.py +3 -1
  64. classiq/interface/backend/quantum_backend_providers.py +1 -0
  65. classiq/interface/chemistry/fermionic_operator.py +15 -13
  66. classiq/interface/chemistry/ground_state_problem.py +18 -3
  67. classiq/interface/chemistry/molecule.py +8 -6
  68. classiq/interface/chemistry/operator.py +20 -14
  69. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  70. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  71. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  73. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  74. classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
  75. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  76. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  77. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  78. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  79. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  80. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  81. classiq/interface/executor/aws_execution_cost.py +4 -3
  82. classiq/interface/executor/estimation.py +2 -2
  83. classiq/interface/executor/execution_preferences.py +5 -34
  84. classiq/interface/executor/execution_request.py +15 -48
  85. classiq/interface/executor/optimizer_preferences.py +22 -13
  86. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  87. classiq/interface/executor/quantum_instruction_set.py +2 -1
  88. classiq/interface/executor/register_initialization.py +1 -3
  89. classiq/interface/executor/result.py +41 -10
  90. classiq/interface/executor/vqe_result.py +2 -2
  91. classiq/interface/finance/function_input.py +17 -4
  92. classiq/interface/finance/gaussian_model_input.py +3 -1
  93. classiq/interface/finance/log_normal_model_input.py +3 -1
  94. classiq/interface/finance/model_input.py +2 -0
  95. classiq/interface/generator/amplitude_loading.py +6 -3
  96. classiq/interface/generator/application_apis/__init__.py +1 -0
  97. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  98. classiq/interface/generator/arith/argument_utils.py +14 -4
  99. classiq/interface/generator/arith/arithmetic.py +3 -1
  100. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  101. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  102. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  103. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  104. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  105. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  106. classiq/interface/generator/arith/binary_ops.py +202 -54
  107. classiq/interface/generator/arith/extremum_operations.py +5 -3
  108. classiq/interface/generator/arith/logical_ops.py +4 -2
  109. classiq/interface/generator/arith/machine_precision.py +3 -0
  110. classiq/interface/generator/arith/number_utils.py +34 -44
  111. classiq/interface/generator/arith/register_user_input.py +21 -1
  112. classiq/interface/generator/arith/unary_ops.py +16 -25
  113. classiq/interface/generator/builtin_api_builder.py +0 -5
  114. classiq/interface/generator/chemistry_function_params.py +4 -4
  115. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  116. classiq/interface/generator/compiler_keywords.py +4 -0
  117. classiq/interface/generator/complex_type.py +3 -10
  118. classiq/interface/generator/constant.py +2 -3
  119. classiq/interface/generator/control_state.py +5 -3
  120. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  121. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  122. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  123. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  124. classiq/interface/generator/expressions/expression.py +3 -5
  125. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  126. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  127. classiq/interface/generator/finance.py +1 -1
  128. classiq/interface/generator/function_params.py +7 -6
  129. classiq/interface/generator/functions/__init__.py +2 -2
  130. classiq/interface/generator/functions/builtins/__init__.py +15 -0
  131. classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
  132. classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
  133. classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
  134. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +612 -219
  135. classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
  136. classiq/interface/generator/functions/classical_type.py +2 -4
  137. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  138. classiq/interface/generator/functions/function_declaration.py +2 -2
  139. classiq/interface/generator/functions/function_implementation.py +8 -4
  140. classiq/interface/generator/functions/native_function_definition.py +4 -2
  141. classiq/interface/generator/functions/register.py +4 -2
  142. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  143. classiq/interface/generator/generated_circuit_data.py +2 -2
  144. classiq/interface/generator/grover_operator.py +5 -3
  145. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  146. classiq/interface/generator/hardware/hardware_data.py +6 -4
  147. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  148. classiq/interface/generator/hartree_fock.py +13 -3
  149. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  150. classiq/interface/generator/mcu.py +5 -3
  151. classiq/interface/generator/mcx.py +7 -5
  152. classiq/interface/generator/model/classical_main_validator.py +1 -1
  153. classiq/interface/generator/model/constraints.py +2 -1
  154. classiq/interface/generator/model/model.py +12 -20
  155. classiq/interface/generator/model/preferences/preferences.py +4 -3
  156. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  157. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  158. classiq/interface/generator/qpe.py +6 -4
  159. classiq/interface/generator/qsvm.py +5 -8
  160. classiq/interface/generator/quantum_function_call.py +21 -16
  161. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  162. classiq/interface/generator/range_types.py +3 -1
  163. classiq/interface/generator/slice_parsing_utils.py +8 -3
  164. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  165. classiq/interface/generator/state_preparation/metrics.py +2 -1
  166. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  167. classiq/interface/generator/state_propagator.py +16 -5
  168. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  169. classiq/interface/generator/types/struct_declaration.py +10 -7
  170. classiq/interface/generator/ucc.py +6 -4
  171. classiq/interface/generator/unitary_gate.py +7 -3
  172. classiq/interface/generator/validations/flow_graph.py +6 -4
  173. classiq/interface/generator/validations/validator_functions.py +6 -4
  174. classiq/interface/hardware.py +2 -2
  175. classiq/interface/helpers/custom_encoders.py +3 -0
  176. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  177. classiq/interface/helpers/validation_helpers.py +1 -1
  178. classiq/interface/helpers/versioned_model.py +4 -1
  179. classiq/interface/ide/show.py +2 -2
  180. classiq/interface/jobs.py +72 -3
  181. classiq/interface/model/bind_operation.py +18 -11
  182. classiq/interface/model/call_synthesis_data.py +68 -0
  183. classiq/interface/model/classical_if.py +13 -0
  184. classiq/interface/model/classical_parameter_declaration.py +2 -3
  185. classiq/interface/model/control.py +16 -0
  186. classiq/interface/model/handle_binding.py +3 -2
  187. classiq/interface/model/inplace_binary_operation.py +2 -2
  188. classiq/interface/model/invert.py +10 -0
  189. classiq/interface/model/model.py +29 -22
  190. classiq/interface/model/native_function_definition.py +3 -5
  191. classiq/interface/model/power.py +12 -0
  192. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  193. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  194. classiq/interface/model/quantum_function_call.py +33 -142
  195. classiq/interface/model/quantum_function_declaration.py +8 -0
  196. classiq/interface/model/quantum_if_operation.py +4 -5
  197. classiq/interface/model/quantum_lambda_function.py +58 -0
  198. classiq/{quantum_register.py → interface/model/quantum_register.py} +17 -9
  199. classiq/interface/model/quantum_statement.py +3 -2
  200. classiq/interface/model/quantum_type.py +58 -59
  201. classiq/interface/model/quantum_variable_declaration.py +3 -3
  202. classiq/interface/model/repeat.py +13 -0
  203. classiq/interface/model/resolvers/function_call_resolver.py +26 -0
  204. classiq/interface/model/statement_block.py +49 -0
  205. classiq/interface/model/validations/handles_validator.py +16 -18
  206. classiq/interface/model/within_apply_operation.py +11 -0
  207. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  208. classiq/interface/server/routes.py +5 -4
  209. classiq/qmod/__init__.py +13 -6
  210. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  211. classiq/qmod/builtins/classical_functions.py +22 -12
  212. classiq/qmod/builtins/functions.py +272 -328
  213. classiq/qmod/builtins/operations.py +171 -35
  214. classiq/qmod/builtins/structs.py +15 -15
  215. classiq/qmod/cfunc.py +42 -0
  216. classiq/qmod/classical_function.py +6 -14
  217. classiq/qmod/declaration_inferrer.py +12 -21
  218. classiq/qmod/expression_query.py +23 -0
  219. classiq/qmod/model_state_container.py +2 -0
  220. classiq/qmod/native/__init__.py +0 -0
  221. classiq/qmod/native/expression_to_qmod.py +189 -0
  222. classiq/qmod/native/pretty_printer.py +340 -0
  223. classiq/qmod/qfunc.py +27 -0
  224. classiq/qmod/qmod_constant.py +100 -0
  225. classiq/qmod/qmod_parameter.py +36 -13
  226. classiq/qmod/qmod_struct.py +3 -3
  227. classiq/qmod/qmod_variable.py +148 -31
  228. classiq/qmod/quantum_callable.py +1 -0
  229. classiq/qmod/quantum_expandable.py +18 -19
  230. classiq/qmod/quantum_function.py +41 -8
  231. classiq/qmod/symbolic.py +48 -5
  232. classiq/qmod/symbolic_expr.py +9 -0
  233. classiq/qmod/utilities.py +13 -0
  234. classiq/qmod/write_qmod.py +39 -0
  235. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/METADATA +2 -1
  236. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/RECORD +244 -225
  237. {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/WHEEL +1 -1
  238. classiq/applications/benchmarking/__init__.py +0 -9
  239. classiq/applications/benchmarking/mirror_benchmarking.py +0 -67
  240. classiq/applications/numpy_utils.py +0 -37
  241. classiq/applications_model_constructors/__init__.py +0 -17
  242. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +0 -178
  243. classiq/applications_model_constructors/libraries/qmci_library.py +0 -109
  244. classiq/builtin_functions/__init__.py +0 -43
  245. classiq/builtin_functions/amplitude_loading.py +0 -3
  246. classiq/builtin_functions/binary_ops.py +0 -1
  247. classiq/builtin_functions/exponentiation.py +0 -5
  248. classiq/builtin_functions/qpe.py +0 -4
  249. classiq/builtin_functions/qsvm.py +0 -7
  250. classiq/builtin_functions/range_types.py +0 -5
  251. classiq/builtin_functions/standard_gates.py +0 -1
  252. classiq/builtin_functions/state_preparation.py +0 -6
  253. classiq/builtin_functions/suzuki_trotter.py +0 -3
  254. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  255. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
  256. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -169
  257. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  258. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  259. classiq/interface/model/numeric_reinterpretation.py +0 -25
  260. classiq/interface/model/operator_synthesis_data.py +0 -48
  261. classiq/model/__init__.py +0 -14
  262. classiq/model/composite_function_generator.py +0 -33
  263. classiq/model/function_handler.py +0 -466
  264. classiq/model/function_handler.pyi +0 -152
  265. classiq/model/logic_flow.py +0 -149
  266. classiq/model/logic_flow_change_handler.py +0 -71
  267. classiq/model/model.py +0 -246
  268. classiq/quantum_functions/__init__.py +0 -17
  269. classiq/quantum_functions/annotation_parser.py +0 -207
  270. classiq/quantum_functions/decorators.py +0 -22
  271. classiq/quantum_functions/function_library.py +0 -181
  272. classiq/quantum_functions/function_parser.py +0 -74
  273. classiq/quantum_functions/quantum_function.py +0 -236
  274. /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers}/__init__.py +0 -0
  275. /classiq/{interface/generator/functions/core_lib_declarations → applications/combinatorial_helpers/arithmetic}/__init__.py +0 -0
  276. /classiq/{interface/generator/functions/core_lib_declarations/quantum_functions/chemistry_functions.py → applications/combinatorial_helpers/pauli_helpers/__init__.py} +0 -0
  277. /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
  278. /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
  279. /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +0 -0
  280. /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +0 -0
@@ -0,0 +1,245 @@
1
+ import json
2
+ from enum import Enum
3
+ from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union
4
+
5
+ import pydantic
6
+ import pyomo.core.expr.numeric_expr as pyo_expr
7
+ import pyomo.environ as pyo
8
+ import sympy
9
+ from pyomo.core import ConcreteModel, Constraint, Objective, Var, maximize
10
+ from pyomo.core.base import _GeneralVarData
11
+ from pyomo.core.base.component import ComponentData
12
+ from pyomo.core.base.constraint import _GeneralConstraintData
13
+ from pyomo.core.base.indexed_component import IndexedComponent
14
+ from pyomo.core.base.objective import ScalarObjective
15
+ from pyomo.core.expr.sympy_tools import (
16
+ Pyomo2SympyVisitor,
17
+ PyomoSympyBimap,
18
+ Sympy2PyomoVisitor,
19
+ )
20
+
21
+ from classiq.interface.generator.expressions.expression import Expression
22
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
23
+
24
+ from classiq.exceptions import ClassiqValueError
25
+
26
+ ListVars = List[_GeneralVarData]
27
+
28
+
29
+ class ObjectiveType(Enum):
30
+ Min = "Min"
31
+ Max = "Max"
32
+
33
+
34
+ class CombinatorialOptimizationStructDeclaration(StructDeclaration):
35
+ variable_lower_bound: int = pydantic.Field(default=0)
36
+ variable_upper_bound: int = pydantic.Field(default=1)
37
+ constraints: List[Expression] = pydantic.Field(
38
+ default_factory=list, description="List of constraint expressions"
39
+ )
40
+ objective_type: ObjectiveType = pydantic.Field(
41
+ description="Specify whether the optimization problem is Min or Max"
42
+ )
43
+ objective_function: Expression = pydantic.Field(
44
+ description="The expression to optimize, according to the objective type"
45
+ )
46
+
47
+
48
+ def contains(var_data: _GeneralVarData, vars_data: ListVars) -> bool:
49
+ # HACK: standard "__containts__ (in)" method doesn't work, because pyomo overrode the __eq__ method (IMO)
50
+ return any(var_data is var_data_temp for var_data_temp in vars_data)
51
+
52
+
53
+ def remove(var_data: _GeneralVarData, vars_data: ListVars) -> ListVars:
54
+ # HACK: standard "list method remove" method doesn't work, because pyomo overrode the __eq__ method (IMO)
55
+ assert contains(var_data, vars_data), "var not in list"
56
+ vars_data = vars_data.copy()
57
+ for idx, var_data_temp in enumerate(vars_data):
58
+ if var_data_temp is var_data:
59
+ del vars_data[idx]
60
+ break
61
+ return vars_data
62
+
63
+
64
+ def index(var_data: _GeneralVarData, vars_data: ListVars) -> int:
65
+ # HACK: standard "index method" doesn't work.
66
+ assert contains(var_data, vars_data), "var not in list"
67
+ idxs = [
68
+ idx for idx, var_data_temp in enumerate(vars_data) if var_data is var_data_temp
69
+ ]
70
+ return idxs[0]
71
+
72
+
73
+ T = TypeVar("T")
74
+
75
+
76
+ def extract(model: ConcreteModel, type_: Type[T]) -> List[T]:
77
+ if type_ == _GeneralVarData:
78
+ type_ = Var
79
+
80
+ elif type_ == _GeneralConstraintData:
81
+ type_ = Constraint
82
+
83
+ components = model.component_objects(type_)
84
+ return [
85
+ component[component_idx]
86
+ for component in components
87
+ for component_idx in component
88
+ ]
89
+
90
+
91
+ def delete_component(model: ConcreteModel, component: ComponentData) -> None:
92
+ parent_ref = component._component
93
+
94
+ if parent_ref is None:
95
+ return
96
+
97
+ parent_component = parent_ref()
98
+
99
+ if component is parent_component:
100
+ model.del_component(component)
101
+ else:
102
+ _delete_element_by_value(parent_component, component)
103
+
104
+ if not parent_component:
105
+ model.del_component(parent_component)
106
+
107
+
108
+ def _delete_element_by_value(dict_: Dict, value: Any) -> None:
109
+ iter_dict = {**dict_}
110
+ for k, v in iter_dict.items():
111
+ if v is value and k in dict_:
112
+ del dict_[k]
113
+
114
+
115
+ def get_name(component: Union[IndexedComponent, ComponentData]) -> str:
116
+ if isinstance(component, IndexedComponent):
117
+ return component._name # constraint.name returns "'{name}'"
118
+ else:
119
+ return component.name
120
+
121
+
122
+ class FixedSympy2PyomoVisitor(Sympy2PyomoVisitor):
123
+ def beforeChild( # noqa: N802
124
+ self, node: Optional[sympy.Expr], child: sympy.Expr, child_idx: Optional[int]
125
+ ) -> Tuple[bool, Union[int, float, None]]:
126
+ if not child._args:
127
+ item = self.object_map.getPyomoSymbol(child, None)
128
+ if item is None:
129
+ if isinstance(child, sympy.Integer): # addition to base implementation
130
+ item = int(child.evalf())
131
+ else:
132
+ item = float(child.evalf())
133
+ return False, item
134
+ return True, None
135
+
136
+
137
+ def sympy2pyomo_expression(
138
+ expr: sympy.core.Basic, object_map: PyomoSympyBimap
139
+ ) -> pyo_expr.ExpressionBase:
140
+ return FixedSympy2PyomoVisitor(object_map).walk_expression(expr)
141
+
142
+
143
+ def convert_pyomo_to_global_presentation(
144
+ pyo_model: pyo.ConcreteModel,
145
+ ) -> pyo.ConcreteModel:
146
+ pyo_model_str = pyomo2qmod("nativePyoModel", pyo_model)
147
+ problem_struct = CombinatorialOptimizationStructDeclaration.parse_raw(pyo_model_str)
148
+
149
+ pyomo_model = pyo.ConcreteModel()
150
+
151
+ var_names = list(problem_struct.variables.keys())
152
+ pyomo_model.var_set = pyo.Var(
153
+ var_names,
154
+ domain=pyo.NonNegativeIntegers,
155
+ bounds=(
156
+ problem_struct.variable_lower_bound,
157
+ problem_struct.variable_upper_bound,
158
+ ),
159
+ )
160
+ obj_map = PyomoSympyBimap()
161
+ var_dict = {
162
+ var_name: obj_map.getSympySymbol(pyomo_model.var_set[var_name])
163
+ for var_name in var_names
164
+ }
165
+
166
+ def expr2pyomo(expr: Expression) -> pyo_expr.ExpressionBase:
167
+ sp_expr = sympy.sympify(expr.expr, locals=var_dict)
168
+ if isinstance(sp_expr, sympy.core.relational.Equality):
169
+ return sympy2pyomo_expression(
170
+ sp_expr.args[0], obj_map
171
+ ) == sympy2pyomo_expression(sp_expr.args[1], obj_map)
172
+
173
+ # Note that strict greater/less than are not supported by Pyomo
174
+ return sympy2pyomo_expression(sp_expr, obj_map)
175
+
176
+ pyomo_model.constraints = pyo.Constraint(
177
+ pyo.RangeSet(0, len(problem_struct.constraints) - 1),
178
+ rule=lambda model, i: expr2pyomo(problem_struct.constraints[i]),
179
+ )
180
+ pyomo_model.objective = pyo.Objective(
181
+ expr=expr2pyomo(problem_struct.objective_function),
182
+ sense=(
183
+ pyo.maximize
184
+ if problem_struct.objective_type == ObjectiveType.Max
185
+ else pyo.minimize
186
+ ),
187
+ )
188
+
189
+ return pyomo_model
190
+
191
+
192
+ def pyomo2qmod(struct_name: str, pyo_model: ConcreteModel) -> str:
193
+ symbols_map = PyomoSympyBimap()
194
+
195
+ variables: List[sympy.Symbol] = []
196
+
197
+ bounds_set = False
198
+ lower_bound = None
199
+ upper_bound = None
200
+
201
+ for var_dict in pyo_model.component_objects(Var):
202
+ for key in var_dict:
203
+ var = Pyomo2SympyVisitor(symbols_map).walk_expression(var_dict[key])
204
+ var.name = var.name.replace(",", "_")
205
+ variables.append(var)
206
+ if bounds_set:
207
+ if lower_bound != var_dict[key].lb:
208
+ raise ClassiqValueError(
209
+ "All problem variables must agree on lower bound"
210
+ )
211
+ if upper_bound != var_dict[key].ub:
212
+ raise ClassiqValueError(
213
+ "All problem variables must agree on upper bound"
214
+ )
215
+ else:
216
+ lower_bound = var_dict[key].lb
217
+ upper_bound = var_dict[key].ub
218
+ bounds_set = True
219
+
220
+ constraint_exprs: List[sympy.Expr] = []
221
+
222
+ constraint_exprs.extend(
223
+ Pyomo2SympyVisitor(symbols_map).walk_expression(constraint_dict[key].expr)
224
+ for constraint_dict in pyo_model.component_objects(Constraint)
225
+ for key in constraint_dict
226
+ )
227
+
228
+ pyo_objective: ScalarObjective = next(pyo_model.component_objects(Objective))
229
+ objective_type_str = "Max" if pyo_objective.sense == maximize else "Min"
230
+ objective_expr: sympy.Expr = Pyomo2SympyVisitor(symbols_map).walk_expression(
231
+ pyo_objective
232
+ )
233
+
234
+ combi_struct_decl = {
235
+ "name": struct_name,
236
+ "variables": {str(variable): {"kind": "int"} for variable in variables},
237
+ "variable_lower_bound": lower_bound,
238
+ "variable_upper_bound": upper_bound,
239
+ "constraints": [
240
+ {"expr": str(constraint_expr)} for constraint_expr in constraint_exprs
241
+ ],
242
+ "objective_type": objective_type_str,
243
+ "objective_function": {"expr": str(objective_expr)},
244
+ }
245
+ return json.dumps(combi_struct_decl, indent=2)
@@ -0,0 +1,22 @@
1
+ from typing import List, Optional
2
+
3
+ import pyomo.core as pyo
4
+ from pyomo.core.base import _GeneralVarData
5
+ from pyomo.core.expr.sympy_tools import Pyomo2SympyVisitor, PyomoSympyBimap
6
+ from sympy import Expr
7
+
8
+
9
+ def sympyify_vars(variables: List[_GeneralVarData]) -> PyomoSympyBimap:
10
+ symbols_map = PyomoSympyBimap()
11
+ for var in variables:
12
+ Pyomo2SympyVisitor(symbols_map).walk_expression(var)
13
+ return symbols_map
14
+
15
+
16
+ def sympyify_expression(
17
+ expression: pyo.Expression, symbols_map: Optional[PyomoSympyBimap] = None
18
+ ) -> Expr:
19
+ if symbols_map is None:
20
+ symbols_map = PyomoSympyBimap()
21
+
22
+ return Pyomo2SympyVisitor(symbols_map).walk_expression(expression)
@@ -0,0 +1,187 @@
1
+ import copy
2
+ from itertools import chain, product
3
+ from typing import Callable, Dict, List, Tuple, Union
4
+
5
+ import pyomo.environ as pyo
6
+ from pyomo.core.base import _GeneralVarData
7
+ from pyomo.core.expr.numeric_expr import ExpressionBase
8
+ from pyomo.core.expr.relational_expr import EqualityExpression
9
+
10
+ from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
11
+ from classiq.interface.combinatorial_optimization.solver_types import QSolver
12
+
13
+ from classiq.applications.combinatorial_helpers import encoding_utils, pyomo_utils
14
+ from classiq.applications.combinatorial_helpers.encoding_mapping import EncodingMapping
15
+ from classiq.applications.combinatorial_helpers.encoding_utils import ONE_HOT_SUFFIX
16
+ from classiq.exceptions import ClassiqCombOptInvalidEncodingTypeError
17
+
18
+
19
+ def _make_invalid_encoding_type_error(
20
+ encoding_type: EncodingType,
21
+ ) -> ClassiqCombOptInvalidEncodingTypeError:
22
+ return ClassiqCombOptInvalidEncodingTypeError(
23
+ encoding_type=encoding_type,
24
+ valid_types=EncodingType.__members__.keys(),
25
+ )
26
+
27
+
28
+ def encoding_length(
29
+ var: _GeneralVarData, encoding_type: Union[EncodingType, None]
30
+ ) -> int:
31
+ if encoding_type is None:
32
+ return 1
33
+
34
+ var_span = encoding_utils.get_var_span(var)
35
+
36
+ if encoding_type == EncodingType.BINARY:
37
+ return var_span.bit_length()
38
+
39
+ elif encoding_type == EncodingType.ONE_HOT:
40
+ return var_span + 1
41
+
42
+ else:
43
+ raise _make_invalid_encoding_type_error(encoding_type)
44
+
45
+
46
+ class ModelEncoder:
47
+ def __init__(
48
+ self,
49
+ model: pyo.ConcreteModel,
50
+ qsolver: QSolver,
51
+ encoding_type: EncodingType = EncodingType.BINARY,
52
+ ) -> None:
53
+ self.encoding_type = encoding_type
54
+ self.encoded_model = copy.deepcopy(model)
55
+ self.qsolver = qsolver
56
+ self.vars_original = list(self.encoded_model.component_objects(pyo.Var))
57
+ self.vars_encoding_mapping = self._encode_variables()
58
+ if self.encoding_type == EncodingType.ONE_HOT:
59
+ self._add_one_hot_constraints()
60
+ self._encode_constraints()
61
+ self._encode_objective()
62
+
63
+ @property
64
+ def _shift_substitution_dict(self) -> Dict[int, pyo.Expression]:
65
+ variables = pyomo_utils.extract(self.encoded_model, pyo.Var)
66
+ return {id(var): var + var.lb for var in variables}
67
+
68
+ def _encode_variables(self) -> EncodingMapping:
69
+ vars_encoding_mapping = EncodingMapping(self.encoding_type)
70
+ for variable in self.vars_original:
71
+ # encode variables
72
+ encoded_var_name = encoding_utils.encoded_obj_name(variable.name)
73
+ encoded_var = pyo.Var(self._get_encoding_idxs(variable), domain=pyo.Binary)
74
+ setattr(self.encoded_model, encoded_var_name, encoded_var)
75
+
76
+ # create mapping between original variables and their encodings
77
+ for var_idx, var_data in variable.items():
78
+ encoding_vars = [
79
+ encoded_var[var_idx, encoding_idx]
80
+ for encoding_idx in range(
81
+ encoding_length(var_data, self.encoding_type)
82
+ )
83
+ ]
84
+
85
+ encoding_expr = self._get_encoding_expr(var_data, encoding_vars)
86
+
87
+ self._add_expr_constraint(
88
+ constraint_name=var_data.name + encoding_utils.ENCODED_SUFFIX,
89
+ expr=EqualityExpression(args=[var_data, encoding_expr]),
90
+ )
91
+
92
+ vars_encoding_mapping.add(
93
+ original_var=var_data,
94
+ encoding_expr=encoding_expr,
95
+ encodings_vars=encoding_vars,
96
+ )
97
+ return vars_encoding_mapping
98
+
99
+ def _get_encoding_expr(
100
+ self, var_data: _GeneralVarData, encoding_vars: List[_GeneralVarData]
101
+ ) -> pyo.Expression:
102
+ if self.encoding_type == EncodingType.BINARY:
103
+ var_span = encoding_utils.get_var_span(var_data)
104
+ coeffs = self._get_binary_coeffs(encoding_vars, var_span)
105
+
106
+ elif self.encoding_type == EncodingType.ONE_HOT:
107
+ coeffs = list(range(len(encoding_vars)))
108
+ else:
109
+ raise _make_invalid_encoding_type_error(self.encoding_type)
110
+
111
+ encoding_expr = sum(
112
+ coeff * encoding_var for coeff, encoding_var in zip(coeffs, encoding_vars)
113
+ )
114
+
115
+ # Encodes variable shift
116
+ encoding_expr += var_data.lb
117
+ return encoding_expr
118
+
119
+ def _get_binary_coeffs(
120
+ self, encoding_vars: List[_GeneralVarData], var_span: int
121
+ ) -> List[int]:
122
+ num_vars = len(encoding_vars)
123
+ if self.qsolver == QSolver.QAOAMixer:
124
+ return [2**idx for idx in range(num_vars)]
125
+
126
+ else: # self.qsolver == QSolver.QAOAPenalty:
127
+ coeffs = [2**idx for idx in range(num_vars - 1)]
128
+ coeffs += [var_span - sum(coeffs)]
129
+ return coeffs
130
+
131
+ def _get_encoding_idxs(self, variable: pyo.Var) -> List[Tuple[int, int]]:
132
+ return list(
133
+ chain(
134
+ *[
135
+ product(
136
+ [var_idx], range(encoding_length(var_data, self.encoding_type))
137
+ )
138
+ for var_idx, var_data in variable.items()
139
+ ]
140
+ )
141
+ )
142
+
143
+ def _add_one_hot_constraints(self) -> None:
144
+ for variable in self.vars_original:
145
+ # potential bug with creating function inside a loop.
146
+ # solved with early binding - https://stackoverflow.com/questions/3431676/creating-functions-in-a-loop
147
+ def one_hot_rule(var_idx: int, var: pyo.Var = variable) -> ExpressionBase:
148
+ var_data = var[var_idx]
149
+ return sum(self.vars_encoding_mapping.get_encoding_vars(var_data)) == 1
150
+
151
+ self._add_rule_constraint(
152
+ constraint_name=variable.name + ONE_HOT_SUFFIX,
153
+ idxs=getattr(self.encoded_model, variable.index),
154
+ rule=one_hot_rule,
155
+ )
156
+
157
+ def _add_rule_constraint(
158
+ self, constraint_name: str, idxs: List[int], rule: Callable
159
+ ) -> None:
160
+ encoding_constraint = pyo.Constraint(idxs, rule=rule)
161
+
162
+ setattr(self.encoded_model, constraint_name, encoding_constraint)
163
+
164
+ def _add_expr_constraint(self, constraint_name: str, expr: ExpressionBase) -> None:
165
+ setattr(self.encoded_model, constraint_name, pyo.Constraint(expr=expr))
166
+
167
+ def _encode_objective(self) -> None:
168
+ encoding_utils.encode_objective(
169
+ self.encoded_model, self.vars_encoding_mapping.substitution_dict
170
+ )
171
+
172
+ def encode_expr(
173
+ self,
174
+ expr: pyo.Expression,
175
+ substitution_dict: Union[Dict[int, pyo.Expression], None] = None,
176
+ ) -> pyo.Expression:
177
+ if substitution_dict is None:
178
+ substitution_dict = self.vars_encoding_mapping.substitution_dict
179
+ return encoding_utils.encode_expr(expr, substitution_dict)
180
+
181
+ def _encode_constraints(self) -> None:
182
+ if self.qsolver == QSolver.QAOAPenalty:
183
+ return
184
+
185
+ encoding_utils.encode_constraints(
186
+ self.encoded_model, self._shift_substitution_dict
187
+ )
@@ -0,0 +1,142 @@
1
+ import copy
2
+ from typing import List, Tuple, Union
3
+
4
+ from pyomo.core import ConcreteModel, Var
5
+ from pyomo.core.base.constraint import _GeneralConstraintData
6
+ from pyomo.core.expr.relational_expr import EqualityExpression
7
+ from pyomo.core.expr.visitor import identify_variables
8
+ from pyomo.repn.standard_repn import _GeneralVarData
9
+
10
+ from classiq.applications.combinatorial_helpers import (
11
+ encoding_utils,
12
+ pyomo_utils,
13
+ sympy_utils,
14
+ )
15
+ from classiq.applications.combinatorial_helpers.arithmetic.isolation import isolate
16
+ from classiq.applications.combinatorial_helpers.encoding_utils import (
17
+ deal_with_trivial_boolean_constraint,
18
+ )
19
+ from classiq.applications.combinatorial_helpers.sympy_utils import (
20
+ sympyify_expression,
21
+ sympyify_vars,
22
+ )
23
+ from classiq.exceptions import (
24
+ ClassiqCombOptNoSolutionError,
25
+ ClassiqCombOptTrivialProblemError,
26
+ )
27
+
28
+
29
+ def remove_fixed_variables(model: ConcreteModel) -> ConcreteModel:
30
+ _should_iterate_fixing = _should_start_fixing(model)
31
+
32
+ while _should_iterate_fixing:
33
+ _change_fixing_constraints_to_fixed_value(model)
34
+ variables = pyomo_utils.extract(model, Var)
35
+
36
+ substitution_dict = {id(var): _get_value_if_exists(var) for var in variables}
37
+
38
+ encoding_utils.encode_constraints(model, substitution_dict)
39
+ encoding_utils.encode_objective(model, substitution_dict)
40
+ _remove_empty_constraints(model)
41
+
42
+ _should_iterate_fixing = _should_continue_fixing(model)
43
+
44
+ assigned_model = copy.deepcopy(model)
45
+ _check_empty_model(assigned_model)
46
+ _remove_assigned_variables(model)
47
+
48
+ return assigned_model
49
+
50
+
51
+ def _should_continue_fixing(model: ConcreteModel) -> bool:
52
+ return bool(len(_get_fixing_constraints(model)))
53
+
54
+
55
+ def _should_start_fixing(model: ConcreteModel) -> bool:
56
+ variables = pyomo_utils.extract(model, Var)
57
+ is_some_var_fixed = any(var.value is not None for var in variables)
58
+
59
+ return is_some_var_fixed or _should_continue_fixing(model)
60
+
61
+
62
+ def _change_fixing_constraints_to_fixed_value(model: ConcreteModel) -> None:
63
+ for constraint in _get_fixing_constraints(model):
64
+ var, var_value = _get_var_and_value_from_fixing_constraint(constraint)
65
+
66
+ if var.value is not None and var.value != var_value:
67
+ raise ClassiqCombOptNoSolutionError
68
+
69
+ if int(var_value) != var_value or var_value < var.lb or var_value > var.ub:
70
+ raise ClassiqCombOptNoSolutionError
71
+
72
+ var.fix(int(var_value))
73
+ pyomo_utils.delete_component(model, constraint)
74
+
75
+
76
+ def _get_fixing_constraints(model: ConcreteModel) -> List[_GeneralConstraintData]:
77
+ constraints = pyomo_utils.extract(model, _GeneralConstraintData)
78
+ return list(filter(_is_fixing_constraint, constraints))
79
+
80
+
81
+ def _get_var_and_value_from_fixing_constraint(
82
+ constraint: _GeneralConstraintData,
83
+ ) -> Tuple[_GeneralVarData, float]:
84
+ var = next(identify_variables(constraint.body))
85
+
86
+ if isinstance(constraint.body, _GeneralVarData):
87
+ return var, constraint.upper.value
88
+
89
+ symbols_map = sympyify_vars([var])
90
+ sympy_exp = sympyify_expression(constraint.expr, symbols_map)
91
+ sympy_var = symbols_map.getSympySymbol(var)
92
+
93
+ isolated_exp = isolate(sympy_exp, sympy_var)
94
+
95
+ return var, float(isolated_exp.args[1])
96
+
97
+
98
+ def _remove_assigned_variables(model: ConcreteModel) -> None:
99
+ for var in pyomo_utils.extract(model, Var):
100
+ if var.value is not None:
101
+ pyomo_utils.delete_component(model, var)
102
+
103
+
104
+ def _remove_empty_constraints(model: ConcreteModel) -> None:
105
+ # (reduces number of slack variables and potential errors)
106
+ for constraint in pyomo_utils.extract(model, _GeneralConstraintData):
107
+ sympyified_expression = sympy_utils.sympyify_expression(constraint.expr)
108
+ deal_with_trivial_boolean_constraint(constraint, sympyified_expression, model)
109
+
110
+
111
+ def add_fixed_variables_to_solution(
112
+ original_model: ConcreteModel, solution: List[int]
113
+ ) -> List[int]:
114
+ variables = pyomo_utils.extract(original_model, Var)
115
+ solution_iter = iter(solution)
116
+ # var.value might be 0 as well
117
+ solution_with_fixed = []
118
+ for var in variables:
119
+ if var.value is not None:
120
+ solution_with_fixed.append(var.value)
121
+ else:
122
+ solution_with_fixed.append(next(solution_iter, 0))
123
+ solution_with_fixed.extend(list(solution_iter))
124
+ return solution_with_fixed
125
+
126
+
127
+ def _get_value_if_exists(var: _GeneralVarData) -> Union[int, _GeneralVarData]:
128
+ return var.value if var.value is not None else var
129
+
130
+
131
+ def _is_fixing_constraint(constraint: _GeneralConstraintData) -> bool:
132
+ return (
133
+ isinstance(constraint.expr, EqualityExpression)
134
+ and len(list(identify_variables(constraint.body))) == 1
135
+ )
136
+
137
+
138
+ def _check_empty_model(model: ConcreteModel) -> None:
139
+ variables = pyomo_utils.extract(model, Var)
140
+ if all(var.value is not None for var in variables):
141
+ solution = add_fixed_variables_to_solution(original_model=model, solution=[])
142
+ raise ClassiqCombOptTrivialProblemError(solution)