luna-quantum 1.1.0__cp312-cp312-win_amd64.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 (276) hide show
  1. luna_quantum/__init__.py +139 -0
  2. luna_quantum/__init__.pyi +98 -0
  3. luna_quantum/_core.cp312-win_amd64.pyd +0 -0
  4. luna_quantum/_core.pyi +4286 -0
  5. luna_quantum/_utility.py +148 -0
  6. luna_quantum/_utility.pyi +20 -0
  7. luna_quantum/algorithms/__init__.py +1 -0
  8. luna_quantum/aqm_overwrites/__init__.py +3 -0
  9. luna_quantum/aqm_overwrites/model.py +184 -0
  10. luna_quantum/backends/__init__.py +1 -0
  11. luna_quantum/client/__init__.py +0 -0
  12. luna_quantum/client/controllers/__init__.py +4 -0
  13. luna_quantum/client/controllers/luna_http_client.py +37 -0
  14. luna_quantum/client/controllers/luna_platform_client.py +256 -0
  15. luna_quantum/client/controllers/luna_q.py +67 -0
  16. luna_quantum/client/controllers/luna_solve.py +129 -0
  17. luna_quantum/client/error/__init__.py +0 -0
  18. luna_quantum/client/error/luna_api_key_invalid_error.py +10 -0
  19. luna_quantum/client/error/luna_api_key_missing_error.py +10 -0
  20. luna_quantum/client/error/luna_error.py +2 -0
  21. luna_quantum/client/error/luna_server_error.py +20 -0
  22. luna_quantum/client/error/timeout_error.py +12 -0
  23. luna_quantum/client/error/transformation_error.py +18 -0
  24. luna_quantum/client/error/utils/__init__.py +0 -0
  25. luna_quantum/client/error/utils/http_error_utils.py +112 -0
  26. luna_quantum/client/interfaces/__init__.py +4 -0
  27. luna_quantum/client/interfaces/clients/__init__.py +25 -0
  28. luna_quantum/client/interfaces/clients/circuit_rest_client_i.py +68 -0
  29. luna_quantum/client/interfaces/clients/info_rest_client_i.py +53 -0
  30. luna_quantum/client/interfaces/clients/model_rest_client_i.py +139 -0
  31. luna_quantum/client/interfaces/clients/qpu_token_rest_client_i.py +364 -0
  32. luna_quantum/client/interfaces/clients/rest_client_i.py +21 -0
  33. luna_quantum/client/interfaces/clients/solve_job_rest_client_i.py +201 -0
  34. luna_quantum/client/interfaces/clients/users_rest_client_i.py +29 -0
  35. luna_quantum/client/interfaces/services/__init__.py +0 -0
  36. luna_quantum/client/interfaces/services/luna_q_i.py +34 -0
  37. luna_quantum/client/interfaces/services/luna_solve_i.py +72 -0
  38. luna_quantum/client/interfaces/services/service_i.py +56 -0
  39. luna_quantum/client/rest_client/__init__.py +15 -0
  40. luna_quantum/client/rest_client/circuit_rest_client.py +107 -0
  41. luna_quantum/client/rest_client/info_rest_client.py +74 -0
  42. luna_quantum/client/rest_client/model_rest_client.py +216 -0
  43. luna_quantum/client/rest_client/qpu_token_rest_client.py +508 -0
  44. luna_quantum/client/rest_client/solve_job_rest_client.py +286 -0
  45. luna_quantum/client/rest_client/users_rest_client.py +35 -0
  46. luna_quantum/client/schemas/__init__.py +26 -0
  47. luna_quantum/client/schemas/circuit.py +48 -0
  48. luna_quantum/client/schemas/create/__init__.py +15 -0
  49. luna_quantum/client/schemas/create/circuit.py +30 -0
  50. luna_quantum/client/schemas/create/optimization.py +39 -0
  51. luna_quantum/client/schemas/create/qpu_token.py +22 -0
  52. luna_quantum/client/schemas/create/qpu_token_time_quota.py +35 -0
  53. luna_quantum/client/schemas/create/qpu_token_time_quota_update.py +24 -0
  54. luna_quantum/client/schemas/create/qubo.py +19 -0
  55. luna_quantum/client/schemas/create/solve_job_create.py +43 -0
  56. luna_quantum/client/schemas/enums/__init__.py +0 -0
  57. luna_quantum/client/schemas/enums/call_style.py +13 -0
  58. luna_quantum/client/schemas/enums/circuit.py +42 -0
  59. luna_quantum/client/schemas/enums/model_format.py +11 -0
  60. luna_quantum/client/schemas/enums/problem.py +50 -0
  61. luna_quantum/client/schemas/enums/qpu_token_type.py +20 -0
  62. luna_quantum/client/schemas/enums/sense.py +8 -0
  63. luna_quantum/client/schemas/enums/status.py +40 -0
  64. luna_quantum/client/schemas/enums/timeframe.py +11 -0
  65. luna_quantum/client/schemas/error_message.py +14 -0
  66. luna_quantum/client/schemas/model_metadata.py +35 -0
  67. luna_quantum/client/schemas/qpu_token/__init__.py +0 -0
  68. luna_quantum/client/schemas/qpu_token/qpu_token.py +154 -0
  69. luna_quantum/client/schemas/qpu_token/qpu_token_source.py +19 -0
  70. luna_quantum/client/schemas/qpu_token/qpu_token_time_quota.py +30 -0
  71. luna_quantum/client/schemas/qpu_token/token_provider.py +132 -0
  72. luna_quantum/client/schemas/representation.py +19 -0
  73. luna_quantum/client/schemas/solution.py +106 -0
  74. luna_quantum/client/schemas/solve_job.py +50 -0
  75. luna_quantum/client/schemas/solver_info.py +11 -0
  76. luna_quantum/client/schemas/user.py +11 -0
  77. luna_quantum/client/schemas/wrappers/__init__.py +5 -0
  78. luna_quantum/client/schemas/wrappers/datetime_wrapper.py +32 -0
  79. luna_quantum/client/utils/__init__.py +0 -0
  80. luna_quantum/client/utils/qpu_token_utils.py +147 -0
  81. luna_quantum/config.py +11 -0
  82. luna_quantum/decorators.py +248 -0
  83. luna_quantum/errors.py +34 -0
  84. luna_quantum/errors.pyi +287 -0
  85. luna_quantum/exceptions/__init__.py +0 -0
  86. luna_quantum/exceptions/base_luna_quantum_error.py +2 -0
  87. luna_quantum/exceptions/luna_quantum_call_type_error.py +9 -0
  88. luna_quantum/exceptions/patch_class_field_exists_error.py +10 -0
  89. luna_quantum/factories/__init__.py +4 -0
  90. luna_quantum/factories/luna_solve_client_factory.py +100 -0
  91. luna_quantum/factories/usecase_factory.py +489 -0
  92. luna_quantum/py.typed +0 -0
  93. luna_quantum/solve/__init__.py +13 -0
  94. luna_quantum/solve/default_token.py +304 -0
  95. luna_quantum/solve/domain/__init__.py +0 -0
  96. luna_quantum/solve/domain/abstract/__init__.py +4 -0
  97. luna_quantum/solve/domain/abstract/luna_algorithm.py +205 -0
  98. luna_quantum/solve/domain/abstract/qpu_token_backend.py +34 -0
  99. luna_quantum/solve/domain/model_metadata.py +56 -0
  100. luna_quantum/solve/domain/solve_job.py +230 -0
  101. luna_quantum/solve/errors/__init__.py +0 -0
  102. luna_quantum/solve/errors/incompatible_backend_error.py +15 -0
  103. luna_quantum/solve/errors/model_metadata_missing_error.py +11 -0
  104. luna_quantum/solve/errors/solve_base_error.py +5 -0
  105. luna_quantum/solve/errors/token_missing_error.py +11 -0
  106. luna_quantum/solve/interfaces/__init__.py +0 -0
  107. luna_quantum/solve/interfaces/algorithm_i.py +49 -0
  108. luna_quantum/solve/interfaces/backend_i.py +28 -0
  109. luna_quantum/solve/interfaces/usecases/__init__.py +59 -0
  110. luna_quantum/solve/interfaces/usecases/model_delete_usecase_i.py +27 -0
  111. luna_quantum/solve/interfaces/usecases/model_fetch_metadata_usecase_i.py +33 -0
  112. luna_quantum/solve/interfaces/usecases/model_get_solutions_usecase_i.py +33 -0
  113. luna_quantum/solve/interfaces/usecases/model_get_solve_jobs_usecase_i.py +33 -0
  114. luna_quantum/solve/interfaces/usecases/model_load_by_id_usecase_i.py +32 -0
  115. luna_quantum/solve/interfaces/usecases/model_load_by_metadata_usecase_i.py +37 -0
  116. luna_quantum/solve/interfaces/usecases/model_load_metadata_by_hash_usecase_i.py +38 -0
  117. luna_quantum/solve/interfaces/usecases/model_save_usecase_i.py +36 -0
  118. luna_quantum/solve/interfaces/usecases/solve_job_cancel_usecase_i.py +33 -0
  119. luna_quantum/solve/interfaces/usecases/solve_job_create_usecase_i.py +44 -0
  120. luna_quantum/solve/interfaces/usecases/solve_job_delete_usecase_i.py +32 -0
  121. luna_quantum/solve/interfaces/usecases/solve_job_fetch_updates_usecase_i.py +38 -0
  122. luna_quantum/solve/interfaces/usecases/solve_job_get_by_id_usecase_i.py +27 -0
  123. luna_quantum/solve/interfaces/usecases/solve_job_get_result_usecase_i.py +63 -0
  124. luna_quantum/solve/parameters/__init__.py +0 -0
  125. luna_quantum/solve/parameters/algorithms/__init__.py +51 -0
  126. luna_quantum/solve/parameters/algorithms/base_params/__init__.py +24 -0
  127. luna_quantum/solve/parameters/algorithms/base_params/decomposer.py +57 -0
  128. luna_quantum/solve/parameters/algorithms/base_params/qaoa_circuit_params.py +95 -0
  129. luna_quantum/solve/parameters/algorithms/base_params/quantum_annealing_params.py +79 -0
  130. luna_quantum/solve/parameters/algorithms/base_params/scipy_optimizer.py +122 -0
  131. luna_quantum/solve/parameters/algorithms/base_params/simulated_annealing_params.py +106 -0
  132. luna_quantum/solve/parameters/algorithms/base_params/tabu_kerberos_params.py +39 -0
  133. luna_quantum/solve/parameters/algorithms/base_params/tabu_search_params.py +129 -0
  134. luna_quantum/solve/parameters/algorithms/flexible_parameter_algorithm.py +59 -0
  135. luna_quantum/solve/parameters/algorithms/genetic_algorithms/__init__.py +4 -0
  136. luna_quantum/solve/parameters/algorithms/genetic_algorithms/qaga.py +131 -0
  137. luna_quantum/solve/parameters/algorithms/genetic_algorithms/saga.py +139 -0
  138. luna_quantum/solve/parameters/algorithms/lq_fda/__init__.py +3 -0
  139. luna_quantum/solve/parameters/algorithms/lq_fda/fujits_da_base.py +85 -0
  140. luna_quantum/solve/parameters/algorithms/lq_fda/fujitsu_da_v3c.py +155 -0
  141. luna_quantum/solve/parameters/algorithms/optimization_solvers/__init__.py +3 -0
  142. luna_quantum/solve/parameters/algorithms/optimization_solvers/scip.py +51 -0
  143. luna_quantum/solve/parameters/algorithms/quantum_annealing/__init__.py +19 -0
  144. luna_quantum/solve/parameters/algorithms/quantum_annealing/kerberos.py +149 -0
  145. luna_quantum/solve/parameters/algorithms/quantum_annealing/leap_hybrid_bqm.py +75 -0
  146. luna_quantum/solve/parameters/algorithms/quantum_annealing/leap_hybrid_cqm.py +75 -0
  147. luna_quantum/solve/parameters/algorithms/quantum_annealing/parallel_tempering_qpu.py +139 -0
  148. luna_quantum/solve/parameters/algorithms/quantum_annealing/population_annealing_qpu.py +109 -0
  149. luna_quantum/solve/parameters/algorithms/quantum_annealing/qbsolv_like_qpu.py +111 -0
  150. luna_quantum/solve/parameters/algorithms/quantum_annealing/quantum_annealing.py +121 -0
  151. luna_quantum/solve/parameters/algorithms/quantum_annealing/repeated_reverse_quantum_annealing.py +174 -0
  152. luna_quantum/solve/parameters/algorithms/quantum_gate/__init__.py +6 -0
  153. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/__init__.py +10 -0
  154. luna_quantum/solve/parameters/algorithms/quantum_gate/flexqaoa/__init__.py +29 -0
  155. luna_quantum/solve/parameters/algorithms/quantum_gate/flexqaoa/config.py +58 -0
  156. luna_quantum/solve/parameters/algorithms/quantum_gate/flexqaoa/flexqaoa.py +188 -0
  157. luna_quantum/solve/parameters/algorithms/quantum_gate/flexqaoa/optimizers.py +53 -0
  158. luna_quantum/solve/parameters/algorithms/quantum_gate/flexqaoa/pipeline.py +164 -0
  159. luna_quantum/solve/parameters/algorithms/quantum_gate/qaoa.py +112 -0
  160. luna_quantum/solve/parameters/algorithms/quantum_gate/qaoa_fo.py +69 -0
  161. luna_quantum/solve/parameters/algorithms/quantum_gate/vqe.py +108 -0
  162. luna_quantum/solve/parameters/algorithms/search_algorithms/__init__.py +5 -0
  163. luna_quantum/solve/parameters/algorithms/search_algorithms/dialectic_search.py +136 -0
  164. luna_quantum/solve/parameters/algorithms/search_algorithms/qbsolv_like_tabu_search.py +117 -0
  165. luna_quantum/solve/parameters/algorithms/search_algorithms/tabu_search.py +126 -0
  166. luna_quantum/solve/parameters/algorithms/simulated_annealing/__init__.py +13 -0
  167. luna_quantum/solve/parameters/algorithms/simulated_annealing/parallel_tempering.py +131 -0
  168. luna_quantum/solve/parameters/algorithms/simulated_annealing/population_annealing.py +95 -0
  169. luna_quantum/solve/parameters/algorithms/simulated_annealing/qbsolv_like_simulated_annealing.py +141 -0
  170. luna_quantum/solve/parameters/algorithms/simulated_annealing/repeated_reverse_simulated_annealing.py +172 -0
  171. luna_quantum/solve/parameters/algorithms/simulated_annealing/simulated_annealing.py +126 -0
  172. luna_quantum/solve/parameters/backends/__init__.py +27 -0
  173. luna_quantum/solve/parameters/backends/aqarios.py +17 -0
  174. luna_quantum/solve/parameters/backends/aqarios_gpu.py +17 -0
  175. luna_quantum/solve/parameters/backends/aws/__init__.py +11 -0
  176. luna_quantum/solve/parameters/backends/aws/aws.py +36 -0
  177. luna_quantum/solve/parameters/backends/aws/aws_backend_base.py +74 -0
  178. luna_quantum/solve/parameters/backends/aws/ionq.py +43 -0
  179. luna_quantum/solve/parameters/backends/aws/iqm.py +31 -0
  180. luna_quantum/solve/parameters/backends/aws/rigetti.py +31 -0
  181. luna_quantum/solve/parameters/backends/cudaq/__init__.py +5 -0
  182. luna_quantum/solve/parameters/backends/cudaq/cudaq_base.py +16 -0
  183. luna_quantum/solve/parameters/backends/cudaq/cudaq_cpu.py +30 -0
  184. luna_quantum/solve/parameters/backends/cudaq/cudaq_gpu.py +32 -0
  185. luna_quantum/solve/parameters/backends/dwave.py +17 -0
  186. luna_quantum/solve/parameters/backends/dwave_qpu.py +166 -0
  187. luna_quantum/solve/parameters/backends/fda.py +17 -0
  188. luna_quantum/solve/parameters/backends/ibm.py +138 -0
  189. luna_quantum/solve/parameters/backends/qctrl.py +103 -0
  190. luna_quantum/solve/parameters/backends/zib.py +17 -0
  191. luna_quantum/solve/parameters/constants.py +11 -0
  192. luna_quantum/solve/parameters/errors.py +30 -0
  193. luna_quantum/solve/parameters/mixins/__init__.py +0 -0
  194. luna_quantum/solve/parameters/mixins/fujitsu_common_params_mixin.py +239 -0
  195. luna_quantum/solve/parameters/mixins/fujitsu_v2_mixin.py +70 -0
  196. luna_quantum/solve/parameters/mixins/qbsolv_like_mixin.py +60 -0
  197. luna_quantum/solve/use_cases/__init__.py +119 -0
  198. luna_quantum/solve/use_cases/arbitrage_edge_based.py +50 -0
  199. luna_quantum/solve/use_cases/arbitrage_node_based.py +55 -0
  200. luna_quantum/solve/use_cases/base.py +7 -0
  201. luna_quantum/solve/use_cases/binary_integer_linear_programming.py +54 -0
  202. luna_quantum/solve/use_cases/binary_paint_shop_problem.py +37 -0
  203. luna_quantum/solve/use_cases/credit_scoring_feature_selection.py +40 -0
  204. luna_quantum/solve/use_cases/dynamic_portfolio_optimization.py +64 -0
  205. luna_quantum/solve/use_cases/exact_cover.py +51 -0
  206. luna_quantum/solve/use_cases/flight_gate_assignment.py +79 -0
  207. luna_quantum/solve/use_cases/graph_coloring.py +42 -0
  208. luna_quantum/solve/use_cases/graph_isomorphism.py +52 -0
  209. luna_quantum/solve/use_cases/graph_partitioning.py +46 -0
  210. luna_quantum/solve/use_cases/hamiltonian_cycle.py +49 -0
  211. luna_quantum/solve/use_cases/induced_subgraph_isomorphism.py +50 -0
  212. luna_quantum/solve/use_cases/job_shop_scheduling.py +44 -0
  213. luna_quantum/solve/use_cases/k_medoids_clustering.py +49 -0
  214. luna_quantum/solve/use_cases/knapsack_integer_weights.py +56 -0
  215. luna_quantum/solve/use_cases/linear_regression.py +60 -0
  216. luna_quantum/solve/use_cases/lmwcs.py +84 -0
  217. luna_quantum/solve/use_cases/longest_path.py +50 -0
  218. luna_quantum/solve/use_cases/market_graph_clustering.py +61 -0
  219. luna_quantum/solve/use_cases/max2sat.py +54 -0
  220. luna_quantum/solve/use_cases/max3sat.py +55 -0
  221. luna_quantum/solve/use_cases/max_clique.py +60 -0
  222. luna_quantum/solve/use_cases/max_cut.py +48 -0
  223. luna_quantum/solve/use_cases/max_independent_set.py +37 -0
  224. luna_quantum/solve/use_cases/minimal_maximal_matching.py +54 -0
  225. luna_quantum/solve/use_cases/minimal_spanning_tree.py +90 -0
  226. luna_quantum/solve/use_cases/minimum_vertex_cover.py +45 -0
  227. luna_quantum/solve/use_cases/number_partitioning.py +32 -0
  228. luna_quantum/solve/use_cases/portfolio_optimization.py +46 -0
  229. luna_quantum/solve/use_cases/portfolio_optimization_ib_tv.py +63 -0
  230. luna_quantum/solve/use_cases/quadratic_assignment.py +49 -0
  231. luna_quantum/solve/use_cases/quadratic_knapsack.py +48 -0
  232. luna_quantum/solve/use_cases/satellite_scheduling.py +73 -0
  233. luna_quantum/solve/use_cases/sensor_placement.py +58 -0
  234. luna_quantum/solve/use_cases/set_cover.py +56 -0
  235. luna_quantum/solve/use_cases/set_packing.py +54 -0
  236. luna_quantum/solve/use_cases/set_partitioning.py +52 -0
  237. luna_quantum/solve/use_cases/subgraph_isomorphism.py +55 -0
  238. luna_quantum/solve/use_cases/subset_sum.py +37 -0
  239. luna_quantum/solve/use_cases/support_vector_machine.py +64 -0
  240. luna_quantum/solve/use_cases/traffic_flow.py +35 -0
  241. luna_quantum/solve/use_cases/travelling_salesman_problem.py +53 -0
  242. luna_quantum/solve/use_cases/type_aliases.py +9 -0
  243. luna_quantum/solve/use_cases/weighted_max_cut.py +37 -0
  244. luna_quantum/solve/usecases/__init__.py +45 -0
  245. luna_quantum/solve/usecases/model_delete_usecase.py +49 -0
  246. luna_quantum/solve/usecases/model_fetch_metadata_usecase.py +50 -0
  247. luna_quantum/solve/usecases/model_get_solution_usecase.py +59 -0
  248. luna_quantum/solve/usecases/model_get_solve_jobs_usecase.py +62 -0
  249. luna_quantum/solve/usecases/model_load_by_id_usecase.py +47 -0
  250. luna_quantum/solve/usecases/model_load_by_metadata_usecase.py +52 -0
  251. luna_quantum/solve/usecases/model_load_metadata_by_hash_usecase.py +51 -0
  252. luna_quantum/solve/usecases/model_save_usecase.py +63 -0
  253. luna_quantum/solve/usecases/solve_job_cancel_usecase.py +51 -0
  254. luna_quantum/solve/usecases/solve_job_create_usecase.py +112 -0
  255. luna_quantum/solve/usecases/solve_job_delete_usecase.py +38 -0
  256. luna_quantum/solve/usecases/solve_job_fetch_updates_usecase.py +49 -0
  257. luna_quantum/solve/usecases/solve_job_get_by_id_usecase.py +44 -0
  258. luna_quantum/solve/usecases/solve_job_get_result_usecase.py +105 -0
  259. luna_quantum/transformations.py +18 -0
  260. luna_quantum/transformations.pyi +371 -0
  261. luna_quantum/translator.py +23 -0
  262. luna_quantum/translator.pyi +869 -0
  263. luna_quantum/util/__init__.py +0 -0
  264. luna_quantum/util/active_waiting.py +79 -0
  265. luna_quantum/util/class_patcher.py +164 -0
  266. luna_quantum/util/debug_info.py +52 -0
  267. luna_quantum/util/log_utils.py +187 -0
  268. luna_quantum/util/pretty_base.py +67 -0
  269. luna_quantum/util/pydantic_utils.py +38 -0
  270. luna_quantum/utils.py +3 -0
  271. luna_quantum/utils.pyi +67 -0
  272. luna_quantum-1.1.0.dist-info/METADATA +36 -0
  273. luna_quantum-1.1.0.dist-info/RECORD +276 -0
  274. luna_quantum-1.1.0.dist-info/WHEEL +4 -0
  275. luna_quantum-1.1.0.dist-info/licenses/LICENSE +176 -0
  276. luna_quantum-1.1.0.dist-info/licenses/NOTICE +13 -0
luna_quantum/_core.pyi ADDED
@@ -0,0 +1,4286 @@
1
+ from collections.abc import Callable, Iterator
2
+ from datetime import datetime, timedelta
3
+ from enum import Enum
4
+ from types import TracebackType
5
+ from typing import Literal, Self, overload
6
+ from numpy.typing import NDArray
7
+ from . import errors, transformations, translator, utils
8
+ from ._utility import deprecated
9
+ from luna_quantum.client.interfaces.services.luna_solve_i import ILunaSolve
10
+ from luna_quantum.solve.domain.model_metadata import ModelMetadata
11
+ from luna_quantum.solve.domain.solve_job import SolveJob
12
+
13
+ __version__ = ...
14
+
15
+ class Vtype(Enum):
16
+ """
17
+ Enumeration of variable types supported by the optimization system.
18
+
19
+ This enum defines the type of a variable used in a model. The type influences
20
+ the domain and behavior of the variable during optimization. It is often passed
21
+ when defining variables to specify how they should behave.
22
+
23
+ Attributes
24
+ ----------
25
+ Real : Vtype
26
+ Continuous real-valued variable. Can take any value within given bounds.
27
+ Integer : Vtype
28
+ Discrete integer-valued variable. Takes integer values within bounds.
29
+ Binary : Vtype
30
+ Binary variable. Can only take values 0 or 1.
31
+ Spin : Vtype
32
+ Spin variable. Can only take values -1 or +1.
33
+
34
+ Examples
35
+ --------
36
+ >>> from luna_quantum import Vtype
37
+ >>> Vtype.Real
38
+ Real
39
+
40
+ >>> str(Vtype.Binary)
41
+ 'Binary'
42
+ """
43
+
44
+ Real = ...
45
+ """Continuous real-valued variable. Can take any value within given bounds."""
46
+ Integer = ...
47
+ """Discrete integer-valued variable. Takes integer values within bounds."""
48
+ Binary = ...
49
+ """Binary variable. Can only take values 0 or 1."""
50
+ Spin = ...
51
+ """Spin variable. Can only take values -1 or +1."""
52
+
53
+ def __str__(self, /) -> str: ...
54
+ def __repr__(self, /) -> str: ...
55
+
56
+ class Unbounded: ...
57
+
58
+ class Bounds:
59
+ """
60
+ Represents bounds for a variable (only supported for real and integer variables).
61
+
62
+ A `Bounds` object defines the valid interval for a variable. Bounds are inclusive,
63
+ and can be partially specified by providing only a lower or upper limit. If neither
64
+ is specified, the variable is considered unbounded.
65
+
66
+ Parameters
67
+ ----------
68
+ lower : float, optional
69
+ Lower bound of the variable. Defaults to negative infinity if not specified.
70
+ upper : float, optional
71
+ Upper bound of the variable. Defaults to positive infinity if not specified.
72
+
73
+ Examples
74
+ --------
75
+ >>> from luna_quantum import Bounds
76
+ >>> Bounds(-1.0, 1.0)
77
+ Bounds { lower: -1, upper: 1 }
78
+
79
+ >>> Bounds(lower=0.0)
80
+ Bounds { lower: -1, upper: unlimited }
81
+
82
+ >>> Bounds(upper=10.0)
83
+ Bounds { lower: unlimited, upper: 1 }
84
+
85
+ Notes
86
+ -----
87
+ - Bounds are only meaningful for variables of type `Vtype.Real` or `Vtype.Integer`.
88
+ - If both bounds are omitted, the variable is unbounded.
89
+ """
90
+
91
+ @overload
92
+ def __init__(self, /, *, lower: (float | type[Unbounded])) -> None: ...
93
+ @overload
94
+ def __init__(self, /, *, upper: (float | type[Unbounded])) -> None: ...
95
+ @overload
96
+ def __init__(
97
+ self, /, lower: (float | type[Unbounded]), upper: (float | type[Unbounded])
98
+ ) -> None: ...
99
+ def __init__(
100
+ self,
101
+ /,
102
+ lower: (float | type[Unbounded] | None) = ...,
103
+ upper: (float | type[Unbounded] | None) = ...,
104
+ ) -> None:
105
+ """
106
+ Create bounds for a variable.
107
+
108
+ See class-level docstring for full documentation.
109
+ """
110
+ ...
111
+
112
+ @property
113
+ def lower(self, /) -> float | type[Unbounded] | None:
114
+ """Get the lower bound."""
115
+ ...
116
+
117
+ @property
118
+ def upper(self, /) -> float | type[Unbounded] | None:
119
+ """Get the upper bound."""
120
+ ...
121
+
122
+ def __str__(self, /) -> str: ...
123
+ def __repr__(self, /) -> str: ...
124
+
125
+ class Variable:
126
+ """
127
+ Represents a symbolic variable within an optimization environment.
128
+
129
+ A `Variable` is the fundamental building block of algebraic expressions
130
+ used in optimization models. Each variable is tied to an `Environment`
131
+ which scopes its lifecycle and expression context. Variables can be
132
+ typed and optionally bounded.
133
+
134
+ Parameters
135
+ ----------
136
+ name : str
137
+ The name of the variable.
138
+ vtype : Vtype, optional
139
+ The variable type (e.g., `Vtype.Real`, `Vtype.Integer`, etc.).
140
+ Defaults to `Vtype.Binary`.
141
+ bounds : Bounds, optional
142
+ Bounds restricting the range of the variable. Only applicable for
143
+ `Real` and `Integer` variables.
144
+ env : Environment, optional
145
+ The environment in which this variable is created. If not provided,
146
+ the current environment from the context manager is used.
147
+
148
+ Examples
149
+ --------
150
+ >>> from luna_quantum import Variable, Environment, Vtype, Bounds
151
+ >>> with Environment():
152
+ ... x = Variable("x")
153
+ ... y = Variable("y", vtype=Vtype.Integer, bounds=Bounds(0, 5))
154
+ ... expr = 2 * x + y - 1
155
+
156
+ Arithmetic Overloads
157
+ --------------------
158
+ Variables support standard arithmetic operations:
159
+
160
+ - Addition: `x + y`, `x + 2`, `2 + x`
161
+ - Subtraction: `x - y`, `3 - x`
162
+ - Multiplication: `x * y`, `2 * x`, `x * 2`
163
+
164
+ All expressions return `Expression` objects and preserve symbolic structure.
165
+
166
+ Notes
167
+ -----
168
+ - A `Variable` is bound to a specific `Environment` instance.
169
+ - Variables are immutable; all operations yield new `Expression` objects.
170
+ - Variables carry their environment, but the environment does not own the variable.
171
+ """
172
+
173
+ @overload
174
+ def __init__(self, /, name: str) -> None: ...
175
+ @overload
176
+ def __init__(self, /, name: str, *, env: Environment) -> None: ...
177
+ @overload
178
+ def __init__(self, /, name: str, *, env: Environment, vtype: Vtype) -> None: ...
179
+ @overload
180
+ def __init__(self, /, name: str, *, vtype: Vtype) -> None: ...
181
+ @overload
182
+ def __init__(self, /, name: str, *, vtype: Vtype, bounds: Bounds) -> None: ...
183
+ @overload
184
+ def __init__(
185
+ self, /, name: str, *, vtype: Vtype, bounds: Bounds, env: Environment
186
+ ) -> None: ...
187
+ def __init__(
188
+ self,
189
+ /,
190
+ name: str,
191
+ *,
192
+ vtype: (Vtype | None) = ...,
193
+ bounds: (Bounds | None) = ...,
194
+ env: (Environment | None) = ...,
195
+ ) -> None:
196
+ """
197
+ Initialize a new Variable.
198
+
199
+ See class-level docstring for full usage.
200
+
201
+ Raises
202
+ ------
203
+ NoActiveEnvironmentFoundError
204
+ If no active environment is found and none is explicitly provided.
205
+ VariableExistsError
206
+ If a variable with the same name already exists in the environment.
207
+ VariableCreationError
208
+ If the variable is tried to be created with incompatible bounds.
209
+ """
210
+ ...
211
+
212
+ @property
213
+ def id(self, /) -> int:
214
+ """Get the id of the variable."""
215
+ ...
216
+
217
+ @property
218
+ def name(self, /) -> str:
219
+ """Get the name of the variable."""
220
+ ...
221
+
222
+ @property
223
+ def bounds(self, /) -> Bounds:
224
+ """Get the bounds of the variable."""
225
+ ...
226
+
227
+ @property
228
+ def vtype(self, /) -> Vtype:
229
+ """Get the vtype of the variable."""
230
+ ...
231
+
232
+ @overload
233
+ def __add__(self, other: int, /) -> Expression: ...
234
+ @overload
235
+ def __add__(self, other: float, /) -> Expression: ...
236
+ @overload
237
+ def __add__(self, other: Variable, /) -> Expression: ...
238
+ @overload
239
+ def __add__(self, other: Expression, /) -> Expression: ...
240
+ def __add__(self, other: (int | float | Variable | Expression), /) -> Expression:
241
+ """
242
+ Add this variable to another value.
243
+
244
+ Parameters
245
+ ----------
246
+ other : int, float, Variable or Expression.
247
+
248
+ Returns
249
+ -------
250
+ Expression
251
+ The resulting symbolic expression.
252
+
253
+ Raises
254
+ ------
255
+ VariablesFromDifferentEnvsError
256
+ If the operands belong to different environments.
257
+ TypeError
258
+ If the operand type is unsupported.
259
+ """
260
+ ...
261
+
262
+ @overload
263
+ def __radd__(self, other: int, /) -> Expression: ...
264
+ @overload
265
+ def __radd__(self, other: float, /) -> Expression: ...
266
+ @overload
267
+ def __radd__(self, other: Variable, /) -> Expression: ...
268
+ @overload
269
+ def __radd__(self, other: Expression, /) -> Expression: ...
270
+ def __radd__(self, other: (int | float | Variable | Expression), /) -> Expression:
271
+ """
272
+ Right-hand addition.
273
+
274
+ Parameters
275
+ ----------
276
+ other : int, float, Variable or Expression.
277
+
278
+ Returns
279
+ -------
280
+ Expression
281
+ The resulting symbolic expression.
282
+
283
+ Raises
284
+ ------
285
+ TypeError
286
+ If the operand type is unsupported.
287
+ """
288
+ ...
289
+
290
+ @overload
291
+ def __sub__(self, other: int, /) -> Expression: ...
292
+ @overload
293
+ def __sub__(self, other: float, /) -> Expression: ...
294
+ @overload
295
+ def __sub__(self, other: Variable, /) -> Expression: ...
296
+ @overload
297
+ def __sub__(self, other: Expression, /) -> Expression: ...
298
+ def __sub__(self, other: (int | float | Variable | Expression), /) -> Expression:
299
+ """
300
+ Subtract a value from this variable.
301
+
302
+ Parameters
303
+ ----------
304
+ other : int, float, Variable or Expression.
305
+
306
+ Returns
307
+ -------
308
+ Expression
309
+ The resulting symbolic expression.
310
+
311
+ Raises
312
+ ------
313
+ VariablesFromDifferentEnvsError
314
+ If the operands belong to different environments.
315
+ TypeError
316
+ If the operand type is unsupported.
317
+ """
318
+ ...
319
+
320
+ @overload
321
+ def __rsub__(self, other: int, /) -> Expression: ...
322
+ @overload
323
+ def __rsub__(self, other: float, /) -> Expression: ...
324
+ def __rsub__(self, other: (int | float), /) -> Expression:
325
+ """
326
+ Subtract this variable from a scalar (right-hand subtraction).
327
+
328
+ Parameters
329
+ ----------
330
+ other : int or float
331
+
332
+ Returns
333
+ -------
334
+ Expression
335
+ The resulting symbolic expression.
336
+
337
+ Raises
338
+ ------
339
+ TypeError
340
+ If `other` is not a scalar.
341
+ """
342
+ ...
343
+
344
+ @overload
345
+ def __mul__(self, other: int, /) -> Expression: ...
346
+ @overload
347
+ def __mul__(self, other: float, /) -> Expression: ...
348
+ @overload
349
+ def __mul__(self, other: Variable, /) -> Expression: ...
350
+ @overload
351
+ def __mul__(self, other: Expression, /) -> Expression: ...
352
+ def __mul__(self, other: (int | float | Variable | Expression), /) -> Expression:
353
+ """
354
+ Multiply this variable by another value.
355
+
356
+ Parameters
357
+ ----------
358
+ other : Variable, Expression, int, or float
359
+
360
+ Returns
361
+ -------
362
+ Expression
363
+ The resulting symbolic expression.
364
+
365
+ Raises
366
+ ------
367
+ VariablesFromDifferentEnvsError
368
+ If the operands belong to different environments.
369
+ TypeError
370
+ If the operand type is unsupported.
371
+ """
372
+ ...
373
+
374
+ @overload
375
+ def __rmul__(self, other: int, /) -> Expression: ...
376
+ @overload
377
+ def __rmul__(self, other: float, /) -> Expression: ...
378
+ @overload
379
+ def __rmul__(self, other: Variable, /) -> Expression: ...
380
+ @overload
381
+ def __rmul__(self, other: Expression, /) -> Expression: ...
382
+ def __rmul__(self, other: (int | float | Variable | Expression), /) -> Expression:
383
+ """
384
+ Right-hand multiplication for scalars.
385
+
386
+ Parameters
387
+ ----------
388
+ other : int or float
389
+
390
+ Returns
391
+ -------
392
+ Expression
393
+ The resulting symbolic expression.
394
+
395
+ Raises
396
+ ------
397
+ TypeError
398
+ If the operand type is unsupported.
399
+ """
400
+ ...
401
+
402
+ def __pow__(self, other: int, /) -> Expression:
403
+ """
404
+ Raise the variable to the power specified by `other`.
405
+
406
+ Parameters
407
+ ----------
408
+ other : int
409
+
410
+ Returns
411
+ -------
412
+ Expression
413
+
414
+ Raises
415
+ ------
416
+ RuntimeError
417
+ If the param `modulo` usually supported for `__pow__` is specified.
418
+ """
419
+ ...
420
+
421
+ @overload
422
+ def __eq__(self, rhs: int, /) -> Constraint: ...
423
+ @overload
424
+ def __eq__(self, rhs: float, /) -> Constraint: ...
425
+ @overload
426
+ def __eq__(self, rhs: Expression, /) -> Constraint: ...
427
+ @overload
428
+ def __eq__(self, rhs: Variable, /) -> bool:
429
+ """
430
+ Check equality of two variables.
431
+
432
+ Parameters
433
+ ----------
434
+ rhs : Variable
435
+
436
+ Returns
437
+ -------
438
+ bool
439
+ """
440
+
441
+ def __eq__(self, rhs: (int | float | Expression), /) -> Constraint:
442
+ """
443
+ Create a constraint: Variable == float | int | Expression.
444
+
445
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
446
+ constraint, resulting in the following constraint:
447
+
448
+ self - rhs == 0
449
+
450
+ Parameters
451
+ ----------
452
+ rhs : float, int or Expression
453
+
454
+ Returns
455
+ -------
456
+ Constraint
457
+
458
+ Raises
459
+ ------
460
+ TypeError
461
+ If the right-hand side is not of type float, int or Expression.
462
+ """
463
+
464
+ @overload
465
+ def __le__(self, rhs: int, /) -> Constraint: ...
466
+ @overload
467
+ def __le__(self, rhs: float, /) -> Constraint: ...
468
+ @overload
469
+ def __le__(self, rhs: Variable, /) -> Constraint: ...
470
+ @overload
471
+ def __le__(self, rhs: Expression, /) -> Constraint: ...
472
+ def __le__(self, rhs: (int | float | Variable | Expression), /) -> Constraint:
473
+ """
474
+ Create a constraint: Variable <= scalar.
475
+
476
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
477
+ constraint, resulting in the following constraint:
478
+
479
+ self - rhs <= 0
480
+
481
+ Parameters
482
+ ----------
483
+ rhs : float, int, Variable or Expression
484
+
485
+ Returns
486
+ -------
487
+ Constraint
488
+
489
+ Raises
490
+ ------
491
+ TypeError
492
+ If the right-hand side is not of type float, int, Variable or Expression.
493
+ """
494
+ ...
495
+
496
+ @overload
497
+ def __ge__(self, rhs: int, /) -> Constraint: ...
498
+ @overload
499
+ def __ge__(self, rhs: float, /) -> Constraint: ...
500
+ @overload
501
+ def __ge__(self, rhs: Variable, /) -> Constraint: ...
502
+ @overload
503
+ def __ge__(self, rhs: Expression, /) -> Constraint: ...
504
+ def __ge__(self, rhs: (int | float | Variable | Expression), /) -> Constraint:
505
+ """
506
+ Create a constraint: Variable >= scalar.
507
+
508
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
509
+ constraint, resulting in the following constraint:
510
+
511
+ self - rhs >= 0
512
+
513
+ Parameters
514
+ ----------
515
+ rhs : float, int, Variable or Expression
516
+
517
+ Returns
518
+ -------
519
+ Constraint
520
+
521
+ Raises
522
+ ------
523
+ TypeError
524
+ If the right-hand side is not of type float, int, Variable or Expression.
525
+ """
526
+ ...
527
+
528
+ def __neg__(self, /) -> Expression:
529
+ """
530
+ Negate the variable, i.e., multiply it by `-1`.
531
+
532
+ Returns
533
+ -------
534
+ Expression
535
+ """
536
+ ...
537
+
538
+ @property
539
+ def _environment(self, /) -> Environment:
540
+ """Get this variables's environment."""
541
+ ...
542
+
543
+ def __hash__(self, /) -> int: ...
544
+ def __str__(self, /) -> str: ...
545
+ def __repr__(self, /) -> str: ...
546
+
547
+ class Constant:
548
+ """A constant expression.
549
+
550
+ Convenience class to indicate the empty set of variables of an expression's
551
+ constant term when iterating over the expression's components.
552
+
553
+ Note that the bias corresponding to the constant part is not part of this class.
554
+
555
+ Examples
556
+ --------
557
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
558
+ >>> expr: Expression = ...
559
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
560
+ >>> bias: float
561
+ >>> for vars, bias in expr.items():
562
+ >>> match vars:
563
+ >>> case Constant(): do_something_with_constant(bias)
564
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
565
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
566
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
567
+ """
568
+
569
+ class Linear:
570
+ """A linear expression.
571
+
572
+ Convenience class to indicate the variable of an expression's linear term when
573
+ iterating over the expression's components.
574
+
575
+ Note that the bias corresponding to this variable is not part of this class.
576
+
577
+ Examples
578
+ --------
579
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
580
+ >>> expr: Expression = ...
581
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
582
+ >>> bias: float
583
+ >>> for vars, bias in expr.items():
584
+ >>> match vars:
585
+ >>> case Constant(): do_something_with_constant(bias)
586
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
587
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
588
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
589
+ """
590
+
591
+ __match_args__ = ("var",)
592
+
593
+ @property
594
+ def var(self) -> Variable: ...
595
+
596
+ class Quadratic:
597
+ """A quadratic expression.
598
+
599
+ Convenience class to indicate the variables of an expression's quadratic term when
600
+ iterating over the expression's components.
601
+
602
+ Note that the bias corresponding to these two variables is not part of this class.
603
+
604
+ Examples
605
+ --------
606
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
607
+ >>> expr: Expression = ...
608
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
609
+ >>> bias: float
610
+ >>> for vars, bias in expr.items():
611
+ >>> match vars:
612
+ >>> case Constant(): do_something_with_constant(bias)
613
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
614
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
615
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
616
+ """
617
+
618
+ __match_args__ = "var_a", "var_b"
619
+
620
+ @property
621
+ def var_a(self) -> Variable: ...
622
+ @property
623
+ def var_b(self) -> Variable: ...
624
+
625
+ class HigherOrder:
626
+ """A higher-order expression.
627
+
628
+ Convenience class to indicate the set of variables of an expression's higher-order
629
+ term when iterating over the expression's components.
630
+
631
+ Note that the bias corresponding to these variables is not part of this class.
632
+
633
+ Examples
634
+ --------
635
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
636
+ >>> expr: Expression = ...
637
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
638
+ >>> bias: float
639
+ >>> for vars, bias in expr.items():
640
+ >>> match vars:
641
+ >>> case Constant(): do_something_with_constant(bias)
642
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
643
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
644
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
645
+ """
646
+
647
+ __match_args__ = ("vars",)
648
+
649
+ @property
650
+ def vars(self) -> list[Variable]: ...
651
+
652
+ class Timing:
653
+ """
654
+ The object that holds information about an algorithm's runtime.
655
+
656
+ This class can only be constructed using a `Timer`. This ensures that a
657
+ `Timing` object always contains a start as well as an end time.
658
+
659
+ The `qpu` field of this class can only be set after constructing it with a timer.
660
+
661
+ Examples
662
+ --------
663
+ >>> from dwave.samplers.tree.solve import BinaryQuadraticModel
664
+ >>> from luna_quantum import Model, Timer, Timing
665
+ >>> model = ... # third-party model
666
+ >>> algorithm = ... # third-party algorithm
667
+ >>> timer = Timer.start()
668
+ >>> sol = algorithm.run(model)
669
+ >>> timing: Timing = timer.stop()
670
+ >>> timing.qpu = sol.qpu_time
671
+ >>> timing.total_seconds
672
+ 1.2999193
673
+ >>> timing.qpu
674
+ 0.02491934
675
+ """
676
+
677
+ @property
678
+ def start(self, /) -> datetime:
679
+ """The starting time of the algorithm."""
680
+ ...
681
+
682
+ @property
683
+ def end(self, /) -> datetime:
684
+ """The end, or finishing, time of the algorithm."""
685
+ ...
686
+
687
+ @property
688
+ def total(self, /) -> timedelta:
689
+ """
690
+ The difference of the end and start time.
691
+
692
+ Raises
693
+ ------
694
+ RuntimeError
695
+ If total cannot be computed due to an inconsistent start or end time.
696
+ """
697
+ ...
698
+
699
+ @property
700
+ def total_seconds(self, /) -> float:
701
+ """
702
+ The total time in seconds an algorithm needed to run.
703
+
704
+ Computed as the difference of end and start time.
705
+
706
+ Raises
707
+ ------
708
+ RuntimeError
709
+ If `total_seconds` cannot be computed due to an inconsistent start or
710
+ end time.
711
+ """
712
+ ...
713
+
714
+ @property
715
+ def qpu(self, /) -> float | None:
716
+ """The qpu usage time of the algorithm this timing object was created for."""
717
+ ...
718
+
719
+ @qpu.setter
720
+ def qpu(self, /, value: (float | None)) -> None:
721
+ """
722
+ Set the qpu usage time.
723
+
724
+ Raises
725
+ ------
726
+ ValueError
727
+ If `value` is negative.
728
+ """
729
+ ...
730
+
731
+ def add_qpu(self, /, value: float) -> None:
732
+ """
733
+ Add qpu usage time to the qpu usage time already present.
734
+
735
+ If the current value is None, this method acts like a setter.
736
+
737
+ Parameters
738
+ ----------
739
+ value : float
740
+ The value to add to the already present qpu value.
741
+
742
+ Raises
743
+ ------
744
+ ValueError
745
+ If `value` is negative.
746
+ """
747
+ ...
748
+
749
+ class Timer:
750
+ """
751
+ Used to measure the computation time of an algorithm.
752
+
753
+ The sole purpose of the `Timer` class is to create a `Timing` object in a safe
754
+ way, i.e., to ensure that the `Timing` object always holds a starting and
755
+ finishing time.
756
+
757
+ Examples
758
+ --------
759
+ Basic usage:
760
+ >>> from luna_quantum import Timer
761
+ >>> timer = Timer.start()
762
+ >>> solution = ... # create a solution by running an algorithm.
763
+ >>> timing = timer.stop()
764
+ """
765
+
766
+ @staticmethod
767
+ def start() -> Timer:
768
+ """
769
+ Create a timer that starts counting immediately.
770
+
771
+ Returns
772
+ -------
773
+ Timer
774
+ The timer.
775
+ """
776
+ ...
777
+
778
+ def stop(self, /) -> Timing:
779
+ """
780
+ Stop the timer, and get the resulting `Timing` object.
781
+
782
+ Returns
783
+ -------
784
+ Timing
785
+ The timing object that holds the start and end time.
786
+ """
787
+ ...
788
+
789
+ class ValueSource(Enum):
790
+ """Toggle enum for choosing the quantity for solution convenience functions."""
791
+
792
+ Obj = ...
793
+ """Use the `obj_values` field."""
794
+ Raw = ...
795
+ """Use the `raw_energies` field."""
796
+
797
+ class Solution:
798
+ """
799
+ The solution object that is obtained by running an algorihtm.
800
+
801
+ The `Solution` class represents a summary of all data obtained from solving a
802
+ model. It contains samples, i.e., assignments of values to each model variable as
803
+ returned by the algorithm, metadata about the solution quality, e.g., the objective
804
+ value, and the runtime of the algorithm.
805
+
806
+ A `Solution` can be constructed explicitly using `from_dict` or by obtaining a
807
+ solution from an algorithm or by converting a different solution format with one of
808
+ the available translators. Note that the latter requires the environment the model
809
+ was created in.
810
+
811
+ Examples
812
+ --------
813
+ Basic usage, assuming that the algorithm already returns a `Solution`:
814
+
815
+ >>> from luna_quantum import Model, Solution
816
+ >>> model: Model = ...
817
+ >>> algorithm = ...
818
+ >>> solution: Solution = algorithm.run(model)
819
+ >>> solution.samples
820
+ [[1, 0, 1], [0, 0, 1]]
821
+
822
+ When you have a `dimod.Sampleset` as the raw solution format:
823
+
824
+ >>> from luna_quantum.translator import BqmTranslator
825
+ >>> from luna_quantum import Model, Solution, DwaveTranslator
826
+ >>> from dimod import SimulatedAnnealingSampler
827
+ >>> model: Model = ...
828
+ >>> bqm = BqmTranslator.from_aq(model)
829
+ >>> sampleset = SimulatedAnnealingSampler().sample(bqm)
830
+ >>> solution = DwaveTranslator.from_dimod_sample_set(sampleset)
831
+ >>> solution.samples
832
+ [[1, 0, 1], [0, 0, 1]]
833
+
834
+ Serialization:
835
+
836
+ >>> blob = solution.encode()
837
+ >>> restored = Solution.decode(blob)
838
+ >>> restored.samples
839
+ [[1, 0, 1], [0, 0, 1]]
840
+
841
+ Notes
842
+ -----
843
+ - To ensure metadata like objective values or feasibility, use
844
+ `model.evaluate(solution)`.
845
+ - Use `encode()` and `decode()` to serialize and recover solutions.
846
+ """
847
+
848
+ def __str__(self, /) -> str: ...
849
+ def __repr__(self, /) -> str: ...
850
+ def __len__(self, /) -> int: ...
851
+ def __iter__(self, /) -> ResultIterator:
852
+ """
853
+ Extract a result view from the `Solution` object.
854
+
855
+ Returns
856
+ -------
857
+ ResultView
858
+
859
+ Raises
860
+ ------
861
+ TypeError
862
+ If `item` has the wrong type.
863
+ IndexError
864
+ If the row index is out of bounds for the variable environment.
865
+ """
866
+ ...
867
+
868
+ def repr_html(self, /) -> str:
869
+ """Represent the solution as a html table.
870
+
871
+ Returns
872
+ -------
873
+ str
874
+ """
875
+
876
+ def __getitem__(self, item: int, /) -> ResultView:
877
+ """
878
+ Extract a result view from the `Solution` object.
879
+
880
+ Returns
881
+ -------
882
+ ResultView
883
+
884
+ Raises
885
+ ------
886
+ TypeError
887
+ If `item` has the wrong type.
888
+ IndexError
889
+ If the row index is out of bounds for the variable environment.
890
+ """
891
+ ...
892
+
893
+ def __eq__(self, other: Solution, /) -> bool:
894
+ """
895
+ Check whether this solution is equal to `other`.
896
+
897
+ Parameters
898
+ ----------
899
+ other : Model
900
+
901
+ Returns
902
+ -------
903
+ bool
904
+ """
905
+ ...
906
+
907
+ def best(self, /) -> ResultView | None:
908
+ """
909
+ Get the best result of the solution if it exists.
910
+
911
+ A best solution is defined as the result with the lowest (in case of Sense.Min)
912
+ or the highest (in case of Sense.Max) objective value that is feasible.
913
+
914
+ Returns
915
+ -------
916
+ ResultView
917
+ The best result of the solution as a view.
918
+ """
919
+ ...
920
+
921
+ @property
922
+ def results(self, /) -> ResultIterator:
923
+ """Get an iterator over the single results of the solution."""
924
+ ...
925
+
926
+ @property
927
+ def samples(self, /) -> Samples:
928
+ """Get a view into the samples of the solution."""
929
+ ...
930
+
931
+ @property
932
+ def obj_values(self, /) -> NDArray | None:
933
+ """
934
+ Get the objective values of the single samples as a ndarray.
935
+
936
+ A value will be None if the sample hasn't yet been evaluated.
937
+ """
938
+ ...
939
+
940
+ @obj_values.setter
941
+ def obj_values(self, other: (NDArray | None)) -> None:
942
+ """Set the objective values of the single samples as a ndarray."""
943
+ ...
944
+
945
+ @property
946
+ def raw_energies(self, /) -> NDArray | None:
947
+ """Get the raw energies.
948
+
949
+ Get the raw energy values of the single samples as returned by the solver /
950
+ algorithm. Will be None if the solver / algorithm did not provide a value.
951
+ """
952
+ ...
953
+
954
+ @raw_energies.setter
955
+ def raw_energies(self, other: (NDArray | None)) -> None:
956
+ """Set the raw energies of the single samples as a ndarray."""
957
+ ...
958
+
959
+ @property
960
+ def counts(self, /) -> NDArray:
961
+ """Return how often each sample occurred in the solution."""
962
+ ...
963
+
964
+ @property
965
+ def runtime(self, /) -> Timing | None:
966
+ """Get the solver / algorithm runtime."""
967
+ ...
968
+
969
+ @runtime.setter
970
+ def runtime(self, /, timing: Timing) -> None:
971
+ """Get the solver / algorithm runtime."""
972
+ ...
973
+
974
+ @property
975
+ def sense(self, /) -> Sense:
976
+ """Get the optimization sense."""
977
+ ...
978
+
979
+ @property
980
+ def best_sample_idx(self, /) -> int | None:
981
+ """Get the index of the sample with the best objective value."""
982
+ ...
983
+
984
+ @property
985
+ def variable_names(self, /) -> list[str]:
986
+ """Get the names of all variables in the solution."""
987
+ ...
988
+
989
+ def cvar(self, /, alpha: float, value_toggle: ValueSource = ...) -> float:
990
+ """
991
+ Compute the Conditional Value at Rist (CVaR) of the solution.
992
+
993
+ Parameters
994
+ ----------
995
+ float : alpha
996
+ The confidence level.
997
+
998
+ Returns
999
+ -------
1000
+ float
1001
+ The CVaR.
1002
+
1003
+ Raises
1004
+ ------
1005
+ ComputationError
1006
+ If the computation fails for any reason.
1007
+ """
1008
+ ...
1009
+
1010
+ def temperature_weighted(
1011
+ self, /, beta: float, value_toggle: ValueSource = ...
1012
+ ) -> float:
1013
+ """
1014
+ Compute the temperature weighted expectation value of the solution.
1015
+
1016
+ Parameters
1017
+ ----------
1018
+ float : beta
1019
+ The inverse temperature for computing Boltzmann weights.
1020
+
1021
+ Returns
1022
+ -------
1023
+ float
1024
+ The temperature weighted expectation value.
1025
+
1026
+ Raises
1027
+ ------
1028
+ ComputationError
1029
+ If the computation fails for any reason.
1030
+ """
1031
+ ...
1032
+
1033
+ def expectation_value(self, /, value_toggle: ValueSource = ...) -> float:
1034
+ """
1035
+ Compute the expectation value of the solution.
1036
+
1037
+ Returns
1038
+ -------
1039
+ float
1040
+ The expectation value.
1041
+
1042
+ Raises
1043
+ ------
1044
+ ComputationError
1045
+ If the computation fails for any reason.
1046
+ """
1047
+ ...
1048
+
1049
+ def feasibility_ratio(self, /) -> float:
1050
+ """
1051
+ Compute the feasibility ratio of the solution.
1052
+
1053
+ Returns
1054
+ -------
1055
+ float
1056
+ The feasibility ratio.
1057
+
1058
+ Raises
1059
+ ------
1060
+ ComputationError
1061
+ If the computation fails for any reason.
1062
+ """
1063
+ ...
1064
+
1065
+ def filter(self, /, f: Callable[[ResultView], bool]) -> Solution:
1066
+ """
1067
+ Get a new solution with all samples for which the condition `f` is true.
1068
+
1069
+ Parameters
1070
+ ----------
1071
+ f : Callable[[ResultView], bool]
1072
+ A filter function yielding true for all samples to be contained in the
1073
+ new solution.
1074
+
1075
+ Returns
1076
+ -------
1077
+ Solution
1078
+ The new solution with only samples for which the condition is true.
1079
+ """
1080
+ ...
1081
+
1082
+ def filter_feasible(self, /) -> Solution:
1083
+ """
1084
+ Get a new solution with all infeasible samples removed.
1085
+
1086
+ Returns
1087
+ -------
1088
+ The new solution with only feasible samples.
1089
+
1090
+ Raises
1091
+ ------
1092
+ ComputationError
1093
+ If the computation fails for any reason.
1094
+ """
1095
+ ...
1096
+
1097
+ def highest_constraint_violation(self, /) -> int | None:
1098
+ """
1099
+ Get the index of the constraint with the highest number of violations.
1100
+
1101
+ Returns
1102
+ -------
1103
+ int | None
1104
+ The index of the constraint with the most violations. None, if the solution
1105
+ was created for an unconstrained model.
1106
+
1107
+ Raises
1108
+ ------
1109
+ ComputationError
1110
+ If the computation fails for any reason.
1111
+ """
1112
+ ...
1113
+
1114
+ @overload
1115
+ def encode(self, /) -> bytes: ...
1116
+ @overload
1117
+ def encode(self, /, *, compress: bool) -> bytes: ...
1118
+ @overload
1119
+ def encode(self, /, *, level: int) -> bytes: ...
1120
+ @overload
1121
+ def encode(self, /, *, compress: bool, level: int) -> bytes: ...
1122
+ def encode(self, /, *, compress: bool = True, level: int = 3) -> bytes:
1123
+ """
1124
+ Serialize the solution into a compact binary format.
1125
+
1126
+ Parameters
1127
+ ----------
1128
+ compress : bool, optional
1129
+ Whether to compress the binary output. Default is True.
1130
+ level : int, optional
1131
+ Compression level (0-9). Default is 3.
1132
+
1133
+ Returns
1134
+ -------
1135
+ bytes
1136
+ Encoded model representation.
1137
+
1138
+ Raises
1139
+ ------
1140
+ IOError
1141
+ If serialization fails.
1142
+ """
1143
+ ...
1144
+
1145
+ @overload
1146
+ def serialize(self, /) -> bytes: ...
1147
+ @overload
1148
+ def serialize(self, /, *, compress: bool) -> bytes: ...
1149
+ @overload
1150
+ def serialize(self, /, *, level: int) -> bytes: ...
1151
+ @overload
1152
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
1153
+ def serialize(
1154
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
1155
+ ) -> bytes:
1156
+ """
1157
+ Alias for `encode()`.
1158
+
1159
+ See `encode()` for details.
1160
+ """
1161
+ ...
1162
+
1163
+ @classmethod
1164
+ def decode(cls, data: bytes) -> Solution:
1165
+ """
1166
+ Reconstruct a solution object from binary data.
1167
+
1168
+ Parameters
1169
+ ----------
1170
+ data : bytes
1171
+ Serialized model blob created by `encode()`.
1172
+
1173
+ Returns
1174
+ -------
1175
+ Solution
1176
+ The reconstructed solution.
1177
+
1178
+ Raises
1179
+ ------
1180
+ DecodeError
1181
+ If decoding fails due to corruption or incompatibility.
1182
+ """
1183
+ ...
1184
+
1185
+ @classmethod
1186
+ def deserialize(cls, data: bytes) -> Solution:
1187
+ """Alias for `decode()`."""
1188
+ ...
1189
+
1190
+ @overload
1191
+ @staticmethod
1192
+ def from_dict(
1193
+ data: dict[Variable, int],
1194
+ *,
1195
+ env: Environment = ...,
1196
+ model: Model = ...,
1197
+ timing: Timing = ...,
1198
+ counts: int = ...,
1199
+ sense: Sense = ...,
1200
+ ) -> Solution: ...
1201
+ @overload
1202
+ @staticmethod
1203
+ def from_dict(
1204
+ data: dict[Variable, float],
1205
+ *,
1206
+ env: Environment = ...,
1207
+ model: Model = ...,
1208
+ timing: Timing = ...,
1209
+ counts: int = ...,
1210
+ sense: Sense = ...,
1211
+ ) -> Solution: ...
1212
+ @overload
1213
+ @staticmethod
1214
+ def from_dict(
1215
+ data: dict[str, int],
1216
+ *,
1217
+ env: Environment = ...,
1218
+ model: Model = ...,
1219
+ timing: Timing = ...,
1220
+ counts: int = ...,
1221
+ sense: Sense = ...,
1222
+ ) -> Solution: ...
1223
+ @overload
1224
+ @staticmethod
1225
+ def from_dict(
1226
+ data: dict[str, float],
1227
+ *,
1228
+ env: Environment = ...,
1229
+ model: Model = ...,
1230
+ timing: Timing = ...,
1231
+ counts: int = ...,
1232
+ sense: Sense = ...,
1233
+ ) -> Solution: ...
1234
+ @overload
1235
+ @staticmethod
1236
+ def from_dict(
1237
+ data: dict[Variable | str, int],
1238
+ *,
1239
+ env: Environment = ...,
1240
+ model: Model = ...,
1241
+ timing: Timing = ...,
1242
+ counts: int = ...,
1243
+ sense: Sense = ...,
1244
+ ) -> Solution: ...
1245
+ @overload
1246
+ @staticmethod
1247
+ def from_dict(
1248
+ data: dict[Variable | str, float],
1249
+ *,
1250
+ env: Environment = ...,
1251
+ model: Model = ...,
1252
+ timing: Timing = ...,
1253
+ counts: int = ...,
1254
+ sense: Sense = ...,
1255
+ ) -> Solution: ...
1256
+ @staticmethod
1257
+ def from_dict(
1258
+ data: dict[Variable | str, int | float],
1259
+ *,
1260
+ env: (Environment | None) = ...,
1261
+ model: (Model | None) = ...,
1262
+ timing: (Timing | None) = ...,
1263
+ counts: (int | None) = ...,
1264
+ sense: (Sense | None) = ...,
1265
+ ) -> Solution:
1266
+ """Create a `Solution` from a dict.
1267
+
1268
+ Create a `Solution` from a dict that maps variables or variable names to their
1269
+ assigned values.
1270
+
1271
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1272
+ there has to be an environment present to determine the correct variable types.
1273
+
1274
+ Parameters
1275
+ ----------
1276
+ data : dict[Variable | str, int | float]
1277
+ The sample that shall be part of the solution.
1278
+ env : Environment, optional
1279
+ The environment the variable types shall be determined from.
1280
+ model : Model, optional
1281
+ A model to evaluate the sample with.
1282
+ counts : int, optional
1283
+ The number of occurrences of this sample.
1284
+
1285
+ Returns
1286
+ -------
1287
+ Solution
1288
+ The solution object created from the sample dict.
1289
+
1290
+ Raises
1291
+ ------
1292
+ NoActiveEnvironmentFoundError
1293
+ If no environment or model is passed to the method or available from the
1294
+ context.
1295
+ ValueError
1296
+ If `env` and `model` are both present. When this is the case, the user's
1297
+ intention is unclear as the model itself already contains an environment.
1298
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1299
+ SolutionTranslationError
1300
+ Generally if the sample translation fails. Might be specified by one of the
1301
+ three following errors.
1302
+ SampleIncorrectLengthErr
1303
+ If a sample has a different number of variables than the environment.
1304
+ SampleUnexpectedVariableError
1305
+ If a sample has a variable that is not present in the environment.
1306
+ ModelVtypeError
1307
+ If the result's variable types are incompatible with the model environment's
1308
+ variable types.
1309
+ """
1310
+ ...
1311
+
1312
+ @overload
1313
+ @staticmethod
1314
+ def from_dicts(
1315
+ data: list[dict[Variable, int]],
1316
+ *,
1317
+ env: Environment = ...,
1318
+ model: Model = ...,
1319
+ timing: Timing = ...,
1320
+ counts: list[int] = ...,
1321
+ sense: Sense = ...,
1322
+ ) -> Solution: ...
1323
+ @overload
1324
+ @staticmethod
1325
+ def from_dicts(
1326
+ data: list[dict[Variable, float]],
1327
+ *,
1328
+ env: Environment = ...,
1329
+ model: Model = ...,
1330
+ timing: Timing = ...,
1331
+ counts: list[int] = ...,
1332
+ sense: Sense = ...,
1333
+ ) -> Solution: ...
1334
+ @overload
1335
+ @staticmethod
1336
+ def from_dicts(
1337
+ data: list[dict[str, int]],
1338
+ *,
1339
+ env: Environment = ...,
1340
+ model: Model = ...,
1341
+ timing: Timing = ...,
1342
+ counts: list[int] = ...,
1343
+ sense: Sense = ...,
1344
+ ) -> Solution: ...
1345
+ @overload
1346
+ @staticmethod
1347
+ def from_dicts(
1348
+ data: list[dict[str, float]],
1349
+ *,
1350
+ env: Environment = ...,
1351
+ model: Model = ...,
1352
+ timing: Timing = ...,
1353
+ counts: list[int] = ...,
1354
+ sense: Sense = ...,
1355
+ ) -> Solution: ...
1356
+ @overload
1357
+ @staticmethod
1358
+ def from_dicts(
1359
+ data: list[dict[Variable | str, int]],
1360
+ *,
1361
+ env: Environment = ...,
1362
+ model: Model = ...,
1363
+ timing: Timing = ...,
1364
+ counts: list[int] = ...,
1365
+ sense: Sense = ...,
1366
+ ) -> Solution: ...
1367
+ @overload
1368
+ @staticmethod
1369
+ def from_dicts(
1370
+ data: list[dict[Variable | str, float]],
1371
+ *,
1372
+ env: Environment = ...,
1373
+ model: Model = ...,
1374
+ timing: Timing = ...,
1375
+ counts: list[int] = ...,
1376
+ sense: Sense = ...,
1377
+ ) -> Solution: ...
1378
+ @staticmethod
1379
+ def from_dicts(
1380
+ data: list[dict[Variable | str, int | float]],
1381
+ *,
1382
+ env: (Environment | None) = ...,
1383
+ model: (Model | None) = ...,
1384
+ timing: (Timing | None) = ...,
1385
+ counts: (list[int] | None) = ...,
1386
+ sense: (Sense | None) = ...,
1387
+ ) -> Solution:
1388
+ """Create a `Solution` from multiple dicts.
1389
+
1390
+ Create a `Solution` from multiple dicts that map variables or variable names to
1391
+ their assigned values. Duplicate samples contained in the `data` list are
1392
+ aggregated to a single sample.
1393
+
1394
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1395
+ there has to be an environment present to determine the correct variable types.
1396
+
1397
+ Parameters
1398
+ ----------
1399
+ data : list[dict[Variable | str, int | float]]
1400
+ The samples that shall be part of the solution.
1401
+ env : Environment, optional
1402
+ The environment the variable types shall be determined from.
1403
+ model : Model, optional
1404
+ A model to evaluate the sample with.
1405
+ counts : int, optional
1406
+ The number of occurrences for each sample.
1407
+ sense: Sense, optional
1408
+ The sense of the optimization problem.
1409
+
1410
+ Returns
1411
+ -------
1412
+ Solution
1413
+ The solution object created from the sample dict.
1414
+
1415
+ Raises
1416
+ ------
1417
+ NoActiveEnvironmentFoundError
1418
+ If no environment or model is passed to the method or available from the
1419
+ context.
1420
+ ValueError
1421
+ If `env` and `model` are both present. When this is the case, the user's
1422
+ intention is unclear as the model itself already contains an environment.
1423
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1424
+ Or if the the number of samples and the number of counts do not match.
1425
+ SolutionTranslationError
1426
+ Generally if the sample translation fails. Might be specified by one of the
1427
+ three following errors.
1428
+ SampleIncorrectLengthErr
1429
+ If a sample has a different number of variables than the environment.
1430
+ SampleUnexpectedVariableError
1431
+ If a sample has a variable that is not present in the environment.
1432
+ ModelVtypeError
1433
+ If the result's variable types are incompatible with the model environment's
1434
+ variable types.
1435
+ """
1436
+ ...
1437
+
1438
+ @staticmethod
1439
+ def from_counts(
1440
+ data: dict[str, int],
1441
+ *,
1442
+ env: (Environment | None) = ...,
1443
+ model: (Model | None) = ...,
1444
+ timing: (Timing | None) = ...,
1445
+ sense: (Sense | None) = ...,
1446
+ bit_order: Literal["LTR", "RTL"] = "RTL",
1447
+ raw_energies: (list[float] | None) = ...,
1448
+ var_order: (list[str] | None) = ...,
1449
+ ) -> Solution:
1450
+ """
1451
+ Create a `Solution` from a dict that maps measured bitstrings to counts.
1452
+
1453
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1454
+ there has to be an environment present to determine the correct variable types.
1455
+ Only applicable to binary or spin models.
1456
+
1457
+ Parameters
1458
+ ----------
1459
+ data : dict[str, int]
1460
+ The counts that shall be part of the solution.
1461
+ env : Environment, optional
1462
+ The environment the variable types shall be determined from.
1463
+ model : Model, optional
1464
+ A model to evaluate the sample with.
1465
+ timing : Timing, optional
1466
+ The timing for acquiring the solution.
1467
+ sense : Sense, optional
1468
+ The sense the model the solution belongs to. Default: Sense.Min
1469
+ bit_order : Literal["LTR", "RTL"]
1470
+ The order of the bits in the bitstring. Default "RTL".
1471
+ energies: list[float], optional
1472
+ The raw energies for each sample. Default None.
1473
+
1474
+ Returns
1475
+ -------
1476
+ Solution
1477
+ The solution object created from the sample dict.
1478
+
1479
+ Raises
1480
+ ------
1481
+ NoActiveEnvironmentFoundError
1482
+ If no environment or model is passed to the method or available from the
1483
+ context.
1484
+ ValueError
1485
+ If `env` and `model` are both present. When this is the case, the user's
1486
+ intention is unclear as the model itself already contains an environment.
1487
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1488
+ Or if the the environment contains non-(binary or spin) variables.
1489
+ Or if a bitstring contains chars other than '0' and '1'.
1490
+ SolutionTranslationError
1491
+ Generally if the sample translation fails. Might be specified by one of the
1492
+ three following errors.
1493
+ SampleIncorrectLengthErr
1494
+ If a sample has a different number of variables than the environment.
1495
+ """
1496
+ ...
1497
+
1498
+ def print(
1499
+ self,
1500
+ /,
1501
+ layout: Literal["row", "column"] = "column",
1502
+ max_line_length: int = 80,
1503
+ max_column_length: int = 5,
1504
+ max_lines: int = 10,
1505
+ max_var_name_length: int = 10,
1506
+ show_metadata: Literal["before", "after", "hide"] = "after",
1507
+ ) -> None:
1508
+ """
1509
+ Show a solution object as a human-readable string.
1510
+
1511
+ This method provides various ways to customize the way the solution is
1512
+ represented as a string.
1513
+
1514
+ Parameters
1515
+ ----------
1516
+ layout : Literal["row", "column"]
1517
+ With `"row"` layout, all assignments to one variable across different
1518
+ samples are shown in the same *row*, and each sample is shown in one
1519
+ column.
1520
+ With `"column"` layout, all assignments to one variable across different
1521
+ samples are shown in the same *column*, and each sample is shown in one row.
1522
+ max_line_length : int
1523
+ The max number of chars shown in one line or, in other words, the max width
1524
+ of a row.
1525
+ max_column_length : int
1526
+ The maximal number of chars in one column. For both the row and column
1527
+ layout, this controls the max number of chars a single variable assignment
1528
+ may be shown with. For the column layout, this also controls the max number
1529
+ of chars that a variable name is shown with.
1530
+ Note: the max column length cannot always be adhered to. This is
1531
+ specifically the case when a variable assignment is so high that the max
1532
+ column length is not sufficient to show the number correctly.
1533
+ max_lines : int
1534
+ The max number of lines used for showing the samples. Note that this
1535
+ parameter does not influence how metadata are shown, s.t. the total number
1536
+ of lines may be higher than `max_lines`.
1537
+ max_var_name_length : int
1538
+ The max number of chars that a variable is shown with in row layout. This
1539
+ parameter is ignored in column layout.
1540
+ show_metadata : Literal["before", "after", "hide"]
1541
+ Whether and where to show sample-specific metadata such as feasibility and
1542
+ objective value. Note that this parameter only controls how sample-specific
1543
+ metadata are shown. Other metadata, like the solution timing will be shown
1544
+ after the samples regardless of the value of this parameter.
1545
+
1546
+ - `"before"`: show metadata before the actual sample, i.e., above the
1547
+ sample in row layout, and left of the sample in column layout.
1548
+ - `"after"`: show metadata after the actual sample, i.e., below the
1549
+ sample in row layout, and right of the sample in column layout.
1550
+ - "hide": do not show sample-specific metadata.
1551
+
1552
+ Returns
1553
+ -------
1554
+ str
1555
+ The solution represented as a string.
1556
+
1557
+ Raises
1558
+ ------
1559
+ ValueError
1560
+ If at least one of the params has an invalid value.
1561
+ """
1562
+ ...
1563
+
1564
+ @overload
1565
+ def add_var(self, var: Variable, data: list[int | float]) -> None: ...
1566
+ @overload
1567
+ def add_var(
1568
+ self, var: str, data: list[int | float], vtype: (Vtype | None) = ...
1569
+ ) -> None: ...
1570
+ def add_var(
1571
+ self,
1572
+ var: (str | Variable),
1573
+ data: list[int | float],
1574
+ vtype: (Vtype | None) = ...,
1575
+ ) -> None:
1576
+ """Add a variable column to the solution.
1577
+
1578
+ Parameters
1579
+ ----------
1580
+ var : str | Variable
1581
+ The name of the variable for which the sample column is created,
1582
+ or the variable itself.
1583
+ data : list[int | float]
1584
+ The contents of the sample column to be added.
1585
+ vtype : Vtype | None, default None
1586
+ The vtype of the variable for which the sample column is created.
1587
+ If the `var` parameter is a str, the vtype is defaulted to Vtype.Binary.
1588
+ If the `var` is a Variable, the `vtype` parameter is ignored and the
1589
+ vtype of the variable is used.
1590
+
1591
+ Raises
1592
+ ------
1593
+ SampleColumnCreationError
1594
+ """
1595
+
1596
+ @overload
1597
+ def add_vars(
1598
+ self, variables: list[Variable], data: list[list[int | float]]
1599
+ ) -> None: ...
1600
+ @overload
1601
+ def add_vars(
1602
+ self, variables: list[str], data: list[list[int | float]], vtypes: list[Vtype]
1603
+ ) -> None: ...
1604
+ @overload
1605
+ def add_vars(
1606
+ self,
1607
+ variables: list[Variable | str],
1608
+ data: list[list[int | float]],
1609
+ vtypes: list[Vtype | None],
1610
+ ) -> None: ...
1611
+ def add_vars(
1612
+ self,
1613
+ variables: list[Variable | str],
1614
+ data: list[list[int | float]],
1615
+ vtypes: (list[Vtype | None] | None) = ...,
1616
+ ) -> None:
1617
+ """Add multiple variable columns to the solution.
1618
+
1619
+ Parameters
1620
+ ----------
1621
+ vars : list[str | Variable]
1622
+ The names of the variable for which the sample columns are created,
1623
+ or a list of the variables itself.
1624
+ data : list[list[int | float]]
1625
+ A list of the contents of the sample columns to be added.
1626
+ vtypes : list[Vtype] | None
1627
+ The vtypes of the variables for which the sample columns are created.
1628
+ If the `vars` parameter is a `list[str], the vtypes are defaulted to
1629
+ Vtype.Binary.
1630
+ If the `vars` is a list[Variable], the `vtypes` parameter is ignored and the
1631
+ vtypes of the variable is used.
1632
+ For mixed `vars`, the vtype is chosen dynamically following the
1633
+ two rules above.
1634
+
1635
+ Raises
1636
+ ------
1637
+ SampleColumnCreationError
1638
+ """
1639
+
1640
+ def remove_var(self, var: (str | Variable)) -> None:
1641
+ """Remove the sample column for the given variable."""
1642
+ ...
1643
+
1644
+ def remove_vars(self, variables: list[str | Variable]) -> None:
1645
+ """Remove the sample columns for the given variables."""
1646
+ ...
1647
+
1648
+ class SamplesIterator:
1649
+ """
1650
+ An iterator over a solution's samples.
1651
+
1652
+ Examples
1653
+ --------
1654
+ >>> from luna_quantum import Solution
1655
+ >>> solution: Solution = ...
1656
+
1657
+ Note: ``solution.samples`` is automatically converted into a ``SamplesIterator``.
1658
+
1659
+ >>> for sample in solution.samples:
1660
+ ... sample
1661
+ [0, -5, 0.28]
1662
+ [1, -4, -0.42]
1663
+ """
1664
+
1665
+ def __iter__(self, /) -> SamplesIterator: ...
1666
+ def __next__(self, /) -> Sample: ...
1667
+
1668
+ class SampleIterator:
1669
+ """
1670
+ An iterator over the variable assignments of a solution's sample.
1671
+
1672
+ Examples
1673
+ --------
1674
+ >>> from luna_quantum import Solution
1675
+ >>> solution: Solution = ...
1676
+ >>> sample = solution.samples[0]
1677
+
1678
+ Note: ``sample`` is automatically converted into a ``SampleIterator``.
1679
+
1680
+ >>> for var in sample:
1681
+ ... var
1682
+ 0
1683
+ -5
1684
+ 0.28
1685
+ """
1686
+
1687
+ def __iter__(self, /) -> SampleIterator: ...
1688
+ def __next__(self, /) -> int | float: ...
1689
+
1690
+ class Samples:
1691
+ """A set-like object containing every different sample of a solution.
1692
+
1693
+ A samples object is simply a set-like object that contains every different sample
1694
+ of a solution. The ``Samples`` class is readonly as it's merely a helper class for
1695
+ looking into a solution's different samples.
1696
+
1697
+ Examples
1698
+ --------
1699
+ >>> from luna_quantum import Model, Sample, Solution
1700
+ >>> model: Model = ...
1701
+ >>> solution: Solution = ...
1702
+ >>> samples: Samples = solution.samples
1703
+ >>> samples
1704
+ [0, -5, 0.28]
1705
+ [1, -4, -0.42]
1706
+ """
1707
+
1708
+ def __str__(self, /) -> str: ...
1709
+ @overload
1710
+ def __getitem__(self, item: int, /) -> Sample: ...
1711
+ @overload
1712
+ def __getitem__(self, item: tuple[int, int], /) -> int | float: ...
1713
+ def __getitem__(self, item: (int | tuple[int, int]), /) -> int | float:
1714
+ """Extract a sample or variable assignment from the ``Samples`` object.
1715
+
1716
+ If ``item`` is an int, returns the sample in this row. If ``item`` is a tuple
1717
+ of ints `(i, j)`, returns the variable assignment in row `i` and column `j`.
1718
+
1719
+ Returns
1720
+ -------
1721
+ Sample or int or float
1722
+
1723
+ Raises
1724
+ ------
1725
+ TypeError
1726
+ If ``item`` has the wrong type.
1727
+ IndexError
1728
+ If the row or column index is out of bounds for the variable environment.
1729
+ """
1730
+ ...
1731
+
1732
+ def __len__(self, /) -> int:
1733
+ """
1734
+ Get the number of samples present in this sample set.
1735
+
1736
+ Returns
1737
+ -------
1738
+ int
1739
+ """
1740
+ ...
1741
+
1742
+ def __iter__(self, /) -> SamplesIterator:
1743
+ """
1744
+ Iterate over all samples of this sample set.
1745
+
1746
+ Returns
1747
+ -------
1748
+ SamplesIterator
1749
+ """
1750
+ ...
1751
+
1752
+ def tolist(self, /) -> list[list[int | float]]:
1753
+ """Convert sample into a 2-dimensional list.
1754
+
1755
+ Convert the sample into a 2-dimensional list where a row constitutes a single
1756
+ sample, and a column constitutes all assignments for a single variable.
1757
+
1758
+ Returns
1759
+ -------
1760
+ list[list[int | float]]
1761
+ The samples object as a 2-dimensional list.
1762
+ """
1763
+ ...
1764
+
1765
+ class Sample:
1766
+ """Assignment of actual values to the model's variables.
1767
+
1768
+ A sample object is an assignment of an actual value to each of the model's
1769
+ variables.
1770
+
1771
+ The ``Sample`` class is readonly as it's merely a helper class for looking into a
1772
+ single sample of a solution.
1773
+
1774
+ Note: a ``Sample`` can be converted to ``list[int | float]`` simply by calling
1775
+ ``list(sample)``.
1776
+
1777
+ Examples
1778
+ --------
1779
+ >>> from luna_quantum import Model, Sample, Solution
1780
+ >>> model: Model = ...
1781
+ >>> solution: Solution = ...
1782
+ >>> sample: Sample = solution.samples[0]
1783
+ >>> sample
1784
+ [0, -5, 0.28]
1785
+ """
1786
+
1787
+ def __str__(self, /) -> str: ...
1788
+ @overload
1789
+ def __getitem__(self, item: int, /) -> int | float: ...
1790
+ @overload
1791
+ def __getitem__(self, item: Variable, /) -> int | float: ...
1792
+ @overload
1793
+ def __getitem__(self, item: str, /) -> int | float: ...
1794
+ def __getitem__(self, item: (int | Variable | str), /) -> int | float:
1795
+ """
1796
+ Extract a variable assignment from the ``Sample`` object.
1797
+
1798
+ Returns
1799
+ -------
1800
+ int or float
1801
+
1802
+ Raises
1803
+ ------
1804
+ TypeError
1805
+ If ``item`` has the wrong type.
1806
+ IndexError
1807
+ If the row or column index is out of bounds for the variable environment.
1808
+ """
1809
+ ...
1810
+
1811
+ def __len__(self, /) -> int:
1812
+ """
1813
+ Get the number of variables present in this sample.
1814
+
1815
+ Returns
1816
+ -------
1817
+ int
1818
+ """
1819
+ ...
1820
+
1821
+ def __iter__(self, /) -> SampleIterator:
1822
+ """
1823
+ Iterate over all variable assignments of this sample.
1824
+
1825
+ Returns
1826
+ -------
1827
+ SampleIterator
1828
+ """
1829
+ ...
1830
+
1831
+ def to_dict(self, /) -> dict[str, int | float]:
1832
+ """Convert the sample to a dictionary.
1833
+
1834
+ Returns
1835
+ -------
1836
+ dict
1837
+ A dictionary representation of the sample, where the keys are the
1838
+ variable names and the values are the variables' assignments.
1839
+ """
1840
+
1841
+ class ResultIterator:
1842
+ """
1843
+ An iterator over a solution's results.
1844
+
1845
+ Examples
1846
+ --------
1847
+ >>> from luna_quantum import ResultIterator, Solution
1848
+ >>> solution: Solution = ...
1849
+ >>> results: ResultIterator = solution.results
1850
+ >>> for result in results:
1851
+ ... result.sample
1852
+ [0, -5, 0.28]
1853
+ [1, -4, -0.42]
1854
+ """
1855
+
1856
+ def __iter__(self, /) -> ResultIterator: ...
1857
+ def __next__(self, /) -> ResultView: ...
1858
+
1859
+ class Result:
1860
+ """
1861
+ A result object can be understood as a solution with only one sample.
1862
+
1863
+ It can be obtained by calling `model.evaluate_sample` for a single sample.
1864
+
1865
+ Most properties available for the solution object are also available for a result,
1866
+ but in the singular form. For example, you can call `solution.obj_values`, but
1867
+ `result.obj_value`.
1868
+
1869
+ Examples
1870
+ --------
1871
+ >>> from luna_quantum import Model, Result, Solution
1872
+ >>> model: Model = ...
1873
+ >>> solution: Solution = ...
1874
+ >>> sample = solution.samples[0]
1875
+ >>> result = model.evaluate_sample(sample)
1876
+ >>> result.obj_value
1877
+ -109.42
1878
+ >>> result.sample
1879
+ [0, -5, 0.28]
1880
+ >>> result.constraints
1881
+ [True, False]
1882
+ >>> result.feasible
1883
+ False
1884
+ """
1885
+
1886
+ @property
1887
+ def sample(self, /) -> Sample:
1888
+ """Get the sample of the result."""
1889
+ ...
1890
+
1891
+ @property
1892
+ def obj_value(self, /) -> float | None:
1893
+ """Get the objective value of the result."""
1894
+ ...
1895
+
1896
+ @property
1897
+ def constraints(self, /) -> NDArray | None:
1898
+ """The result's feasibility of all constraints.
1899
+
1900
+ Get this result's feasibility values of all constraints. Note that
1901
+ `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1902
+ this result.
1903
+ """
1904
+ ...
1905
+
1906
+ @property
1907
+ def variable_bounds(self, /) -> NDArray | None:
1908
+ """Get this result's feasibility values of all variable bounds."""
1909
+ ...
1910
+
1911
+ @property
1912
+ def feasible(self, /) -> bool | None:
1913
+ """Return whether all constraint results are feasible for this result."""
1914
+ ...
1915
+
1916
+ def __str__(self, /) -> str: ...
1917
+ def __repr__(self, /) -> str: ...
1918
+
1919
+ class ResultView:
1920
+ """
1921
+ A result view object serves as a view into one row of a solution object.
1922
+
1923
+ The `Result` class is readonly as it's merely a helper class for looking into a
1924
+ solution's row, i.e., a single sample and this sample's metadata.
1925
+
1926
+ Most properties available for the solution object are also available for a result,
1927
+ but in the singular form. For example, you can call `solution.obj_values`, but
1928
+ `result.obj_value`.
1929
+
1930
+ Examples
1931
+ --------
1932
+ >>> from luna_quantum import ResultView, Solution
1933
+ >>> solution: Solution = ...
1934
+ >>> result: ResultView = solution[0]
1935
+ >>> result.obj_value
1936
+ -109.42
1937
+ >>> result.sample
1938
+ [0, -5, 0.28]
1939
+ >>> result.constraints
1940
+ [True, False]
1941
+ >>> result.feasible
1942
+ False
1943
+ """
1944
+
1945
+ @property
1946
+ def sample(self, /) -> Sample:
1947
+ """Get the sample of the result."""
1948
+ ...
1949
+
1950
+ @property
1951
+ def counts(self, /) -> int:
1952
+ """Return how often this result appears in the solution."""
1953
+ ...
1954
+
1955
+ @property
1956
+ def obj_value(self, /) -> float | None:
1957
+ """
1958
+ Get the objective value of this sample if present.
1959
+
1960
+ This is the value computed by the corresponding AqModel.
1961
+ """
1962
+ ...
1963
+
1964
+ @property
1965
+ def raw_energy(self, /) -> float | None:
1966
+ """
1967
+ Get the raw energy returned by the algorithm if present.
1968
+
1969
+ This value is not guaranteed to be accurate under consideration of the
1970
+ corresponding AqModel.
1971
+ """
1972
+ ...
1973
+
1974
+ @property
1975
+ def constraints(self, /) -> NDArray | None:
1976
+ """
1977
+ Get this result's feasibility values of all constraints.
1978
+
1979
+ Note that `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1980
+ this result.
1981
+ """
1982
+ ...
1983
+
1984
+ @property
1985
+ def variable_bounds(self, /) -> NDArray | None:
1986
+ """Get this result's feasibility values of all variable bounds."""
1987
+ ...
1988
+
1989
+ @property
1990
+ def feasible(self, /) -> bool | None:
1991
+ """Return whether all constraint results are feasible for this result."""
1992
+ ...
1993
+
1994
+ def __str__(self, /) -> str: ...
1995
+ def __repr__(self, /) -> str: ...
1996
+ def __eq__(self, other: ResultView, /) -> bool: ...
1997
+
1998
+ class Sense(Enum):
1999
+ """
2000
+ Enumeration of optimization senses supported by the optimization system.
2001
+
2002
+ This enum defines the type of optimization used for a model. The type influences
2003
+ the domain and behavior of the model during optimization.
2004
+ """
2005
+
2006
+ Min = ...
2007
+ """Indicate the objective function to be minimized."""
2008
+ Max = ...
2009
+ """Indicate the objective function to be maximized."""
2010
+
2011
+ class ConstraintType(Enum):
2012
+ """
2013
+ Enumeration of constraint types supported by the optimization system.
2014
+
2015
+ This enum defines the type of constraint used within a model.
2016
+ """
2017
+
2018
+ Unconstrained = ...
2019
+ """The model contains no constraints, i.e., is unconstrained."""
2020
+ Equality = ...
2021
+ """The model contains equality constraints (`Comparator.Eq`)."""
2022
+ Inequality = ...
2023
+ """The model contains inequality constraints (`Comparator.Le`, `Comparator.Ge`).
2024
+
2025
+ implicitly includes the `ConstraintType.LessEqual` and `ConstraintType.GreaterEqual`
2026
+ options.
2027
+ """
2028
+ LessEqual = ...
2029
+ """The model contains less-equal-inequality constraints (`Comparator.Le`)."""
2030
+ GreaterEqual = ...
2031
+ """The model contains greater-equal-inequality constraints (`Comparator.Ge`)."""
2032
+
2033
+ class ModelSpecs:
2034
+ """A class containing sepcifications of a model."""
2035
+
2036
+ def __init__(
2037
+ self,
2038
+ /,
2039
+ *,
2040
+ sense: (Sense | None) = ...,
2041
+ vtypes: (list[Vtype] | None) = ...,
2042
+ constraints: (list[ConstraintType] | None) = ...,
2043
+ max_degree: (int | None) = ...,
2044
+ max_constraint_degree: (int | None) = ...,
2045
+ max_num_variables: (int | None) = ...,
2046
+ ) -> None:
2047
+ """Create a ModelSpec instance.
2048
+
2049
+ Parameters
2050
+ ----------
2051
+ sense: Sense | None
2052
+ The exepected Sense of a model, default None.
2053
+ vtypes: list[Vtype] | None
2054
+ The exepected vtypes in a model, default None.
2055
+ constraints: list[ConstraintType] | None = ...,
2056
+ The exepected constraint types in a model, default None.
2057
+ max_degree: int | None
2058
+ The exepected maximum degree of the model's objective function,
2059
+ default None.
2060
+ max_constraint_degree: int | None
2061
+ The exepected maximum degree of the model's constraints, default None.
2062
+ max_num_variables: int | None
2063
+ The exepected maximum number of the variables in the model, default None.
2064
+ """
2065
+ ...
2066
+
2067
+ @property
2068
+ def sense(self) -> Sense | None:
2069
+ """The sense specification, can be `None` if no sense spec is available."""
2070
+ ...
2071
+
2072
+ @property
2073
+ def max_degree(self) -> int | None:
2074
+ """The specification for the max degree of the objective function.
2075
+
2076
+ Can be `None` if no max_degree spec is available.
2077
+ """
2078
+ ...
2079
+
2080
+ @property
2081
+ def max_constraint_degree(self) -> int | None:
2082
+ """The specification for the max degree of all constraints.
2083
+
2084
+ Can be `None` if no max_constraint_degree spec is available.
2085
+ """
2086
+ ...
2087
+
2088
+ @property
2089
+ def max_num_variables(self) -> int | None:
2090
+ """The specification for the max number of variables in the model.
2091
+
2092
+ Can be `None` if no max_num_variables spec is available.
2093
+ """
2094
+ ...
2095
+
2096
+ @property
2097
+ def vtypes(self) -> list[Vtype] | None:
2098
+ """The vtypes specification, can be `None` if no vtypes spec is available."""
2099
+ ...
2100
+
2101
+ @property
2102
+ def constraints(self) -> list[ConstraintType] | None:
2103
+ """
2104
+ The constraints specification.
2105
+
2106
+ Can be `None` if no constraints spec is available.
2107
+ """
2108
+ ...
2109
+
2110
+ def satisfies(self, other: ModelSpecs) -> bool:
2111
+ """Check if `self` satisfies the model specs given in `other`.
2112
+
2113
+ Parameters
2114
+ ----------
2115
+ other : ModelSpecs
2116
+ The model specifications `self` should satisfy.
2117
+ """
2118
+ ...
2119
+
2120
+ def __str__(self, /) -> str: ...
2121
+
2122
+ class Model:
2123
+ """
2124
+ A symbolic optimization model consisting of an objective and constraints.
2125
+
2126
+ The `Model` class represents a structured symbolic optimization problem. It
2127
+ combines a scalar objective `Expression`, a collection of `ConstraintCollection`,
2128
+ and a shared `Environment` that scopes all variables used in the model.
2129
+
2130
+ Models can be constructed explicitly by passing an environment, or implicitly
2131
+ by allowing the model to create its own private environment. If constructed
2132
+ inside an active `Environment` context (via `with Environment()`), that context
2133
+ is used automatically.
2134
+
2135
+ Parameters
2136
+ ----------
2137
+ env : Environment, optional
2138
+ The environment in which variables and expressions are created. If not
2139
+ provided, the model will either use the current context (if active), or
2140
+ create a new private environment.
2141
+ name : str, optional
2142
+ An optional name assigned to the model.
2143
+
2144
+ Examples
2145
+ --------
2146
+ Basic usage:
2147
+
2148
+ >>> from luna_quantum import Model, Variable
2149
+ >>> model = Model("MyModel")
2150
+ >>> with model.environment:
2151
+ ... x = Variable("x")
2152
+ ... y = Variable("y")
2153
+ >>> model.objective = x * y + x
2154
+ >>> model.constraints += x >= 0
2155
+ >>> model.constraints += y <= 5
2156
+
2157
+ With explicit environment:
2158
+
2159
+ >>> from luna_quantum import Environment
2160
+ >>> env = Environment()
2161
+ >>> model = Model("ScopedModel", env)
2162
+ >>> with env:
2163
+ ... x = Variable("x")
2164
+ ... model.objective = x * x
2165
+
2166
+ Serialization:
2167
+
2168
+ >>> blob = model.encode()
2169
+ >>> restored = Model.decode(blob)
2170
+ >>> restored.name
2171
+ 'MyModel'
2172
+
2173
+ Notes
2174
+ -----
2175
+ - The `Model` class does not solve the optimization problem.
2176
+ - Use `.objective`, `.constraints`, and `.environment` to access the symbolic
2177
+ content.
2178
+ - Use `encode()` and `decode()` to serialize and recover models.
2179
+ """
2180
+
2181
+ @overload
2182
+ def __init__(self, /) -> None: ...
2183
+ @overload
2184
+ def __init__(self, /, name: str) -> None: ...
2185
+ @overload
2186
+ def __init__(self, /, name: str, *, sense: Sense) -> None: ...
2187
+ @overload
2188
+ def __init__(self, /, name: str, *, env: Environment) -> None: ...
2189
+ @overload
2190
+ def __init__(self, /, *, sense: Sense) -> None: ...
2191
+ @overload
2192
+ def __init__(self, /, *, env: Environment) -> None: ...
2193
+ @overload
2194
+ def __init__(self, /, *, sense: Sense, env: Environment) -> None: ...
2195
+ @overload
2196
+ def __init__(self, /, name: str, *, sense: Sense, env: Environment) -> None: ...
2197
+ def __init__(
2198
+ self,
2199
+ /,
2200
+ name: (str | None) = ...,
2201
+ *,
2202
+ sense: (Sense | None) = ...,
2203
+ env: (Environment | None) = ...,
2204
+ ) -> None:
2205
+ """
2206
+ Initialize a new symbolic model.
2207
+
2208
+ Parameters
2209
+ ----------
2210
+ name : str, optional
2211
+ An optional name for the model.
2212
+ env : Environment, optional
2213
+ The environment in which the model operates. If not provided, a new
2214
+ environment will be created or inferred from context.
2215
+ """
2216
+ ...
2217
+
2218
+ def set_sense(self, /, sense: Sense) -> None:
2219
+ """
2220
+ Set the optimization sense of a model.
2221
+
2222
+ Parameters
2223
+ ----------
2224
+ sense : Sense
2225
+ The sense of the model (minimization, maximization)
2226
+ """
2227
+ ...
2228
+
2229
+ @overload
2230
+ def add_variable(self, name: str, /) -> Variable: ...
2231
+ @overload
2232
+ def add_variable(self, name: str, /, vtype: (Vtype | None) = ...) -> Variable: ...
2233
+ @overload
2234
+ def add_variable(
2235
+ self, name: str, /, vtype: Vtype, *, lower: (float | type[Unbounded] | None)
2236
+ ) -> Variable: ...
2237
+ @overload
2238
+ def add_variable(
2239
+ self, name: str, /, vtype: Vtype, *, upper: (float | type[Unbounded] | None)
2240
+ ) -> Variable: ...
2241
+ @overload
2242
+ def add_variable(
2243
+ self,
2244
+ name: str,
2245
+ /,
2246
+ vtype: Vtype,
2247
+ *,
2248
+ lower: (float | type[Unbounded] | None),
2249
+ upper: (float | type[Unbounded] | None),
2250
+ ) -> Variable: ...
2251
+ def add_variable(
2252
+ self,
2253
+ name: str,
2254
+ /,
2255
+ vtype: (Vtype | None) = ...,
2256
+ *,
2257
+ lower: (float | type[Unbounded] | None) = ...,
2258
+ upper: (float | type[Unbounded] | None) = ...,
2259
+ ) -> Variable:
2260
+ """
2261
+ Add a new variable to the model.
2262
+
2263
+ Parameters
2264
+ ----------
2265
+ name : str
2266
+ The name of the variable.
2267
+ vtype : Vtype, optional
2268
+ The variable type (e.g., `Vtype.Real`, `Vtype.Integer`, etc.).
2269
+ Defaults to `Vtype.Binary`.
2270
+ lower: float, optional
2271
+ The lower bound restricts the range of the variable. Only applicable for
2272
+ `Real` and `Integer` variables.
2273
+ upper: float, optional
2274
+ The upper bound restricts the range of the variable. Only applicable for
2275
+ `Real` and `Integer` variables.
2276
+
2277
+ Returns
2278
+ -------
2279
+ Variable
2280
+ The variable added to the model.
2281
+ """
2282
+ ...
2283
+
2284
+ @overload
2285
+ def add_variable_with_fallback(self, name: str, /) -> Variable: ...
2286
+ @overload
2287
+ def add_variable_with_fallback(
2288
+ self, name: str, /, vtype: (Vtype | None) = ...
2289
+ ) -> Variable: ...
2290
+ @overload
2291
+ def add_variable_with_fallback(
2292
+ self, name: str, /, vtype: Vtype, *, lower: (float | type[Unbounded] | None)
2293
+ ) -> Variable: ...
2294
+ @overload
2295
+ def add_variable_with_fallback(
2296
+ self, name: str, /, vtype: Vtype, *, upper: (float | type[Unbounded] | None)
2297
+ ) -> Variable: ...
2298
+ @overload
2299
+ def add_variable_with_fallback(
2300
+ self,
2301
+ name: str,
2302
+ /,
2303
+ vtype: Vtype,
2304
+ *,
2305
+ lower: (float | type[Unbounded] | None),
2306
+ upper: (float | type[Unbounded] | None),
2307
+ ) -> Variable: ...
2308
+ def add_variable_with_fallback(
2309
+ self,
2310
+ name: str,
2311
+ /,
2312
+ vtype: (Vtype | None) = ...,
2313
+ *,
2314
+ lower: (float | type[Unbounded] | None) = ...,
2315
+ upper: (float | type[Unbounded] | None) = ...,
2316
+ ) -> Variable:
2317
+ """
2318
+ Add a new variable to the model with fallback renaming.
2319
+
2320
+ Parameters
2321
+ ----------
2322
+ name : str
2323
+ The name of the variable.
2324
+ vtype : Vtype, optional
2325
+ The variable type (e.g., `Vtype.Real`, `Vtype.Integer`, etc.).
2326
+ Defaults to `Vtype.Binary`.
2327
+ lower: float, optional
2328
+ The lower bound restricts the range of the variable. Only applicable for
2329
+ `Real` and `Integer` variables.
2330
+ upper: float, optional
2331
+ The upper bound restricts the range of the variable. Only applicable for
2332
+ `Real` and `Integer` variables.
2333
+
2334
+ Returns
2335
+ -------
2336
+ Variable
2337
+ The variable added to the model.
2338
+ """
2339
+ ...
2340
+
2341
+ def get_variable(self, name: str, /) -> Variable:
2342
+ """Get a variable by its label (name).
2343
+
2344
+ Parameters
2345
+ ----------
2346
+ label : str
2347
+ The name/label of the variable
2348
+
2349
+ Returns
2350
+ -------
2351
+ Variable
2352
+ The variable with the specified label/name.
2353
+
2354
+ Raises
2355
+ ------
2356
+ VariableNotExistingError
2357
+ If no variable with the specified name is registered.
2358
+ """
2359
+ ...
2360
+
2361
+ @property
2362
+ def name(self, /) -> str:
2363
+ """Return the name of the model."""
2364
+ ...
2365
+
2366
+ @name.setter
2367
+ def name(self, /, name: str) -> None:
2368
+ """Set the name of the model."""
2369
+ ...
2370
+
2371
+ @property
2372
+ def sense(self, /) -> Sense:
2373
+ """
2374
+ Get the sense of the model.
2375
+
2376
+ Returns
2377
+ -------
2378
+ Sense
2379
+ The sense of the model (Min or Max).
2380
+ """
2381
+ ...
2382
+
2383
+ @property
2384
+ def objective(self, /) -> Expression:
2385
+ """Get the objective expression of the model."""
2386
+ ...
2387
+
2388
+ @objective.setter
2389
+ def objective(self, value: Expression, /) -> None:
2390
+ """Set the objective expression of the model."""
2391
+ ...
2392
+
2393
+ @property
2394
+ def constraints(self, /) -> ConstraintCollection:
2395
+ """Access the set of constraints associated with the model."""
2396
+ ...
2397
+
2398
+ @constraints.setter
2399
+ def constraints(self, value: ConstraintCollection, /) -> None:
2400
+ """Replace the model's constraints with a new set."""
2401
+ ...
2402
+
2403
+ @property
2404
+ def environment(self, /) -> Environment:
2405
+ """Get the environment in which this model is defined."""
2406
+ ...
2407
+
2408
+ @overload
2409
+ def variables(self, /) -> list[Variable]: ...
2410
+ @overload
2411
+ def variables(self, /, *, active: bool) -> list[Variable]: ...
2412
+ def variables(self, /, active: (bool | None) = ...) -> list[Variable]:
2413
+ """
2414
+ Get all variables that are part of this model.
2415
+
2416
+ Parameters
2417
+ ----------
2418
+ active : bool, optional
2419
+ Instead of all variables from the environment, return only those that are
2420
+ actually present in the model's objective.
2421
+
2422
+ Returns
2423
+ -------
2424
+ The model's variables as a list.
2425
+ """
2426
+ ...
2427
+
2428
+ @overload
2429
+ def add_constraint(self, /, constraint: Constraint) -> None: ...
2430
+ @overload
2431
+ def add_constraint(self, /, constraint: Constraint, name: str) -> None: ...
2432
+ def add_constraint(
2433
+ self, /, constraint: Constraint, name: (str | None) = ...
2434
+ ) -> None:
2435
+ """
2436
+ Add a constraint to the model's constraint collection.
2437
+
2438
+ Parameters
2439
+ ----------
2440
+ constraint : Constraint
2441
+ The constraint to be added.
2442
+ name : str, optional
2443
+ The name of the constraint to be added.
2444
+ """
2445
+ ...
2446
+
2447
+ @overload
2448
+ def set_objective(self, /, expression: Expression) -> None: ...
2449
+ @overload
2450
+ def set_objective(self, /, expression: Expression, *, sense: Sense) -> None: ...
2451
+ def set_objective(
2452
+ self, /, expression: Expression, *, sense: (Sense | None) = ...
2453
+ ) -> None:
2454
+ """
2455
+ Set the model's objective to this expression.
2456
+
2457
+ Parameters
2458
+ ----------
2459
+ expression : Expression
2460
+ The expression assigned to the model's objective.
2461
+ sense : Sense, optional
2462
+ The sense of the model for this objective, by default Sense.Min.
2463
+ """
2464
+ ...
2465
+
2466
+ @property
2467
+ def num_variables(self, /) -> int:
2468
+ """
2469
+ Return the number of variables defined in the model.
2470
+
2471
+ Returns
2472
+ -------
2473
+ int
2474
+ Total number of variables.
2475
+ """
2476
+ ...
2477
+
2478
+ @property
2479
+ def num_constraints(self, /) -> int:
2480
+ """
2481
+ Return the number of constraints defined in the model.
2482
+
2483
+ Returns
2484
+ -------
2485
+ int
2486
+ Total number of constraints.
2487
+ """
2488
+ ...
2489
+
2490
+ def evaluate(self, /, solution: Solution) -> Solution:
2491
+ """
2492
+ Evaluate the model given a solution.
2493
+
2494
+ Parameters
2495
+ ----------
2496
+ solution : Solution
2497
+ The solution used to evaluate the model with.
2498
+
2499
+ Returns
2500
+ -------
2501
+ Solution
2502
+ A new solution object with filled-out information.
2503
+ """
2504
+ ...
2505
+
2506
+ def evaluate_sample(self, /, sample: Sample) -> Result:
2507
+ """
2508
+ Evaluate the model given a single sample.
2509
+
2510
+ Parameters
2511
+ ----------
2512
+ sample : Sample
2513
+ The sample used to evaluate the model with.
2514
+
2515
+ Returns
2516
+ -------
2517
+ Result
2518
+ A result object containing the information from the evaluation process.
2519
+ """
2520
+ ...
2521
+
2522
+ def violated_constraints(self, /, sample: Sample) -> ConstraintCollection:
2523
+ """
2524
+ Get all model constraints that are violated by the given sample.
2525
+
2526
+ Parameters
2527
+ ----------
2528
+ sample : Sample
2529
+ The sample to check constraint feasibility for.
2530
+
2531
+ Returns
2532
+ -------
2533
+ ConstraintCollection
2534
+ The constraints violated by the given sample.
2535
+ """
2536
+ ...
2537
+
2538
+ def substitute(
2539
+ self, /, target: Variable, replacement: (Expression | Variable)
2540
+ ) -> None:
2541
+ """Substitute every occurrence of variable.
2542
+
2543
+ Substitute every occurrence of a variable in the model's objective and
2544
+ constraint expressions with another expression.
2545
+
2546
+ Given a `Model` instance `self`, this method replaces all occurrences of
2547
+ `target` with `replacement` for the objective and each constraint.
2548
+ If any substitution would cross differing environments (e.g. captures from two
2549
+ different scopes), it raises a `DifferentEnvsError`.
2550
+
2551
+ Parameters
2552
+ ----------
2553
+ target : VarRef
2554
+ The variable reference to replace.
2555
+ replacement : Expression
2556
+ The expression to insert in place of `target`.
2557
+
2558
+ Returns
2559
+ -------
2560
+ None
2561
+ Performs substitution in place; no return value.
2562
+
2563
+ Raises
2564
+ ------
2565
+ DifferentEnvsError
2566
+ If the environments of `self`, `target`, and `replacement`
2567
+ are not compatible.
2568
+ """
2569
+ ...
2570
+
2571
+ def get_specs(self) -> ModelSpecs:
2572
+ """Get this model's specs."""
2573
+ ...
2574
+
2575
+ def satisfies(self, specs: ModelSpecs) -> bool:
2576
+ """Check if the model satisfies the given specs.
2577
+
2578
+ Parameters
2579
+ ----------
2580
+ specs : ModelSpecs
2581
+ The sepcs this model's specs are compared to.
2582
+ """
2583
+ ...
2584
+
2585
+ @overload
2586
+ def encode(self, /) -> bytes: ...
2587
+ @overload
2588
+ def encode(self, /, *, compress: bool) -> bytes: ...
2589
+ @overload
2590
+ def encode(self, /, *, level: int) -> bytes: ...
2591
+ @overload
2592
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
2593
+ def encode(
2594
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
2595
+ ) -> bytes:
2596
+ """
2597
+ Serialize the model into a compact binary format.
2598
+
2599
+ Parameters
2600
+ ----------
2601
+ compress : bool, optional
2602
+ Whether to compress the binary output. Default is True.
2603
+ level : int, optional
2604
+ Compression level (0-9). Default is 3.
2605
+
2606
+ Returns
2607
+ -------
2608
+ bytes
2609
+ Encoded model representation.
2610
+
2611
+ Raises
2612
+ ------
2613
+ IOError
2614
+ If serialization fails.
2615
+ """
2616
+ ...
2617
+
2618
+ @overload
2619
+ def serialize(self, /) -> bytes: ...
2620
+ @overload
2621
+ def serialize(self, /, *, compress: bool) -> bytes: ...
2622
+ @overload
2623
+ def serialize(self, /, *, level: int) -> bytes: ...
2624
+ @overload
2625
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
2626
+ def serialize(
2627
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
2628
+ ) -> bytes:
2629
+ """
2630
+ Alias for `encode()`.
2631
+
2632
+ See `encode()` for full documentation.
2633
+ """
2634
+ ...
2635
+
2636
+ @classmethod
2637
+ def decode(cls, data: bytes) -> Model:
2638
+ """
2639
+ Reconstruct a symbolic model from binary data.
2640
+
2641
+ Parameters
2642
+ ----------
2643
+ data : bytes
2644
+ Serialized model blob created by `encode()`.
2645
+
2646
+ Returns
2647
+ -------
2648
+ Model
2649
+ The reconstructed model.
2650
+
2651
+ Raises
2652
+ ------
2653
+ DecodeError
2654
+ If decoding fails due to corruption or incompatibility.
2655
+ """
2656
+ ...
2657
+
2658
+ @classmethod
2659
+ def deserialize(cls, data: bytes) -> Model:
2660
+ """
2661
+ Alias for `decode()`.
2662
+
2663
+ See `decode()` for full documentation.
2664
+ """
2665
+ ...
2666
+
2667
+ def __eq__(self, other: Model, /) -> bool:
2668
+ """
2669
+ Check whether this model is equal to `other`.
2670
+
2671
+ Parameters
2672
+ ----------
2673
+ other : Model
2674
+
2675
+ Returns
2676
+ -------
2677
+ bool
2678
+ """
2679
+ ...
2680
+
2681
+ def equal_contents(self, other: Model, /) -> bool:
2682
+ """
2683
+ Check whether this model has equal contents as `other`.
2684
+
2685
+ Parameters
2686
+ ----------
2687
+ other : Model
2688
+
2689
+ Returns
2690
+ -------
2691
+ bool
2692
+ """
2693
+ ...
2694
+
2695
+ def vtypes(self, /) -> list[Vtype]:
2696
+ """Get a list of all unique variable types of all variables in this model."""
2697
+ ...
2698
+
2699
+ def __str__(self, /) -> str: ...
2700
+ def __repr__(self, /) -> str: ...
2701
+ def __hash__(self, /) -> int: ...
2702
+ def deep_clone(self) -> Model:
2703
+ """Make a deep clone of the model."""
2704
+ ...
2705
+ metadata: ModelMetadata | None = ...
2706
+
2707
+ @staticmethod
2708
+ def load_luna(model_id: str, client: (ILunaSolve | str | None) = None) -> Model: ...
2709
+ def save_luna(self, client: (ILunaSolve | str | None) = None) -> None: ...
2710
+ def delete_luna(self, client: (ILunaSolve | str | None) = None) -> None: ...
2711
+ def load_solutions(
2712
+ self, client: (ILunaSolve | str | None) = None
2713
+ ) -> list[Solution]: ...
2714
+ def load_solve_jobs(
2715
+ self, client: (ILunaSolve | str | None) = None
2716
+ ) -> list[SolveJob]: ...
2717
+
2718
+ class Expression:
2719
+ """
2720
+ Polynomial expression supporting symbolic arithmetic, constraint creation.
2721
+
2722
+ An `Expression` represents a real-valued mathematical function composed of
2723
+ variables, scalars, and coefficients. Expressions may include constant, linear,
2724
+ quadratic, and higher-order terms (cubic and beyond). They are used to build
2725
+ objective functions and constraints in symbolic optimization models.
2726
+
2727
+ Expressions support both regular and in-place arithmetic, including addition and
2728
+ multiplication with integers, floats, `Variable` instances, and other `Expression`s.
2729
+
2730
+ Parameters
2731
+ ----------
2732
+ env : Environment, optional
2733
+ Environment used to scope the expression when explicitly instantiating it.
2734
+ Typically, expressions are constructed implicitly via arithmetic on variables.
2735
+
2736
+ Examples
2737
+ --------
2738
+ Constructing expressions from variables:
2739
+
2740
+ >>> from luna_quantum import Environment, Variable
2741
+ >>> with Environment():
2742
+ ... x = Variable("x")
2743
+ ... y = Variable("y")
2744
+ ... expr = 1 + 2 * x + 3 * x * y + x * y * y
2745
+
2746
+ Inspecting terms:
2747
+
2748
+ >>> expr.get_offset()
2749
+ 1.0
2750
+ >>> expr.get_linear(x)
2751
+ 2.0
2752
+ >>> expr.get_quadratic(x, y)
2753
+ 3.0
2754
+ >>> expr.get_higher_order((x, y, y))
2755
+ 1.0
2756
+
2757
+ In-place arithmetic:
2758
+
2759
+ >>> expr += x
2760
+ >>> expr *= 2
2761
+
2762
+ Creating constraints:
2763
+
2764
+ >>> constraint = expr == 10.0
2765
+ >>> constraint2 = expr <= 15
2766
+
2767
+ Serialization:
2768
+
2769
+ >>> blob = expr.encode()
2770
+ >>> restored = Expression.decode(blob)
2771
+
2772
+ Supported Arithmetic
2773
+ --------------------
2774
+ The following operations are supported:
2775
+
2776
+ - Addition:
2777
+ * `expr + expr` → `Expression`
2778
+ * `expr + variable` → `Expression`
2779
+ * `expr + int | float` → `Expression`
2780
+ * `int | float + expr` → `Expression`
2781
+
2782
+ - In-place addition:
2783
+ * `expr += expr`
2784
+ * `expr += variable`
2785
+ * `expr += int | float`
2786
+
2787
+ - Multiplication:
2788
+ * `expr * expr`
2789
+ * `expr * variable`
2790
+ * `expr * int | float`
2791
+ * `int | float * expr`
2792
+
2793
+ - In-place multiplication:
2794
+ * `expr *= expr`
2795
+ * `expr *= variable`
2796
+ * `expr *= int | float`
2797
+
2798
+ - Constraint creation:
2799
+ * `expr == constant` → `Constraint`
2800
+ * `expr <= constant` → `Constraint`
2801
+ * `expr >= constant` → `Constraint`
2802
+
2803
+ Notes
2804
+ -----
2805
+ - Expressions are mutable: in-place operations (`+=`, `*=`) modify the instance.
2806
+ - Expressions are scoped to an environment via the variables they reference.
2807
+ - Comparisons like `expr == expr` return `bool`, not constraints.
2808
+ - Use `==`, `<=`, `>=` with numeric constants to create constraints.
2809
+ """
2810
+
2811
+ @overload
2812
+ def __init__(self, /) -> None: ...
2813
+ @overload
2814
+ def __init__(self, /, env: Environment) -> None: ...
2815
+ def __init__(self, /, env: (Environment | None) = ...) -> None:
2816
+ """
2817
+ Create a new empty expression scoped to an environment.
2818
+
2819
+ Parameters
2820
+ ----------
2821
+ env : Environment
2822
+ The environment to which this expression is bound.
2823
+
2824
+ Raises
2825
+ ------
2826
+ NoActiveEnvironmentFoundError
2827
+ If no environment is provided and none is active in the context.
2828
+ """
2829
+ ...
2830
+
2831
+ @overload
2832
+ @staticmethod
2833
+ def const(val: float, /) -> Expression: ...
2834
+ @overload
2835
+ @staticmethod
2836
+ def const(val: float, /, env: Environment) -> Expression: ...
2837
+ @staticmethod
2838
+ def const(val: float, /, env: (Environment | None) = None) -> Expression:
2839
+ """Create constant expression.
2840
+
2841
+ Parameters
2842
+ ----------
2843
+ val : float
2844
+ The constant
2845
+
2846
+ Returns
2847
+ -------
2848
+ Expression
2849
+ """
2850
+ ...
2851
+
2852
+ def get_offset(self, /) -> float:
2853
+ """
2854
+ Get the constant (offset) term in the expression.
2855
+
2856
+ Returns
2857
+ -------
2858
+ float
2859
+ The constant term.
2860
+ """
2861
+ ...
2862
+
2863
+ def get_linear(self, /, variable: Variable) -> float:
2864
+ """
2865
+ Get the coefficient of a linear term for a given variable.
2866
+
2867
+ Parameters
2868
+ ----------
2869
+ variable : Variable
2870
+ The variable whose linear coefficient is being queried.
2871
+
2872
+ Returns
2873
+ -------
2874
+ float
2875
+ The coefficient, or 0.0 if the variable is not present.
2876
+
2877
+ Raises
2878
+ ------
2879
+ VariableOutOfRangeError
2880
+ If the variable index is not valid in this expression's environment.
2881
+ """
2882
+ ...
2883
+
2884
+ def get_quadratic(self, /, u: Variable, v: Variable) -> float:
2885
+ """
2886
+ Get the coefficient for a quadratic term (u * v).
2887
+
2888
+ Parameters
2889
+ ----------
2890
+ u : Variable
2891
+ v : Variable
2892
+
2893
+ Returns
2894
+ -------
2895
+ float
2896
+ The coefficient, or 0.0 if not present.
2897
+
2898
+ Raises
2899
+ ------
2900
+ VariableOutOfRangeError
2901
+ If either variable is out of bounds for the expression's environment.
2902
+ """
2903
+ ...
2904
+
2905
+ def get_higher_order(self, /, variables: tuple[Variable, ...]) -> float:
2906
+ """
2907
+ Get the coefficient for a higher-order term (degree ≥ 3).
2908
+
2909
+ Parameters
2910
+ ----------
2911
+ variables : tuple of Variable
2912
+ A tuple of variables specifying the term.
2913
+
2914
+ Returns
2915
+ -------
2916
+ float
2917
+ The coefficient, or 0.0 if not present.
2918
+
2919
+ Raises
2920
+ ------
2921
+ VariableOutOfRangeError
2922
+ If any variable is out of bounds for the environment.
2923
+ """
2924
+ ...
2925
+
2926
+ def items(self, /) -> ExpressionIterator:
2927
+ """
2928
+ Iterate over the single components of an expression.
2929
+
2930
+ An *component* refers to
2931
+ a single constant, linear, quadratic, or higher-order term of an expression.
2932
+
2933
+ Returns
2934
+ -------
2935
+ ExpressionIterator
2936
+ The iterator over the expression's components.
2937
+ """
2938
+ ...
2939
+
2940
+ @property
2941
+ def num_variables(self, /) -> int:
2942
+ """
2943
+ Return the number of distinct variables in the expression.
2944
+
2945
+ Returns
2946
+ -------
2947
+ int
2948
+ Number of variables with non-zero coefficients.
2949
+ """
2950
+ ...
2951
+
2952
+ def variables(self, /) -> list[Variable]:
2953
+ """
2954
+ Get all variables that are part of this expression.
2955
+
2956
+ Returns
2957
+ -------
2958
+ list[Variable]
2959
+ The list of active variables
2960
+ """
2961
+ ...
2962
+
2963
+ def linear_items(self, /) -> list[tuple[Variable, float]]:
2964
+ """
2965
+ Get all linear components.
2966
+
2967
+ Returns
2968
+ -------
2969
+ list[tuple[Variable, float]]
2970
+ The linear components.
2971
+ """
2972
+ ...
2973
+
2974
+ def quadratic_items(self, /) -> list[tuple[Variable, Variable, float]]:
2975
+ """
2976
+ Get all quadratic components.
2977
+
2978
+ Returns
2979
+ -------
2980
+ list[tuple[Variable, Variable, float]]
2981
+ The quadratic components.
2982
+ """
2983
+ ...
2984
+
2985
+ def higher_order_items(self, /) -> list[tuple[list[Variable], float]]:
2986
+ """
2987
+ Get all higher-order components.
2988
+
2989
+ Returns
2990
+ -------
2991
+ list[tuple[list[Variable], float]]
2992
+ The higher-order components.
2993
+ """
2994
+ ...
2995
+
2996
+ def is_constant(self, /) -> bool:
2997
+ """
2998
+ Check if expression is constant.
2999
+
3000
+ Returns
3001
+ -------
3002
+ bool
3003
+ If the expression is constant
3004
+ """
3005
+ ...
3006
+
3007
+ def has_quadratic(self, /) -> bool:
3008
+ """
3009
+ Check if expression has quadratic.
3010
+
3011
+ Returns
3012
+ -------
3013
+ bool
3014
+ If the expression has quadratic
3015
+ """
3016
+ ...
3017
+
3018
+ def has_higher_order(self, /) -> bool:
3019
+ """
3020
+ Check if expression has higher-order.
3021
+
3022
+ Returns
3023
+ -------
3024
+ bool
3025
+ If the expression has higher-order
3026
+ """
3027
+ ...
3028
+
3029
+ def is_equal(self, /, other: Expression) -> bool:
3030
+ """
3031
+ Compare two expressions for equality.
3032
+
3033
+ Parameters
3034
+ ----------
3035
+ other : Expression
3036
+ The expression to which `self` is compared to.
3037
+
3038
+ Returns
3039
+ -------
3040
+ bool
3041
+ If the two expressions are equal.
3042
+ """
3043
+ ...
3044
+
3045
+ def separate(self, variables: list[Variable]) -> tuple[Expression, Expression]:
3046
+ """
3047
+ Separates expression into two expressions based on presence of variables.
3048
+
3049
+ Parameters
3050
+ ----------
3051
+ variables : list[Variable]
3052
+ The variables of which one must at least be present in a left term.
3053
+
3054
+ Returns
3055
+ -------
3056
+ tuple[Expression, Expression]
3057
+ Two expressions, left contains one of the variables right does not, i.e.
3058
+ (contains, does not contain)
3059
+ """
3060
+
3061
+ def substitute(
3062
+ self, /, target: Variable, replacement: (Expression | Variable)
3063
+ ) -> Expression:
3064
+ """
3065
+ Substitute every occurrence of a variable with another expression.
3066
+
3067
+ Given an expression `self`, this method replaces all occurrences of `target`
3068
+ with `replacement`. If the substitution would cross differing environments
3069
+ (e.g. captures from two different scopes), it returns a `DifferentEnvsErr`.
3070
+
3071
+ Parameters
3072
+ ----------
3073
+ target : VarRef
3074
+ The variable reference to replace.
3075
+ replacement : Expression
3076
+ The expression to insert in place of `target`.
3077
+
3078
+ Returns
3079
+ -------
3080
+ Expression
3081
+ The resulting expression after substitution.
3082
+
3083
+ Raises
3084
+ ------
3085
+ DifferentEnvsErr
3086
+ If the environments of `self`, `target` and `replacement`
3087
+ are not compatible.
3088
+ """
3089
+ ...
3090
+
3091
+ def equal_contents(self, other: Expression, /) -> bool:
3092
+ """
3093
+ Check whether this expression has equal contents as `other`.
3094
+
3095
+ Parameters
3096
+ ----------
3097
+ other : Expression
3098
+
3099
+ Returns
3100
+ -------
3101
+ bool
3102
+ """
3103
+ ...
3104
+
3105
+ @overload
3106
+ def encode(self, /) -> bytes: ...
3107
+ @overload
3108
+ def encode(self, /, *, compress: bool) -> bytes: ...
3109
+ @overload
3110
+ def encode(self, /, *, level: int) -> bytes: ...
3111
+ @overload
3112
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
3113
+ def encode(
3114
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
3115
+ ) -> bytes:
3116
+ """
3117
+ Serialize the expression into a compact binary format.
3118
+
3119
+ Parameters
3120
+ ----------
3121
+ compress : bool, optional
3122
+ Whether to compress the data. Default is True.
3123
+ level : int, optional
3124
+ Compression level (0-9). Default is 3.
3125
+
3126
+ Returns
3127
+ -------
3128
+ bytes
3129
+ Encoded representation of the expression.
3130
+
3131
+ Raises
3132
+ ------
3133
+ IOError
3134
+ If serialization fails.
3135
+ """
3136
+ ...
3137
+
3138
+ @overload
3139
+ def serialize(self, /) -> bytes: ...
3140
+ @overload
3141
+ def serialize(self, /, *, compress: bool) -> bytes: ...
3142
+ @overload
3143
+ def serialize(self, /, *, level: int) -> bytes: ...
3144
+ @overload
3145
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
3146
+ def serialize(
3147
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
3148
+ ) -> bytes:
3149
+ """
3150
+ Alias for `encode()`.
3151
+
3152
+ See `encode()` for full documentation.
3153
+ """
3154
+ ...
3155
+
3156
+ @classmethod
3157
+ def decode(cls, data: bytes, env: Environment) -> Expression:
3158
+ """
3159
+ Reconstruct an expression from encoded bytes.
3160
+
3161
+ Parameters
3162
+ ----------
3163
+ data : bytes
3164
+ Binary blob returned by `encode()`.
3165
+ env : Environment
3166
+ The environment of the expression.
3167
+
3168
+ Returns
3169
+ -------
3170
+ Expression
3171
+ Deserialized expression object.
3172
+
3173
+ Raises
3174
+ ------
3175
+ DecodeError
3176
+ If decoding fails due to corruption or incompatibility.
3177
+ """
3178
+ ...
3179
+
3180
+ @classmethod
3181
+ def deserialize(cls, data: bytes, env: Environment) -> Expression:
3182
+ """
3183
+ Alias for `decode()`.
3184
+
3185
+ See `decode()` for full documentation.
3186
+ """
3187
+ ...
3188
+
3189
+ @staticmethod
3190
+ def deep_clone_many(exprs: list[Expression]) -> list[Expression]:
3191
+ """Deep clones all provided expressions into new environment.
3192
+
3193
+ Parameters
3194
+ ----------
3195
+ exprs: list[Expression]
3196
+ The expressions to move to new_environment
3197
+
3198
+ Returns
3199
+ -------
3200
+ list[Expressions]
3201
+ The same expressions but part of a new environment
3202
+ """
3203
+ ...
3204
+
3205
+ @overload
3206
+ def __add__(self, other: Expression, /) -> Expression: ...
3207
+ @overload
3208
+ def __add__(self, other: Variable, /) -> Expression: ...
3209
+ @overload
3210
+ def __add__(self, other: int, /) -> Expression: ...
3211
+ @overload
3212
+ def __add__(self, other: float, /) -> Expression: ...
3213
+ def __add__(self, other: (Expression | Variable | int | float), /) -> Expression:
3214
+ """
3215
+ Add another expression, variable, or scalar.
3216
+
3217
+ Parameters
3218
+ ----------
3219
+ other : Expression, Variable, int, or float
3220
+
3221
+ Returns
3222
+ -------
3223
+ Expression
3224
+
3225
+ Raises
3226
+ ------
3227
+ VariablesFromDifferentEnvsError
3228
+ If operands are from different environments.
3229
+ TypeError
3230
+ If the operand type is unsupported.
3231
+ """
3232
+ ...
3233
+
3234
+ @overload
3235
+ def __radd__(self, other: Expression, /) -> Expression: ...
3236
+ @overload
3237
+ def __radd__(self, other: Variable, /) -> Expression: ...
3238
+ @overload
3239
+ def __radd__(self, other: int, /) -> Expression: ...
3240
+ @overload
3241
+ def __radd__(self, other: float, /) -> Expression: ...
3242
+ def __radd__(self, other: (Expression | Variable | int | float), /) -> Expression:
3243
+ """
3244
+ Add this expression to a scalar or variable.
3245
+
3246
+ Parameters
3247
+ ----------
3248
+ other : int, float, or Variable
3249
+
3250
+ Returns
3251
+ -------
3252
+ Expression
3253
+
3254
+ Raises
3255
+ ------
3256
+ TypeError
3257
+ If the operand type is unsupported.
3258
+ """
3259
+ ...
3260
+
3261
+ @overload
3262
+ def __iadd__(self, other: Expression, /) -> Self: ...
3263
+ @overload
3264
+ def __iadd__(self, other: Variable, /) -> Self: ...
3265
+ @overload
3266
+ def __iadd__(self, other: int, /) -> Self: ...
3267
+ @overload
3268
+ def __iadd__(self, other: float, /) -> Self: ...
3269
+ def __iadd__(self, other: (Expression | Variable | int | float), /) -> Self:
3270
+ """
3271
+ In-place addition.
3272
+
3273
+ Parameters
3274
+ ----------
3275
+ other : Expression, Variable, int, or float
3276
+
3277
+ Returns
3278
+ -------
3279
+ Self
3280
+
3281
+ Raises
3282
+ ------
3283
+ VariablesFromDifferentEnvsError
3284
+ If operands are from different environments.
3285
+ TypeError
3286
+ If the operand type is unsupported.
3287
+ """
3288
+ ...
3289
+
3290
+ @overload
3291
+ def __isub__(self, other: Expression, /) -> Self: ...
3292
+ @overload
3293
+ def __isub__(self, other: Variable, /) -> Self: ...
3294
+ @overload
3295
+ def __isub__(self, other: int, /) -> Self: ...
3296
+ @overload
3297
+ def __isub__(self, other: float, /) -> Self: ...
3298
+ def __isub__(self, other: (Expression | Variable | int | float), /) -> Self:
3299
+ """
3300
+ In-place subtraction.
3301
+
3302
+ Parameters
3303
+ ----------
3304
+ other : Expression, Variable, int, or float
3305
+
3306
+ Returns
3307
+ -------
3308
+ Self
3309
+
3310
+ Raises
3311
+ ------
3312
+ VariablesFromDifferentEnvsError
3313
+ If operands are from different environments.
3314
+ TypeError
3315
+ If the operand type is unsupported.
3316
+ """
3317
+ ...
3318
+
3319
+ @overload
3320
+ def __sub__(self, other: Expression, /) -> Expression: ...
3321
+ @overload
3322
+ def __sub__(self, other: Variable, /) -> Expression: ...
3323
+ @overload
3324
+ def __sub__(self, other: int, /) -> Expression: ...
3325
+ @overload
3326
+ def __sub__(self, other: float, /) -> Expression: ...
3327
+ def __sub__(self, other: (Expression | Variable | int | float), /) -> Expression:
3328
+ """
3329
+ Subtract another expression, variable, or scalar.
3330
+
3331
+ Parameters
3332
+ ----------
3333
+ other : Expression, Variable, int, or float
3334
+
3335
+ Returns
3336
+ -------
3337
+ Expression
3338
+
3339
+ Raises
3340
+ ------
3341
+ VariablesFromDifferentEnvsError
3342
+ If operands are from different environments.
3343
+ TypeError
3344
+ If the operand type is unsupported.
3345
+ """
3346
+ ...
3347
+
3348
+ @overload
3349
+ def __mul__(self, other: Expression, /) -> Expression: ...
3350
+ @overload
3351
+ def __mul__(self, other: Variable, /) -> Expression: ...
3352
+ @overload
3353
+ def __mul__(self, other: int, /) -> Expression: ...
3354
+ @overload
3355
+ def __mul__(self, other: float, /) -> Expression: ...
3356
+ def __mul__(self, other: (Expression | Variable | int | float), /) -> Expression:
3357
+ """
3358
+ Multiply this expression by another value.
3359
+
3360
+ Parameters
3361
+ ----------
3362
+ other : Expression, Variable, int, or float
3363
+
3364
+ Returns
3365
+ -------
3366
+ Expression
3367
+
3368
+ Raises
3369
+ ------
3370
+ VariablesFromDifferentEnvsError
3371
+ If operands are from different environments.
3372
+ TypeError
3373
+ If the operand type is unsupported.
3374
+ """
3375
+ ...
3376
+
3377
+ @overload
3378
+ def __rmul__(self, other: int, /) -> Expression: ...
3379
+ @overload
3380
+ def __rmul__(self, other: float, /) -> Expression: ...
3381
+ def __rmul__(self, other: (int | float), /) -> Expression:
3382
+ """
3383
+ Right-hand multiplication.
3384
+
3385
+ Parameters
3386
+ ----------
3387
+ other : int or float
3388
+
3389
+ Returns
3390
+ -------
3391
+ Expression
3392
+
3393
+ Raises
3394
+ ------
3395
+ TypeError
3396
+ If the operand type is unsupported.
3397
+ """
3398
+ ...
3399
+
3400
+ @overload
3401
+ def __imul__(self, other: Expression, /) -> Self: ...
3402
+ @overload
3403
+ def __imul__(self, other: Variable, /) -> Self: ...
3404
+ @overload
3405
+ def __imul__(self, other: int, /) -> Self: ...
3406
+ @overload
3407
+ def __imul__(self, other: float, /) -> Self: ...
3408
+ def __imul__(self, other: (Expression | Variable | int | float), /) -> Self:
3409
+ """
3410
+ In-place multiplication.
3411
+
3412
+ Parameters
3413
+ ----------
3414
+ other : Expression, Variable, int, or float
3415
+
3416
+ Returns
3417
+ -------
3418
+ Self
3419
+
3420
+ Raises
3421
+ ------
3422
+ VariablesFromDifferentEnvsError
3423
+ If operands are from different environments.
3424
+ TypeError
3425
+ If the operand type is unsupported.
3426
+ """
3427
+ ...
3428
+
3429
+ def __pow__(self, other: int, /) -> Expression:
3430
+ """
3431
+ Raise the expression to the power specified by `other`.
3432
+
3433
+ Parameters
3434
+ ----------
3435
+ other : int
3436
+
3437
+ Returns
3438
+ -------
3439
+ Expression
3440
+
3441
+ Raises
3442
+ ------
3443
+ RuntimeError
3444
+ If the param `modulo` usually supported for `__pow__` is specified.
3445
+ """
3446
+ ...
3447
+
3448
+ @overload
3449
+ def __eq__(self, rhs: Expression, /) -> Constraint: ...
3450
+ @overload
3451
+ def __eq__(self, rhs: Variable, /) -> Constraint: ...
3452
+ @overload
3453
+ def __eq__(self, rhs: int, /) -> Constraint: ...
3454
+ @overload
3455
+ def __eq__(self, rhs: float, /) -> Constraint: ...
3456
+ def __eq__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
3457
+ """
3458
+ Compare to a different expression or create a constraint `expression == scalar`.
3459
+
3460
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
3461
+ constraint, resulting in the following constraint:
3462
+
3463
+ self - rhs == 0
3464
+
3465
+ Parameters
3466
+ ----------
3467
+ rhs : Expression or float, int, Variable or Expression
3468
+
3469
+ Returns
3470
+ -------
3471
+ bool or Constraint
3472
+
3473
+ Raises
3474
+ ------
3475
+ TypeError
3476
+ If the right-hand side is not an Expression or scalar.
3477
+ """
3478
+ ...
3479
+
3480
+ @overload
3481
+ def __le__(self, rhs: Expression, /) -> Constraint: ...
3482
+ @overload
3483
+ def __le__(self, rhs: Variable, /) -> Constraint: ...
3484
+ @overload
3485
+ def __le__(self, rhs: int, /) -> Constraint: ...
3486
+ @overload
3487
+ def __le__(self, rhs: float, /) -> Constraint: ...
3488
+ def __le__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
3489
+ """
3490
+ Create a constraint `expression <= scalar`.
3491
+
3492
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
3493
+ constraint, resulting in the following constraint:
3494
+
3495
+ self - rhs <= 0
3496
+
3497
+ Parameters
3498
+ ----------
3499
+ rhs : float, int, Variable or Expression
3500
+
3501
+ Returns
3502
+ -------
3503
+ Constraint
3504
+
3505
+ Raises
3506
+ ------
3507
+ TypeError
3508
+ If the right-hand side is not of type float, int, Variable or Expression.
3509
+ """
3510
+ ...
3511
+
3512
+ @overload
3513
+ def __ge__(self, rhs: Expression, /) -> Constraint: ...
3514
+ @overload
3515
+ def __ge__(self, rhs: Variable, /) -> Constraint: ...
3516
+ @overload
3517
+ def __ge__(self, rhs: int, /) -> Constraint: ...
3518
+ @overload
3519
+ def __ge__(self, rhs: float, /) -> Constraint: ...
3520
+ def __ge__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
3521
+ """
3522
+ Create a constraint: expression >= scalar.
3523
+
3524
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
3525
+ constraint, resulting in the following constraint:
3526
+
3527
+ self - rhs >= 0
3528
+
3529
+ Parameters
3530
+ ----------
3531
+ rhs : float, int, Variable or Expression
3532
+
3533
+ Returns
3534
+ -------
3535
+ Constraint
3536
+
3537
+ Raises
3538
+ ------
3539
+ TypeError
3540
+ If the right-hand side is not of type float, int, Variable or Expression.
3541
+ """
3542
+ ...
3543
+
3544
+ def __neg__(self, /) -> Expression:
3545
+ """
3546
+ Negate the expression, i.e., multiply it by `-1`.
3547
+
3548
+ Returns
3549
+ -------
3550
+ Expression
3551
+ """
3552
+ ...
3553
+
3554
+ def degree(self, /) -> int:
3555
+ """Get the degree of this expression."""
3556
+ ...
3557
+
3558
+ @property
3559
+ def environment(self, /) -> Environment:
3560
+ """Get this expression's environment."""
3561
+ ...
3562
+
3563
+ def __str__(self, /) -> str: ...
3564
+ def __repr__(self, /) -> str: ...
3565
+ def evaluate(self, solution: Solution, /) -> NDArray:
3566
+ """Evaluate an expression based on an existing solution."""
3567
+ ...
3568
+
3569
+ class ExpressionIterator:
3570
+ """
3571
+ Iterate over the single components of an expression.
3572
+
3573
+ Examples
3574
+ --------
3575
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
3576
+ >>> expr: Expression = ...
3577
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
3578
+ >>> bias: float
3579
+ >>> for vars, bias in expr.items():
3580
+ >>> match vars:
3581
+ >>> case Constant(): do_something_with_constant(bias)
3582
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
3583
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
3584
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
3585
+ """
3586
+
3587
+ def __next__(self) -> tuple[Constant | Linear | Quadratic | HigherOrder, float]: ...
3588
+ def __iter__(self) -> ExpressionIterator: ...
3589
+
3590
+ class Environment:
3591
+ """
3592
+ Execution context for variable creation and expression scoping.
3593
+
3594
+ An `Environment` provides the symbolic scope in which `Variable`s are defined.
3595
+ It is required for constructing variables ensuring consistency across expressions.
3596
+ The environment does **not** store constraints or expressions — it only facilitates
3597
+ their creation by acting as a context manager and anchor for `Variable` instances.
3598
+
3599
+ Environments are best used with `with` blocks, but can also be passed manually
3600
+ to models or variables.
3601
+
3602
+ Examples
3603
+ --------
3604
+ Create variables inside an environment:
3605
+
3606
+ >>> from luna_quantum import Environment, Variable
3607
+ >>> with Environment() as env:
3608
+ ... x = Variable("x")
3609
+ ... y = Variable("y")
3610
+
3611
+ Serialize the environment state:
3612
+
3613
+ >>> data = env.encode()
3614
+ >>> expr = Environment.decode(data)
3615
+
3616
+ Notes
3617
+ -----
3618
+ - The environment is required to create `Variable` instances.
3619
+ - It does **not** own constraints or expressions — they merely reference variables
3620
+ tied to an environment.
3621
+ - Environments **cannot be nested**. Only one can be active at a time.
3622
+ - Use `encode()` / `decode()` to persist and recover expression trees.
3623
+ """
3624
+
3625
+ def __init__(self, /) -> None:
3626
+ """
3627
+ Initialize a new environment for variable construction.
3628
+
3629
+ It is recommended to use this in a `with` statement to ensure proper scoping.
3630
+ """
3631
+ ...
3632
+
3633
+ def __enter__(self, /) -> Self:
3634
+ """
3635
+ Activate this environment for variable creation.
3636
+
3637
+ Returns
3638
+ -------
3639
+ Environment
3640
+ The current environment (self).
3641
+
3642
+ Raises
3643
+ ------
3644
+ MultipleActiveEnvironmentsError
3645
+ If another environment is already active.
3646
+ """
3647
+ ...
3648
+
3649
+ def __exit__(
3650
+ self,
3651
+ /,
3652
+ exc_type: (type[BaseException] | None) = ...,
3653
+ exc_value: (BaseException | None) = ...,
3654
+ exc_traceback: (TracebackType | None) = ...,
3655
+ ) -> None:
3656
+ """
3657
+ Deactivate this environment.
3658
+
3659
+ Called automatically at the end of a `with` block.
3660
+ """
3661
+ ...
3662
+
3663
+ def get_variable(self, /, name: str) -> Variable:
3664
+ """
3665
+ Get a variable by its label (name).
3666
+
3667
+ Parameters
3668
+ ----------
3669
+ name : str
3670
+ The name/label of the variable
3671
+
3672
+ Returns
3673
+ -------
3674
+ Variable
3675
+ The variable with the specified label/name.
3676
+
3677
+ Raises
3678
+ ------
3679
+ VariableNotExistingError
3680
+ If no variable with the specified name is registered.
3681
+ """
3682
+ ...
3683
+
3684
+ @overload
3685
+ def encode(self, /) -> bytes: ...
3686
+ @overload
3687
+ def encode(self, /, *, compress: bool) -> bytes: ...
3688
+ @overload
3689
+ def encode(self, /, *, level: int) -> bytes: ...
3690
+ @overload
3691
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
3692
+ def encode(
3693
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
3694
+ ) -> bytes:
3695
+ """
3696
+ Serialize the environment into a compact binary format.
3697
+
3698
+ This is the preferred method for persisting an environment's state.
3699
+
3700
+ Parameters
3701
+ ----------
3702
+ compress : bool, optional
3703
+ Whether to compress the binary output. Default is `True`.
3704
+ level : int, optional
3705
+ Compression level (e.g., from 0 to 9). Default is `3`.
3706
+
3707
+ Returns
3708
+ -------
3709
+ bytes
3710
+ Encoded binary representation of the environment.
3711
+
3712
+ Raises
3713
+ ------
3714
+ IOError
3715
+ If serialization fails.
3716
+ """
3717
+ ...
3718
+
3719
+ @overload
3720
+ def serialize(self, /) -> bytes: ...
3721
+ @overload
3722
+ def serialize(self, /, *, compress: bool) -> bytes: ...
3723
+ @overload
3724
+ def serialize(self, /, *, level: int) -> bytes: ...
3725
+ @overload
3726
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
3727
+ def serialize(
3728
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
3729
+ ) -> bytes:
3730
+ """
3731
+ Alias for `encode()`.
3732
+
3733
+ See `encode()` for full usage details.
3734
+ """
3735
+ ...
3736
+
3737
+ @classmethod
3738
+ def decode(cls, data: bytes) -> Environment:
3739
+ """
3740
+ Reconstruct an expression from a previously encoded binary blob.
3741
+
3742
+ Parameters
3743
+ ----------
3744
+ data : bytes
3745
+ The binary data returned from `Environment.encode()`.
3746
+
3747
+ Returns
3748
+ -------
3749
+ Expression
3750
+ The reconstructed symbolic expression.
3751
+
3752
+ Raises
3753
+ ------
3754
+ DecodeError
3755
+ If decoding fails due to corruption or incompatibility.
3756
+ """
3757
+ ...
3758
+
3759
+ @classmethod
3760
+ def deserialize(cls, data: bytes) -> Environment:
3761
+ """
3762
+ Alias for `decode()`.
3763
+
3764
+ See `decode()` for full usage details.
3765
+ """
3766
+ ...
3767
+
3768
+ def equal_contents(self, other: Environment, /) -> bool:
3769
+ """
3770
+ Check whether this environment has equal contents as `other`.
3771
+
3772
+ Parameters
3773
+ ----------
3774
+ other : Environment
3775
+
3776
+ Returns
3777
+ -------
3778
+ bool
3779
+ """
3780
+ ...
3781
+
3782
+ def __eq__(self, other: Environment, /) -> bool: ...
3783
+ def __str__(self, /) -> str: ...
3784
+ def __repr__(self, /) -> str: ...
3785
+ @property
3786
+ def num_variables(self, /) -> int:
3787
+ """Get the number of variables in env."""
3788
+ ...
3789
+
3790
+ def variables(self, /) -> list[Variable]:
3791
+ """Get the variables in env."""
3792
+ ...
3793
+
3794
+ class Comparator(Enum):
3795
+ """
3796
+ Comparison operators used to define constraints.
3797
+
3798
+ This enum represents the logical relation between the left-hand side (LHS)
3799
+ and the right-hand side (RHS) of a constraint.
3800
+
3801
+ Attributes
3802
+ ----------
3803
+ Eq : Comparator
3804
+ Equality constraint (==).
3805
+ Le : Comparator
3806
+ Less-than-or-equal constraint (<=).
3807
+ Ge : Comparator
3808
+ Greater-than-or-equal constraint (>=).
3809
+
3810
+ Examples
3811
+ --------
3812
+ >>> from luna_quantum import Comparator
3813
+ >>> str(Comparator.Eq)
3814
+ '=='
3815
+ """
3816
+
3817
+ Eq = ...
3818
+ """Equality (==)"""
3819
+ Le = ...
3820
+ """Less-than or equal (<=)"""
3821
+ Ge = ...
3822
+ """Greater-than or equal (>=)"""
3823
+
3824
+ def __str__(self, /) -> str: ...
3825
+ def __repr__(self, /) -> str: ...
3826
+
3827
+ class Constraint:
3828
+ """
3829
+ A symbolic constraint formed by comparing an expression to a constant.
3830
+
3831
+ A `Constraint` captures a relation of the form:
3832
+ `expression comparator constant`, where the comparator is one of:
3833
+ `==`, `<=`, or `>=`.
3834
+
3835
+ While constraints are usually created by comparing an `Expression` to a scalar
3836
+ (e.g., `expr == 3.0`), they can also be constructed manually using this class.
3837
+
3838
+ Parameters
3839
+ ----------
3840
+ lhs : Expression
3841
+ The left-hand side expression.
3842
+ rhs : float
3843
+ The scalar right-hand side value.
3844
+ comparator : Comparator
3845
+ The relation between lhs and rhs (e.g., `Comparator.Eq`).
3846
+
3847
+ Examples
3848
+ --------
3849
+ >>> from luna_quantum import Environment, Variable, Constraint, Comparator
3850
+ >>> with Environment():
3851
+ ... x = Variable("x")
3852
+ ... c = Constraint(x + 2, 5.0, Comparator.Eq)
3853
+
3854
+ Or create via comparison:
3855
+
3856
+ >>> expr = 2 * x + 1
3857
+ >>> c2 = expr <= 10.0
3858
+ """
3859
+
3860
+ @overload
3861
+ def __init__(
3862
+ self, /, lhs: Expression, rhs: Expression, comparator: Comparator
3863
+ ) -> None: ...
3864
+ @overload
3865
+ def __init__(
3866
+ self, /, lhs: Expression, rhs: Variable, comparator: Comparator
3867
+ ) -> None: ...
3868
+ @overload
3869
+ def __init__(
3870
+ self, /, lhs: Expression, rhs: int, comparator: Comparator
3871
+ ) -> None: ...
3872
+ @overload
3873
+ def __init__(
3874
+ self, /, lhs: Expression, rhs: float, comparator: Comparator
3875
+ ) -> None: ...
3876
+ @overload
3877
+ def __init__(
3878
+ self, /, lhs: Expression, rhs: Expression, comparator: Comparator, name: str
3879
+ ) -> None: ...
3880
+ @overload
3881
+ def __init__(
3882
+ self, /, lhs: Expression, rhs: Variable, comparator: Comparator, name: str
3883
+ ) -> None: ...
3884
+ @overload
3885
+ def __init__(
3886
+ self, /, lhs: Expression, rhs: int, comparator: Comparator, name: str
3887
+ ) -> None: ...
3888
+ @overload
3889
+ def __init__(
3890
+ self, /, lhs: Expression, rhs: float, comparator: Comparator, name: str
3891
+ ) -> None: ...
3892
+ @overload
3893
+ def __init__(
3894
+ self, /, lhs: Variable, rhs: Expression, comparator: Comparator
3895
+ ) -> None: ...
3896
+ @overload
3897
+ def __init__(
3898
+ self, /, lhs: Variable, rhs: Variable, comparator: Comparator
3899
+ ) -> None: ...
3900
+ @overload
3901
+ def __init__(self, /, lhs: Variable, rhs: int, comparator: Comparator) -> None: ...
3902
+ @overload
3903
+ def __init__(
3904
+ self, /, lhs: Variable, rhs: float, comparator: Comparator
3905
+ ) -> None: ...
3906
+ @overload
3907
+ def __init__(
3908
+ self, /, lhs: Variable, rhs: Expression, comparator: Comparator, name: str
3909
+ ) -> None: ...
3910
+ @overload
3911
+ def __init__(
3912
+ self, /, lhs: Variable, rhs: Variable, comparator: Comparator, name: str
3913
+ ) -> None: ...
3914
+ @overload
3915
+ def __init__(
3916
+ self, /, lhs: Variable, rhs: int, comparator: Comparator, name: str
3917
+ ) -> None: ...
3918
+ @overload
3919
+ def __init__(
3920
+ self, /, lhs: Variable, rhs: float, comparator: Comparator, name: str
3921
+ ) -> None: ...
3922
+ def __init__(
3923
+ self,
3924
+ /,
3925
+ lhs: (Variable | Expression),
3926
+ rhs: (int | float | Expression | Variable),
3927
+ comparator: Comparator,
3928
+ name: str,
3929
+ ) -> None:
3930
+ """
3931
+ Construct a new symbolic constraint.
3932
+
3933
+ Parameters
3934
+ ----------
3935
+ lhs : Expression | Variable
3936
+ Left-hand side symbolic expression or variable.
3937
+ rhs : int | float | Expression | Variable
3938
+ Scalar right-hand side constant.
3939
+ comparator : Comparator
3940
+ Relational operator (e.g., Comparator.Eq, Comparator.Le).
3941
+ name : str
3942
+ The name of the constraint
3943
+
3944
+ Raises
3945
+ ------
3946
+ TypeError
3947
+ If lhs is not an Expression or rhs is not a scalar float.
3948
+ IllegalConstraintNameError
3949
+ If the constraint is tried to be created with an illegal name.
3950
+ """
3951
+ ...
3952
+
3953
+ @property
3954
+ def name(self, /) -> str | None:
3955
+ """
3956
+ Get the name of the constraint.
3957
+
3958
+ Returns
3959
+ -------
3960
+ str, optional
3961
+ Returns the name of the constraint as a string or None if it is unnamed.
3962
+ """
3963
+ ...
3964
+
3965
+ @property
3966
+ def lhs(self, /) -> Expression:
3967
+ """
3968
+ Get the left-hand side of the constraint.
3969
+
3970
+ Returns
3971
+ -------
3972
+ Expression
3973
+ The left-hand side expression.
3974
+ """
3975
+ ...
3976
+
3977
+ @property
3978
+ def rhs(self, /) -> float:
3979
+ """
3980
+ Get the right-hand side of the constraint.
3981
+
3982
+ Returns
3983
+ -------
3984
+ float
3985
+ The right-hand side expression.
3986
+ """
3987
+ ...
3988
+
3989
+ @property
3990
+ def comparator(self, /) -> Comparator:
3991
+ """
3992
+ Get the comparator of the constraint.
3993
+
3994
+ Returns
3995
+ -------
3996
+ Comparator
3997
+ The comparator of the constraint.
3998
+ """
3999
+ ...
4000
+
4001
+ def __eq__(self, other: Constraint, /) -> bool: ...
4002
+ def __str__(self, /) -> str: ...
4003
+ def __repr__(self, /) -> str: ...
4004
+
4005
+ class ConstraintCollectionIterator:
4006
+ """
4007
+ Iterate over the name, constraint tuples of a constraint collection.
4008
+
4009
+ Examples
4010
+ --------
4011
+ >>> from luna_quantum import ConstraintCollection
4012
+ >>> coll: ConstraintCollection = ...
4013
+ for (name, constraint) in coll.items():
4014
+ ...
4015
+ """
4016
+
4017
+ def __next__(self) -> tuple[str, Constraint]: ...
4018
+ def __iter__(self) -> ConstraintCollectionIterator: ...
4019
+
4020
+ class ConstraintCollection:
4021
+ """
4022
+ A collection of symbolic constraints used to define a model.
4023
+
4024
+ The `ConstraintCollection` object serves as a container for individual `Constraint`
4025
+ instances. It supports adding constraints programmatically and exporting
4026
+ them for serialization.
4027
+
4028
+ ConstraintCollection are typically added using `add_constraint()`
4029
+ or the `+=` operator.
4030
+
4031
+ Examples
4032
+ --------
4033
+ >>> from luna_quantum import ConstraintCollection, Constraint, Environment, Variable
4034
+ >>> with Environment():
4035
+ ... x = Variable("x")
4036
+ ... c = Constraint(x + 1, 0.0, Comparator.Le)
4037
+
4038
+ >>> cs = ConstraintCollection()
4039
+ >>> cs += x >= 1.0
4040
+
4041
+ Serialization:
4042
+
4043
+ >>> blob = cs.encode()
4044
+ >>> expr = ConstraintCollection.decode(blob)
4045
+
4046
+ Notes
4047
+ -----
4048
+ - This class does not check feasibility or enforce satisfaction.
4049
+ - Use `encode()`/`decode()` to serialize constraints alongside expressions.
4050
+ """
4051
+
4052
+ def __init__(self, /) -> None: ...
4053
+ @overload
4054
+ def add_constraint(self, /, constraint: Constraint) -> None: ...
4055
+ @overload
4056
+ def add_constraint(self, /, constraint: Constraint, name: str) -> None: ...
4057
+ def add_constraint(
4058
+ self, /, constraint: Constraint, name: (str | None) = ...
4059
+ ) -> None:
4060
+ """
4061
+ Add a constraint to the collection.
4062
+
4063
+ Parameters
4064
+ ----------
4065
+ constraint : Constraint
4066
+ The constraint to be added.
4067
+ name : str, optional
4068
+ The name of the constraint to be added.
4069
+ """
4070
+ ...
4071
+
4072
+ def items(self, /) -> ConstraintCollectionIterator:
4073
+ """Iterate over all items (`(name, constraint)`) in the collection."""
4074
+ ...
4075
+
4076
+ @overload
4077
+ def encode(self, /) -> bytes: ...
4078
+ @overload
4079
+ def encode(self, /, *, compress: bool) -> bytes: ...
4080
+ @overload
4081
+ def encode(self, /, *, level: int) -> bytes: ...
4082
+ @overload
4083
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
4084
+ def encode(
4085
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
4086
+ ) -> bytes:
4087
+ """
4088
+ Serialize the constraint collection to a binary blob.
4089
+
4090
+ Parameters
4091
+ ----------
4092
+ compress : bool, optional
4093
+ Whether to compress the result. Default is True.
4094
+ level : int, optional
4095
+ Compression level (0-9). Default is 3.
4096
+
4097
+ Returns
4098
+ -------
4099
+ bytes
4100
+ Encoded representation of the constraints.
4101
+
4102
+ Raises
4103
+ ------
4104
+ IOError
4105
+ If serialization fails.
4106
+ """
4107
+ ...
4108
+
4109
+ @overload
4110
+ def serialize(self, /) -> bytes: ...
4111
+ @overload
4112
+ def serialize(self, /, *, compress: bool) -> bytes: ...
4113
+ @overload
4114
+ def serialize(self, /, *, level: int) -> bytes: ...
4115
+ @overload
4116
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
4117
+ def serialize(
4118
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
4119
+ ) -> bytes:
4120
+ """
4121
+ Alias for `encode()`.
4122
+
4123
+ See `encode()` for details.
4124
+ """
4125
+ ...
4126
+
4127
+ @classmethod
4128
+ def decode(cls, data: bytes, env: Environment) -> Expression:
4129
+ """
4130
+ Deserialize an expression from binary constraint data.
4131
+
4132
+ Parameters
4133
+ ----------
4134
+ data : bytes
4135
+ Encoded blob from `encode()`.
4136
+
4137
+ Returns
4138
+ -------
4139
+ Expression
4140
+ Expression reconstructed from the constraint context.
4141
+
4142
+ Raises
4143
+ ------
4144
+ DecodeError
4145
+ If decoding fails due to corruption or incompatibility.
4146
+ """
4147
+ ...
4148
+
4149
+ @classmethod
4150
+ def deserialize(cls, data: bytes, env: Environment) -> Expression:
4151
+ """
4152
+ Alias for `decode()`.
4153
+
4154
+ See `decode()` for usage.
4155
+ """
4156
+ ...
4157
+
4158
+ @overload
4159
+ def __iadd__(self, constraint: Constraint, /) -> Self: ...
4160
+ @overload
4161
+ def __iadd__(self, constraint: tuple[Constraint, str], /) -> Self: ...
4162
+ def __iadd__(self, constraint: (Constraint | tuple[Constraint, str]), /) -> Self:
4163
+ """
4164
+ In-place constraint addition using `+=`.
4165
+
4166
+ Parameters
4167
+ ----------
4168
+ constraint : Constraint | tuple[Constraint, str]
4169
+ The constraint to add.
4170
+
4171
+ Returns
4172
+ -------
4173
+ ConstraintCollection
4174
+ The updated collection.
4175
+
4176
+ Raises
4177
+ ------
4178
+ TypeError
4179
+ If the value is not a `Constraint` or valid symbolic comparison.
4180
+ """
4181
+ ...
4182
+
4183
+ @overload
4184
+ def get(self, item: str, /) -> Constraint: ...
4185
+ @deprecated(
4186
+ "Constraint access using int will be removed, use name (str) based indexing instead."
4187
+ )
4188
+ @overload
4189
+ def get(self, item: int, /) -> Constraint: ...
4190
+ def get(self, item: (int | str), /) -> Constraint:
4191
+ """Get a constraint for its name or index."""
4192
+ ...
4193
+
4194
+ @overload
4195
+ def remove(self, item: str, /) -> Constraint: ...
4196
+ @deprecated(
4197
+ "Constraint access using int will be removed, use name (str) based indexing instead."
4198
+ )
4199
+ @overload
4200
+ def remove(self, item: int, /) -> Constraint: ...
4201
+ def remove(self, item: (int | str), /) -> Constraint:
4202
+ """Remove a constraint for its name or index."""
4203
+ ...
4204
+
4205
+ def __eq__(self, other: ConstraintCollection, /) -> bool: ...
4206
+ def __str__(self, /) -> str: ...
4207
+ def __repr__(self, /) -> str: ...
4208
+ @overload
4209
+ def __getitem__(self, item: str, /) -> Constraint: ...
4210
+ @deprecated(
4211
+ "Constraint access using int will be removed, use name (str) based indexing instead."
4212
+ )
4213
+ @overload
4214
+ def __getitem__(self, item: int, /) -> Constraint: ...
4215
+ def __getitem__(self, item: (int | str), /) -> Constraint: ...
4216
+ @overload
4217
+ def __setitem__(self, item: str, content: Constraint, /) -> None: ...
4218
+ @deprecated(
4219
+ "Constraint access using int will be removed, use name (str) based indexing instead."
4220
+ )
4221
+ @overload
4222
+ def __setitem__(self, item: int, content: Constraint, /) -> None: ...
4223
+ def __setitem__(self, item: (int | str), content: Constraint, /) -> None: ...
4224
+ def __len__(self, /) -> int:
4225
+ """
4226
+ Get the number of constraints.
4227
+
4228
+ Returns
4229
+ -------
4230
+ int
4231
+ The number of constraints associated with this `ConstraintCollection`
4232
+ object.
4233
+ """
4234
+ ...
4235
+
4236
+ def __iter__(self, /) -> Iterator[Constraint]: ...
4237
+ def equal_contents(self, other: ConstraintCollection, /) -> bool:
4238
+ """
4239
+ Check whether this constraints has equal contents as `other`.
4240
+
4241
+ Parameters
4242
+ ----------
4243
+ other : ConstraintCollection
4244
+
4245
+ Returns
4246
+ -------
4247
+ bool
4248
+ """
4249
+ ...
4250
+
4251
+ def ctypes(self, /) -> list[Comparator]:
4252
+ """Get all unique constraint types identified using their comparator."""
4253
+ ...
4254
+
4255
+ __version__: str
4256
+ __aq_model_version__: str
4257
+ __luna_quantum_version__: str
4258
+ __all__ = [
4259
+ "Bounds",
4260
+ "Comparator",
4261
+ "Constraint",
4262
+ "ConstraintCollection",
4263
+ "Environment",
4264
+ "Expression",
4265
+ "Model",
4266
+ "Result",
4267
+ "ResultIterator",
4268
+ "ResultView",
4269
+ "Sample",
4270
+ "SampleIterator",
4271
+ "Samples",
4272
+ "SamplesIterator",
4273
+ "Sense",
4274
+ "Solution",
4275
+ "Timer",
4276
+ "Timing",
4277
+ "Variable",
4278
+ "Vtype",
4279
+ "__aq_model_version__",
4280
+ "__luna_quantum_version__",
4281
+ "__version__",
4282
+ "errors",
4283
+ "transformations",
4284
+ "translator",
4285
+ "utils",
4286
+ ]