luna-quantum 1.0.0__cp313-cp313-macosx_10_12_x86_64.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.

Potentially problematic release.


This version of luna-quantum might be problematic. Click here for more details.

Files changed (252) hide show
  1. luna_quantum/__init__.py +96 -0
  2. luna_quantum/__init__.pyi +68 -0
  3. luna_quantum/_core.cpython-313-darwin.so +0 -0
  4. luna_quantum/_core.pyi +3017 -0
  5. luna_quantum/aqm_overwrites/__init__.py +3 -0
  6. luna_quantum/aqm_overwrites/model.py +184 -0
  7. luna_quantum/client/__init__.py +0 -0
  8. luna_quantum/client/controllers/__init__.py +4 -0
  9. luna_quantum/client/controllers/luna_http_client.py +37 -0
  10. luna_quantum/client/controllers/luna_platform_client.py +153 -0
  11. luna_quantum/client/controllers/luna_q.py +62 -0
  12. luna_quantum/client/controllers/luna_solve.py +125 -0
  13. luna_quantum/client/error/__init__.py +0 -0
  14. luna_quantum/client/error/luna_api_key_invalid_error.py +10 -0
  15. luna_quantum/client/error/luna_api_key_missing_error.py +10 -0
  16. luna_quantum/client/error/luna_error.py +2 -0
  17. luna_quantum/client/error/luna_server_error.py +20 -0
  18. luna_quantum/client/error/timeout_error.py +12 -0
  19. luna_quantum/client/error/transformation_error.py +18 -0
  20. luna_quantum/client/error/utils/__init__.py +0 -0
  21. luna_quantum/client/error/utils/http_error_utils.py +112 -0
  22. luna_quantum/client/interfaces/__init__.py +4 -0
  23. luna_quantum/client/interfaces/clients/__init__.py +25 -0
  24. luna_quantum/client/interfaces/clients/circuit_rest_client_i.py +68 -0
  25. luna_quantum/client/interfaces/clients/info_rest_client_i.py +53 -0
  26. luna_quantum/client/interfaces/clients/model_rest_client_i.py +139 -0
  27. luna_quantum/client/interfaces/clients/qpu_token_rest_client_i.py +364 -0
  28. luna_quantum/client/interfaces/clients/rest_client_i.py +21 -0
  29. luna_quantum/client/interfaces/clients/solve_job_rest_client_i.py +201 -0
  30. luna_quantum/client/interfaces/clients/users_rest_client_i.py +29 -0
  31. luna_quantum/client/interfaces/services/__init__.py +0 -0
  32. luna_quantum/client/interfaces/services/luna_q_i.py +34 -0
  33. luna_quantum/client/interfaces/services/luna_solve_i.py +72 -0
  34. luna_quantum/client/interfaces/services/service_i.py +36 -0
  35. luna_quantum/client/rest_client/__init__.py +15 -0
  36. luna_quantum/client/rest_client/circuit_rest_client.py +107 -0
  37. luna_quantum/client/rest_client/info_rest_client.py +76 -0
  38. luna_quantum/client/rest_client/model_rest_client.py +216 -0
  39. luna_quantum/client/rest_client/qpu_token_rest_client.py +504 -0
  40. luna_quantum/client/rest_client/solve_job_rest_client.py +286 -0
  41. luna_quantum/client/rest_client/users_rest_client.py +35 -0
  42. luna_quantum/client/schemas/__init__.py +26 -0
  43. luna_quantum/client/schemas/circuit.py +49 -0
  44. luna_quantum/client/schemas/create/__init__.py +6 -0
  45. luna_quantum/client/schemas/create/circuit.py +31 -0
  46. luna_quantum/client/schemas/create/optimization.py +39 -0
  47. luna_quantum/client/schemas/create/qpu_token.py +25 -0
  48. luna_quantum/client/schemas/create/qpu_token_time_quota.py +29 -0
  49. luna_quantum/client/schemas/create/qubo.py +19 -0
  50. luna_quantum/client/schemas/create/solve_job_create.py +43 -0
  51. luna_quantum/client/schemas/enums/__init__.py +0 -0
  52. luna_quantum/client/schemas/enums/call_style.py +13 -0
  53. luna_quantum/client/schemas/enums/circuit.py +42 -0
  54. luna_quantum/client/schemas/enums/model_format.py +11 -0
  55. luna_quantum/client/schemas/enums/problem.py +50 -0
  56. luna_quantum/client/schemas/enums/qpu_token_type.py +20 -0
  57. luna_quantum/client/schemas/enums/sense.py +8 -0
  58. luna_quantum/client/schemas/enums/status.py +40 -0
  59. luna_quantum/client/schemas/enums/timeframe.py +11 -0
  60. luna_quantum/client/schemas/error_message.py +14 -0
  61. luna_quantum/client/schemas/model_metadata.py +35 -0
  62. luna_quantum/client/schemas/qpu_token/__init__.py +0 -0
  63. luna_quantum/client/schemas/qpu_token/qpu_token.py +161 -0
  64. luna_quantum/client/schemas/qpu_token/qpu_token_source.py +19 -0
  65. luna_quantum/client/schemas/qpu_token/qpu_token_time_quota.py +28 -0
  66. luna_quantum/client/schemas/qpu_token/token_provider.py +135 -0
  67. luna_quantum/client/schemas/representation.py +19 -0
  68. luna_quantum/client/schemas/solution.py +106 -0
  69. luna_quantum/client/schemas/solve_job.py +47 -0
  70. luna_quantum/client/schemas/solver_info.py +11 -0
  71. luna_quantum/client/schemas/user.py +11 -0
  72. luna_quantum/client/schemas/wrappers/__init__.py +5 -0
  73. luna_quantum/client/schemas/wrappers/datetime_wrapper.py +32 -0
  74. luna_quantum/client/utils/__init__.py +0 -0
  75. luna_quantum/client/utils/qpu_token_utils.py +147 -0
  76. luna_quantum/errors.py +1 -0
  77. luna_quantum/errors.pyi +202 -0
  78. luna_quantum/exceptions/__init__.py +0 -0
  79. luna_quantum/exceptions/base_luna_quantum_error.py +2 -0
  80. luna_quantum/exceptions/patch_class_field_exists_error.py +10 -0
  81. luna_quantum/factories/__init__.py +4 -0
  82. luna_quantum/factories/luna_solve_client_factory.py +75 -0
  83. luna_quantum/factories/usecase_factory.py +457 -0
  84. luna_quantum/py.typed +0 -0
  85. luna_quantum/solve/__init__.py +13 -0
  86. luna_quantum/solve/default_token.py +304 -0
  87. luna_quantum/solve/domain/__init__.py +0 -0
  88. luna_quantum/solve/domain/abstract/__init__.py +4 -0
  89. luna_quantum/solve/domain/abstract/luna_algorithm.py +203 -0
  90. luna_quantum/solve/domain/abstract/qpu_token_backend.py +34 -0
  91. luna_quantum/solve/domain/model_metadata.py +54 -0
  92. luna_quantum/solve/domain/solve_job.py +187 -0
  93. luna_quantum/solve/errors/__init__.py +0 -0
  94. luna_quantum/solve/errors/incompatible_backend_error.py +15 -0
  95. luna_quantum/solve/errors/model_metadata_missing_error.py +11 -0
  96. luna_quantum/solve/errors/solve_base_error.py +5 -0
  97. luna_quantum/solve/errors/token_missing_error.py +11 -0
  98. luna_quantum/solve/interfaces/__init__.py +0 -0
  99. luna_quantum/solve/interfaces/algorithm_i.py +47 -0
  100. luna_quantum/solve/interfaces/backend_i.py +28 -0
  101. luna_quantum/solve/interfaces/usecases/__init__.py +55 -0
  102. luna_quantum/solve/interfaces/usecases/model_delete_usecase_i.py +27 -0
  103. luna_quantum/solve/interfaces/usecases/model_fetch_metadata_usecase_i.py +33 -0
  104. luna_quantum/solve/interfaces/usecases/model_get_solutions_usecase_i.py +33 -0
  105. luna_quantum/solve/interfaces/usecases/model_get_solve_jobs_usecase_i.py +33 -0
  106. luna_quantum/solve/interfaces/usecases/model_load_by_id_usecase_i.py +32 -0
  107. luna_quantum/solve/interfaces/usecases/model_load_by_metadata_usecase_i.py +37 -0
  108. luna_quantum/solve/interfaces/usecases/model_load_metadata_by_hash_usecase_i.py +38 -0
  109. luna_quantum/solve/interfaces/usecases/model_save_usecase_i.py +36 -0
  110. luna_quantum/solve/interfaces/usecases/solve_job_cancel_usecase_i.py +33 -0
  111. luna_quantum/solve/interfaces/usecases/solve_job_create_usecase_i.py +44 -0
  112. luna_quantum/solve/interfaces/usecases/solve_job_delete_usecase_i.py +32 -0
  113. luna_quantum/solve/interfaces/usecases/solve_job_fetch_updates_usecase_i.py +38 -0
  114. luna_quantum/solve/interfaces/usecases/solve_job_get_result_usecase_i.py +63 -0
  115. luna_quantum/solve/parameters/__init__.py +0 -0
  116. luna_quantum/solve/parameters/algorithms/__init__.py +49 -0
  117. luna_quantum/solve/parameters/algorithms/base_params/__init__.py +24 -0
  118. luna_quantum/solve/parameters/algorithms/base_params/decomposer.py +57 -0
  119. luna_quantum/solve/parameters/algorithms/base_params/qaoa_circuit_params.py +95 -0
  120. luna_quantum/solve/parameters/algorithms/base_params/quantum_annealing_params.py +78 -0
  121. luna_quantum/solve/parameters/algorithms/base_params/scipy_optimizer.py +120 -0
  122. luna_quantum/solve/parameters/algorithms/base_params/simulated_annealing_params.py +106 -0
  123. luna_quantum/solve/parameters/algorithms/base_params/tabu_kerberos_params.py +39 -0
  124. luna_quantum/solve/parameters/algorithms/base_params/tabu_search_params.py +129 -0
  125. luna_quantum/solve/parameters/algorithms/flexible_parameter_algorithm.py +59 -0
  126. luna_quantum/solve/parameters/algorithms/genetic_algorithms/__init__.py +4 -0
  127. luna_quantum/solve/parameters/algorithms/genetic_algorithms/qaga.py +131 -0
  128. luna_quantum/solve/parameters/algorithms/genetic_algorithms/saga.py +139 -0
  129. luna_quantum/solve/parameters/algorithms/optimization_solvers/__init__.py +3 -0
  130. luna_quantum/solve/parameters/algorithms/optimization_solvers/scip.py +51 -0
  131. luna_quantum/solve/parameters/algorithms/quantum_annealing/__init__.py +19 -0
  132. luna_quantum/solve/parameters/algorithms/quantum_annealing/kerberos.py +149 -0
  133. luna_quantum/solve/parameters/algorithms/quantum_annealing/leap_hybrid_bqm.py +75 -0
  134. luna_quantum/solve/parameters/algorithms/quantum_annealing/leap_hybrid_cqm.py +75 -0
  135. luna_quantum/solve/parameters/algorithms/quantum_annealing/parallel_tempering_qpu.py +139 -0
  136. luna_quantum/solve/parameters/algorithms/quantum_annealing/population_annealing_qpu.py +109 -0
  137. luna_quantum/solve/parameters/algorithms/quantum_annealing/qbsolv_like_qpu.py +111 -0
  138. luna_quantum/solve/parameters/algorithms/quantum_annealing/quantum_annealing.py +105 -0
  139. luna_quantum/solve/parameters/algorithms/quantum_annealing/repeated_reverse_quantum_annealing.py +174 -0
  140. luna_quantum/solve/parameters/algorithms/quantum_gate/__init__.py +6 -0
  141. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/__init__.py +26 -0
  142. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/config.py +80 -0
  143. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/flex_qaoa.py +226 -0
  144. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/optimizers.py +97 -0
  145. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/pipeline.py +87 -0
  146. luna_quantum/solve/parameters/algorithms/quantum_gate/qaoa.py +104 -0
  147. luna_quantum/solve/parameters/algorithms/quantum_gate/qaoa_fo.py +69 -0
  148. luna_quantum/solve/parameters/algorithms/quantum_gate/vqe.py +109 -0
  149. luna_quantum/solve/parameters/algorithms/search_algorithms/__init__.py +5 -0
  150. luna_quantum/solve/parameters/algorithms/search_algorithms/dialectic_search.py +152 -0
  151. luna_quantum/solve/parameters/algorithms/search_algorithms/qbsolv_like_tabu_search.py +117 -0
  152. luna_quantum/solve/parameters/algorithms/search_algorithms/tabu_search.py +126 -0
  153. luna_quantum/solve/parameters/algorithms/simulated_annealing/__init__.py +13 -0
  154. luna_quantum/solve/parameters/algorithms/simulated_annealing/parallel_tempering.py +131 -0
  155. luna_quantum/solve/parameters/algorithms/simulated_annealing/population_annealing.py +95 -0
  156. luna_quantum/solve/parameters/algorithms/simulated_annealing/qbsolv_like_simulated_annealing.py +141 -0
  157. luna_quantum/solve/parameters/algorithms/simulated_annealing/repeated_reverse_simulated_annealing.py +172 -0
  158. luna_quantum/solve/parameters/algorithms/simulated_annealing/simulated_annealing.py +126 -0
  159. luna_quantum/solve/parameters/backends/__init__.py +20 -0
  160. luna_quantum/solve/parameters/backends/aqarios.py +17 -0
  161. luna_quantum/solve/parameters/backends/aws/__init__.py +11 -0
  162. luna_quantum/solve/parameters/backends/aws/aws.py +36 -0
  163. luna_quantum/solve/parameters/backends/aws/aws_backend_base.py +74 -0
  164. luna_quantum/solve/parameters/backends/aws/ionq.py +43 -0
  165. luna_quantum/solve/parameters/backends/aws/iqm.py +31 -0
  166. luna_quantum/solve/parameters/backends/aws/rigetti.py +31 -0
  167. luna_quantum/solve/parameters/backends/dwave.py +17 -0
  168. luna_quantum/solve/parameters/backends/dwave_qpu.py +164 -0
  169. luna_quantum/solve/parameters/backends/ibm.py +132 -0
  170. luna_quantum/solve/parameters/backends/qctrl.py +130 -0
  171. luna_quantum/solve/parameters/backends/zib.py +17 -0
  172. luna_quantum/solve/parameters/constants.py +11 -0
  173. luna_quantum/solve/parameters/mixins/__init__.py +0 -0
  174. luna_quantum/solve/parameters/mixins/fujitsu_common_params_mixin.py +239 -0
  175. luna_quantum/solve/parameters/mixins/fujitsu_v2_mixin.py +70 -0
  176. luna_quantum/solve/parameters/mixins/qbsolv_like_mixin.py +60 -0
  177. luna_quantum/solve/use_cases/__init__.py +119 -0
  178. luna_quantum/solve/use_cases/arbitrage_edge_based.py +50 -0
  179. luna_quantum/solve/use_cases/arbitrage_node_based.py +55 -0
  180. luna_quantum/solve/use_cases/base.py +7 -0
  181. luna_quantum/solve/use_cases/binary_integer_linear_programming.py +54 -0
  182. luna_quantum/solve/use_cases/binary_paint_shop_problem.py +37 -0
  183. luna_quantum/solve/use_cases/credit_scoring_feature_selection.py +40 -0
  184. luna_quantum/solve/use_cases/dynamic_portfolio_optimization.py +64 -0
  185. luna_quantum/solve/use_cases/exact_cover.py +51 -0
  186. luna_quantum/solve/use_cases/flight_gate_assignment.py +79 -0
  187. luna_quantum/solve/use_cases/graph_coloring.py +42 -0
  188. luna_quantum/solve/use_cases/graph_isomorphism.py +52 -0
  189. luna_quantum/solve/use_cases/graph_partitioning.py +46 -0
  190. luna_quantum/solve/use_cases/hamiltonian_cycle.py +49 -0
  191. luna_quantum/solve/use_cases/induced_subgraph_isomorphism.py +50 -0
  192. luna_quantum/solve/use_cases/job_shop_scheduling.py +44 -0
  193. luna_quantum/solve/use_cases/k_medoids_clustering.py +49 -0
  194. luna_quantum/solve/use_cases/knapsack_integer_weights.py +56 -0
  195. luna_quantum/solve/use_cases/linear_regression.py +60 -0
  196. luna_quantum/solve/use_cases/lmwcs.py +84 -0
  197. luna_quantum/solve/use_cases/longest_path.py +50 -0
  198. luna_quantum/solve/use_cases/market_graph_clustering.py +61 -0
  199. luna_quantum/solve/use_cases/max2sat.py +54 -0
  200. luna_quantum/solve/use_cases/max3sat.py +55 -0
  201. luna_quantum/solve/use_cases/max_clique.py +60 -0
  202. luna_quantum/solve/use_cases/max_cut.py +48 -0
  203. luna_quantum/solve/use_cases/max_independent_set.py +37 -0
  204. luna_quantum/solve/use_cases/minimal_maximal_matching.py +54 -0
  205. luna_quantum/solve/use_cases/minimal_spanning_tree.py +90 -0
  206. luna_quantum/solve/use_cases/minimum_vertex_cover.py +45 -0
  207. luna_quantum/solve/use_cases/number_partitioning.py +32 -0
  208. luna_quantum/solve/use_cases/portfolio_optimization.py +46 -0
  209. luna_quantum/solve/use_cases/portfolio_optimization_ib_tv.py +63 -0
  210. luna_quantum/solve/use_cases/quadratic_assignment.py +49 -0
  211. luna_quantum/solve/use_cases/quadratic_knapsack.py +48 -0
  212. luna_quantum/solve/use_cases/satellite_scheduling.py +73 -0
  213. luna_quantum/solve/use_cases/sensor_placement.py +58 -0
  214. luna_quantum/solve/use_cases/set_cover.py +56 -0
  215. luna_quantum/solve/use_cases/set_packing.py +54 -0
  216. luna_quantum/solve/use_cases/set_partitioning.py +52 -0
  217. luna_quantum/solve/use_cases/subgraph_isomorphism.py +55 -0
  218. luna_quantum/solve/use_cases/subset_sum.py +37 -0
  219. luna_quantum/solve/use_cases/support_vector_machine.py +64 -0
  220. luna_quantum/solve/use_cases/traffic_flow.py +35 -0
  221. luna_quantum/solve/use_cases/travelling_salesman_problem.py +53 -0
  222. luna_quantum/solve/use_cases/type_aliases.py +9 -0
  223. luna_quantum/solve/use_cases/weighted_max_cut.py +37 -0
  224. luna_quantum/solve/usecases/__init__.py +45 -0
  225. luna_quantum/solve/usecases/model_delete_usecase.py +49 -0
  226. luna_quantum/solve/usecases/model_fetch_metadata_usecase.py +50 -0
  227. luna_quantum/solve/usecases/model_get_solution_usecase.py +56 -0
  228. luna_quantum/solve/usecases/model_get_solve_jobs_usecase.py +62 -0
  229. luna_quantum/solve/usecases/model_load_by_id_usecase.py +47 -0
  230. luna_quantum/solve/usecases/model_load_by_metadata_usecase.py +52 -0
  231. luna_quantum/solve/usecases/model_load_metadata_by_hash_usecase.py +51 -0
  232. luna_quantum/solve/usecases/model_save_usecase.py +63 -0
  233. luna_quantum/solve/usecases/solve_job_cancel_usecase.py +51 -0
  234. luna_quantum/solve/usecases/solve_job_create_usecase.py +112 -0
  235. luna_quantum/solve/usecases/solve_job_delete_usecase.py +38 -0
  236. luna_quantum/solve/usecases/solve_job_fetch_updates_usecase.py +49 -0
  237. luna_quantum/solve/usecases/solve_job_get_result_usecase.py +97 -0
  238. luna_quantum/translator.py +1 -0
  239. luna_quantum/translator.pyi +833 -0
  240. luna_quantum/util/__init__.py +0 -0
  241. luna_quantum/util/active_waiting.py +79 -0
  242. luna_quantum/util/class_patcher.py +164 -0
  243. luna_quantum/util/log_utils.py +167 -0
  244. luna_quantum/util/pretty_base.py +67 -0
  245. luna_quantum/util/pydantic_utils.py +38 -0
  246. luna_quantum/utils.py +54 -0
  247. luna_quantum/utils.pyi +35 -0
  248. luna_quantum-1.0.0.dist-info/METADATA +37 -0
  249. luna_quantum-1.0.0.dist-info/RECORD +252 -0
  250. luna_quantum-1.0.0.dist-info/WHEEL +4 -0
  251. luna_quantum-1.0.0.dist-info/licenses/LICENSE +176 -0
  252. luna_quantum-1.0.0.dist-info/licenses/NOTICE +13 -0
luna_quantum/_core.pyi ADDED
@@ -0,0 +1,3017 @@
1
+ from datetime import datetime, timedelta
2
+ from enum import Enum
3
+ from typing import Any, overload
4
+
5
+ from numpy.typing import NDArray
6
+
7
+ from . import exceptions, translator
8
+ from .client.interfaces.services.luna_solve_i import ILunaSolve
9
+ from .solve import ModelMetadata
10
+ from .solve.domain.solve_job import SolveJob
11
+
12
+ # _variable.pyi
13
+ class Vtype(Enum):
14
+ """
15
+ Enumeration of variable types supported by the optimization system.
16
+
17
+ This enum defines the type of a variable used in a model. The type influences
18
+ the domain and behavior of the variable during optimization. It is often passed
19
+ when defining variables to specify how they should behave.
20
+
21
+ Attributes
22
+ ----------
23
+ Real : Vtype
24
+ Continuous real-valued variable. Can take any value within given bounds.
25
+ Integer : Vtype
26
+ Discrete integer-valued variable. Takes integer values within bounds.
27
+ Binary : Vtype
28
+ Binary variable. Can only take values 0 or 1.
29
+ Spin : Vtype
30
+ Spin variable. Can only take values -1 or +1.
31
+
32
+ Examples
33
+ --------
34
+ >>> from luna_quantum import Vtype
35
+ >>> Vtype.Real
36
+ Real
37
+
38
+ >>> str(Vtype.Binary)
39
+ 'Binary'
40
+ """
41
+
42
+ Real = ...
43
+ """Continuous real-valued variable. Can take any value within given bounds."""
44
+ Integer = ...
45
+ """Discrete integer-valued variable. Takes integer values within bounds."""
46
+ Binary = ...
47
+ """Binary variable. Can only take values 0 or 1."""
48
+
49
+ Spin = ...
50
+ """Spin variable. Can only take values -1 or +1."""
51
+
52
+ class Unbounded: ...
53
+
54
+ class Bounds:
55
+ """
56
+ Represents bounds for a variable (only supported for real and integer variables).
57
+
58
+ A `Bounds` object defines the valid interval for a variable. Bounds are inclusive,
59
+ and can be partially specified by providing only a lower or upper limit. If neither
60
+ is specified, the variable is considered unbounded.
61
+
62
+ Parameters
63
+ ----------
64
+ lower : float, optional
65
+ Lower bound of the variable. Defaults to negative infinity if not specified.
66
+ upper : float, optional
67
+ Upper bound of the variable. Defaults to positive infinity if not specified.
68
+
69
+ Examples
70
+ --------
71
+ >>> from luna_quantum import Bounds
72
+ >>> Bounds(-1.0, 1.0)
73
+ Bounds { lower: -1, upper: 1 }
74
+
75
+ >>> Bounds(lower=0.0)
76
+ Bounds { lower: -1, upper: unlimited }
77
+
78
+ >>> Bounds(upper=10.0)
79
+ Bounds { lower: unlimited, upper: 1 }
80
+
81
+ Notes
82
+ -----
83
+ - Bounds are only meaningful for variables of type `Vtype.Real` or `Vtype.Integer`.
84
+ - If both bounds are omitted, the variable is unbounded.
85
+ """
86
+
87
+ @overload
88
+ def __init__(self, /, *, lower: float | Unbounded) -> None: ...
89
+ @overload
90
+ def __init__(self, /, *, upper: float | type[Unbounded]) -> None: ...
91
+ @overload
92
+ def __init__(
93
+ self, /, lower: float | type[Unbounded], upper: float | type[Unbounded]
94
+ ) -> None: ...
95
+ @overload
96
+ def __init__(
97
+ self,
98
+ /,
99
+ lower: float | type[Unbounded] | None = ...,
100
+ upper: float | type[Unbounded] | None = ...,
101
+ ) -> None:
102
+ """
103
+ Create bounds for a variable.
104
+
105
+ See class-level docstring for full documentation.
106
+ """
107
+
108
+ @property
109
+ def lower(self, /) -> float | Unbounded | None:
110
+ """Get the lower bound"""
111
+
112
+ @property
113
+ def upper(self, /) -> float | Unbounded | None:
114
+ """Get the upper bound"""
115
+
116
+ class Variable:
117
+ """
118
+ Represents a symbolic variable within an optimization environment.
119
+
120
+ A `Variable` is the fundamental building block of algebraic expressions
121
+ used in optimization models. Each variable is tied to an `Environment`
122
+ which scopes its lifecycle and expression context. Variables can be
123
+ typed and optionally bounded.
124
+
125
+ Parameters
126
+ ----------
127
+ name : str
128
+ The name of the variable.
129
+ vtype : Vtype, optional
130
+ The variable type (e.g., `Vtype.Real`, `Vtype.Integer`, etc.).
131
+ Defaults to `Vtype.Binary`.
132
+ bounds : Bounds, optional
133
+ Bounds restricting the range of the variable. Only applicable for
134
+ `Real` and `Integer` variables.
135
+ env : Environment, optional
136
+ The environment in which this variable is created. If not provided,
137
+ the current environment from the context manager is used.
138
+
139
+ Examples
140
+ --------
141
+ >>> from luna_quantum import Variable, Environment, Vtype, Bounds
142
+ >>> with Environment():
143
+ ... x = Variable("x")
144
+ ... y = Variable("y", vtype=Vtype.Integer, bounds=Bounds(0, 5))
145
+ ... expr = 2 * x + y - 1
146
+
147
+ Arithmetic Overloads
148
+ --------------------
149
+ Variables support standard arithmetic operations:
150
+
151
+ - Addition: `x + y`, `x + 2`, `2 + x`
152
+ - Subtraction: `x - y`, `3 - x`
153
+ - Multiplication: `x * y`, `2 * x`, `x * 2`
154
+
155
+ All expressions return `Expression` objects and preserve symbolic structure.
156
+
157
+ Notes
158
+ -----
159
+ - A `Variable` is bound to a specific `Environment` instance.
160
+ - Variables are immutable; all operations yield new `Expression` objects.
161
+ - Variables carry their environment, but the environment does not own the variable.
162
+ """
163
+
164
+ @overload
165
+ def __init__(self, /, name: str) -> None: ...
166
+ @overload
167
+ def __init__(self, /, name: str, *, env: Environment) -> None: ...
168
+ @overload
169
+ def __init__(self, /, name: str, *, vtype: Vtype) -> None: ...
170
+ @overload
171
+ def __init__(self, /, name: str, *, vtype: Vtype, bounds: Bounds) -> None: ...
172
+ @overload
173
+ def __init__(
174
+ self, /, name: str, *, vtype: Vtype, bounds: Bounds, env: Environment
175
+ ) -> None: ...
176
+ def __init__(
177
+ self,
178
+ /,
179
+ name: str,
180
+ *,
181
+ vtype: Vtype | None = ...,
182
+ bounds: Bounds | None = ...,
183
+ env: Environment | None = ...,
184
+ ) -> None:
185
+ """
186
+ Initialize a new Variable.
187
+
188
+ See class-level docstring for full usage.
189
+
190
+ Raises
191
+ ------
192
+ NoActiveEnvironmentFoundError
193
+ If no active environment is found and none is explicitly provided.
194
+ VariableExistsError
195
+ If a variable with the same name already exists in the environment.
196
+ VariableCreationError
197
+ If the variable is tried to be created with incompatible bounds.
198
+ """
199
+
200
+ @property
201
+ def name(self, /) -> str:
202
+ """Get the name of the variable."""
203
+
204
+ @property
205
+ def bounds(self, /) -> Bounds:
206
+ """Get the bounds of the variable."""
207
+
208
+ @overload
209
+ def __add__(self, other: int, /) -> Expression: ...
210
+ @overload
211
+ def __add__(self, other: float, /) -> Expression: ...
212
+ @overload
213
+ def __add__(self, other: Variable, /) -> Expression: ...
214
+ @overload
215
+ def __add__(self, other: Expression, /) -> Expression: ...
216
+ def __add__(self, other: float | Variable | Expression, /) -> Expression:
217
+ """
218
+ Add this variable to another value.
219
+
220
+ Parameters
221
+ ----------
222
+ other : int, float, Variable or Expression.
223
+
224
+ Returns
225
+ -------
226
+ Expression
227
+ The resulting symbolic expression.
228
+
229
+ Raises
230
+ ------
231
+ VariablesFromDifferentEnvsError
232
+ If the operands belong to different environments.
233
+ TypeError
234
+ If the operand type is unsupported.
235
+ """
236
+
237
+ @overload
238
+ def __radd__(self, other: int, /) -> Expression: ...
239
+ @overload
240
+ def __radd__(self, other: float, /) -> Expression: ...
241
+ @overload
242
+ def __radd__(self, other: Variable, /) -> Expression: ...
243
+ @overload
244
+ def __radd__(self, other: Expression, /) -> Expression: ...
245
+ def __radd__(self, other: float | Variable | Expression, /) -> Expression:
246
+ """
247
+ Right-hand addition.
248
+
249
+ Parameters
250
+ ----------
251
+ other : int, float, Variable or Expression.
252
+
253
+ Returns
254
+ -------
255
+ Expression
256
+ The resulting symbolic expression.
257
+
258
+ Raises
259
+ ------
260
+ TypeError
261
+ If the operand type is unsupported.
262
+ """
263
+
264
+ @overload
265
+ def __sub__(self, other: int, /) -> Expression: ...
266
+ @overload
267
+ def __sub__(self, other: float, /) -> Expression: ...
268
+ @overload
269
+ def __sub__(self, other: Variable, /) -> Expression: ...
270
+ @overload
271
+ def __sub__(self, other: Expression, /) -> Expression: ...
272
+ def __sub__(self, other: float | Variable | Expression, /) -> Expression:
273
+ """
274
+ Subtract a value from this variable.
275
+
276
+ Parameters
277
+ ----------
278
+ other : int, float, Variable or Expression.
279
+
280
+ Returns
281
+ -------
282
+ Expression
283
+ The resulting symbolic expression.
284
+
285
+ Raises
286
+ ------
287
+ VariablesFromDifferentEnvsError
288
+ If the operands belong to different environments.
289
+ TypeError
290
+ If the operand type is unsupported.
291
+ """
292
+
293
+ @overload
294
+ def __rsub__(self, other: int, /) -> Expression: ...
295
+ @overload
296
+ def __rsub__(self, other: float, /) -> Expression: ...
297
+ def __rsub__(self, other: float, /) -> Expression:
298
+ """
299
+ Subtract this variable from a scalar (right-hand subtraction).
300
+
301
+ Parameters
302
+ ----------
303
+ other : int or float
304
+
305
+ Returns
306
+ -------
307
+ Expression
308
+ The resulting symbolic expression.
309
+
310
+ Raises
311
+ ------
312
+ TypeError
313
+ If `other` is not a scalar.
314
+ """
315
+
316
+ @overload
317
+ def __mul__(self, other: int, /) -> Expression: ...
318
+ @overload
319
+ def __mul__(self, other: float, /) -> Expression: ...
320
+ @overload
321
+ def __mul__(self, other: Variable, /) -> Expression: ...
322
+ @overload
323
+ def __mul__(self, other: Expression, /) -> Expression: ...
324
+ def __mul__(self, other: float | Variable | Expression, /) -> Expression:
325
+ """
326
+ Multiply this variable by another value.
327
+
328
+ Parameters
329
+ ----------
330
+ other : Variable, Expression, int, or float
331
+
332
+ Returns
333
+ -------
334
+ Expression
335
+ The resulting symbolic expression.
336
+
337
+ Raises
338
+ ------
339
+ VariablesFromDifferentEnvsError
340
+ If the operands belong to different environments.
341
+ TypeError
342
+ If the operand type is unsupported.
343
+ """
344
+
345
+ @overload
346
+ def __rmul__(self, other: int, /) -> Expression: ...
347
+ @overload
348
+ def __rmul__(self, other: float, /) -> Expression: ...
349
+ @overload
350
+ def __rmul__(self, other: Variable, /) -> Expression: ...
351
+ @overload
352
+ def __rmul__(self, other: Expression, /) -> Expression: ...
353
+ def __rmul__(self, other: float | Variable | Expression, /) -> Expression:
354
+ """
355
+ Right-hand multiplication for scalars.
356
+
357
+ Parameters
358
+ ----------
359
+ other : int or float
360
+
361
+ Returns
362
+ -------
363
+ Expression
364
+ The resulting symbolic expression.
365
+
366
+ Raises
367
+ ------
368
+ TypeError
369
+ If the operand type is unsupported.
370
+ """
371
+
372
+ def __pow__(self, other: int, /) -> Expression:
373
+ """
374
+ Raise the variable to the power specified by `other`.
375
+
376
+ Parameters
377
+ ----------
378
+ other : int
379
+
380
+ Returns
381
+ -------
382
+ Expression
383
+
384
+ Raises
385
+ ------
386
+ RuntimeError
387
+ If the param `modulo` usually supported for `__pow__` is specified.
388
+ """
389
+
390
+ @overload
391
+ def __eq__(self, rhs: int, /) -> Constraint: ...
392
+ @overload
393
+ def __eq__(self, rhs: float, /) -> Constraint: ...
394
+ @overload
395
+ def __eq__(self, rhs: Expression, /) -> Constraint: ...
396
+ @overload
397
+ def __eq__(self, rhs: Variable, /) -> bool:
398
+ """
399
+ Check equality of two variables.
400
+
401
+ Parameters
402
+ ----------
403
+ rhs : Variable
404
+
405
+ Returns
406
+ -------
407
+ bool
408
+ """
409
+
410
+ def __eq__(self, rhs: float | Expression, /) -> Constraint: # type: ignore
411
+ """
412
+ Create a constraint: Variable == float | int | Expression.
413
+
414
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
415
+ constraint, resulting in the following constraint:
416
+
417
+ self - rhs == 0
418
+
419
+ Parameters
420
+ ----------
421
+ rhs : float, int or Expression
422
+
423
+ Returns
424
+ -------
425
+ Constraint
426
+
427
+ Raises
428
+ ------
429
+ TypeError
430
+ If the right-hand side is not of type float, int or Expression.
431
+ """
432
+
433
+ @overload
434
+ def __le__(self, rhs: int, /) -> Constraint: ...
435
+ @overload
436
+ def __le__(self, rhs: float, /) -> Constraint: ...
437
+ @overload
438
+ def __le__(self, rhs: Variable, /) -> Constraint: ...
439
+ @overload
440
+ def __le__(self, rhs: Expression, /) -> Constraint: ...
441
+ def __le__(self, rhs: float | Variable | Expression, /) -> Constraint: # type: ignore
442
+ """
443
+ Create a constraint: Variable <= scalar.
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, Variable 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, Variable or Expression.
462
+ """
463
+
464
+ @overload
465
+ def __ge__(self, rhs: int, /) -> Constraint: ...
466
+ @overload
467
+ def __ge__(self, rhs: float, /) -> Constraint: ...
468
+ @overload
469
+ def __ge__(self, rhs: Variable, /) -> Constraint: ...
470
+ @overload
471
+ def __ge__(self, rhs: Expression, /) -> Constraint: ...
472
+ def __ge__(self, rhs: 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
+ def __neg__(self, /) -> Expression:
496
+ """
497
+ Negate the variable, i.e., multiply it by `-1`.
498
+
499
+ Returns
500
+ -------
501
+ Expression
502
+ """
503
+
504
+ def __hash__(self, /) -> int: ...
505
+
506
+ # _timing.pyi
507
+ class Timing:
508
+ """
509
+ The object that holds information about an algorithm's runtime.
510
+
511
+ This class can only be constructed using a `Timer`. This ensures that a
512
+ `Timing` object always contains a start as well as an end time.
513
+
514
+ The `qpu` field of this class can only be set after constructing it with a timer.
515
+
516
+ Examples
517
+ --------
518
+ >>> from dwave.samplers.tree.solve import BinaryQuadraticModel
519
+ >>> from luna_quantum import Model, Timer, Timing
520
+ >>> model = ... # third-party model
521
+ >>> algorithm = ... # third-party algorithm
522
+ >>> timer = Timer.start()
523
+ >>> sol = algorithm.run(model)
524
+ >>> timing: Timing = timer.stop()
525
+ >>> timing.qpu = sol.qpu_time
526
+ >>> timing.total_seconds
527
+ 1.2999193
528
+ >>> timing.qpu
529
+ 0.02491934
530
+ """
531
+
532
+ @property
533
+ def start(self, /) -> datetime:
534
+ """The starting time of the algorithm."""
535
+
536
+ @property
537
+ def end(self, /) -> datetime:
538
+ """The end, or finishing, time of the algorithm."""
539
+
540
+ @property
541
+ def total(self, /) -> timedelta:
542
+ """
543
+ The difference of the end and start time.
544
+
545
+ Raises
546
+ ------
547
+ RuntimeError
548
+ If total cannot be computed due to an inconsistent start or end time.
549
+ """
550
+
551
+ @property
552
+ def total_seconds(self, /) -> float:
553
+ """
554
+ The total time in seconds an algorithm needed to run. Computed as the
555
+ difference of end and start time.
556
+
557
+ Raises
558
+ ------
559
+ RuntimeError
560
+ If total_seconds cannot be computed due to an inconsistent start or end time.
561
+ """
562
+
563
+ @property
564
+ def qpu(self, /) -> float | None:
565
+ """The qpu usage time of the algorithm this timing object was created for."""
566
+
567
+ @qpu.setter
568
+ def qpu(self, /, value: float | None):
569
+ """
570
+ Set the qpu usage time.
571
+
572
+ Raises
573
+ ------
574
+ ValueError
575
+ If `value` is negative.
576
+ """
577
+
578
+ def add_qpu(self, /, value: float):
579
+ """
580
+ Add qpu usage time to the qpu usage time already present. If the current value
581
+ is None, this method acts like a setter.
582
+
583
+ Parameters
584
+ ----------
585
+ value : float
586
+ The value to add to the already present qpu value.
587
+
588
+ Raises
589
+ ------
590
+ ValueError
591
+ If `value` is negative.
592
+ """
593
+
594
+ class Timer:
595
+ """
596
+ Used to measure the computation time of an algorithm.
597
+
598
+ The sole purpose of the `Timer` class is to create a `Timing` object in a safe
599
+ way, i.e., to ensure that the `Timing` object always holds a starting and
600
+ finishing time.
601
+
602
+ Examples
603
+ --------
604
+ Basic usage:
605
+ >>> from luna_quantum import Timer
606
+ >>> timer = Timer.start()
607
+ >>> solution = ... # create a solution by running an algorithm.
608
+ >>> timing = timer.stop()
609
+ """
610
+
611
+ @staticmethod
612
+ def start() -> Timer:
613
+ """
614
+ Create a timer that starts counting immediately.
615
+
616
+ Returns
617
+ -------
618
+ Timer
619
+ The timer.
620
+ """
621
+
622
+ def stop(self, /) -> Timing:
623
+ """
624
+ Stop the timer, and get the resulting `Timing` object.
625
+
626
+ Returns
627
+ -------
628
+ Timing
629
+ The timing object that holds the start and end time.
630
+ """
631
+
632
+ # _solution.pyi
633
+ class Solution:
634
+ """
635
+ The solution object that is obtained by running an algorihtm.
636
+
637
+ The `Solution` class represents a summary of all data obtained from solving a
638
+ model. It contains samples, i.e., assignments of values to each model variable as
639
+ returned by the algorithm, metadata about the solution quality, e.g., the objective
640
+ value, and the runtime of the algorithm.
641
+
642
+ A `Solution` can be constructed explicitly using `from_dict` or by obtaining a solution
643
+ from an algorithm or by converting a different solution format with one of the available
644
+ translators. Note that the latter requires the environment the model was created in.
645
+
646
+ Examples
647
+ --------
648
+ Basic usage, assuming that the algorithm already returns a `Solution`:
649
+
650
+ >>> from luna_quantum import Model, Solution
651
+ >>> model: Model = ...
652
+ >>> algorithm = ...
653
+ >>> solution: Solution = algorithm.run(model)
654
+ >>> solution.samples
655
+ [[1, 0, 1], [0, 0, 1]]
656
+
657
+ When you have a `dimod.Sampleset` as the raw solution format:
658
+
659
+ >>> from luna_quantum.translator import BqmTranslator
660
+ >>> from luna_quantum import Model, Solution, DwaveTranslator
661
+ >>> from dimod import SimulatedAnnealingSampler
662
+ >>> model: Model = ...
663
+ >>> bqm = BqmTranslator.from_aq(model)
664
+ >>> sampleset = SimulatedAnnealingSampler().sample(bqm)
665
+ >>> solution = DwaveTranslator.from_dimod_sample_set(sampleset)
666
+ >>> solution.samples
667
+ [[1, 0, 1], [0, 0, 1]]
668
+
669
+ Serialization:
670
+
671
+ >>> blob = solution.encode()
672
+ >>> restored = Solution.decode(blob)
673
+ >>> restored.samples
674
+ [[1, 0, 1], [0, 0, 1]]
675
+
676
+ Notes
677
+ -----
678
+ - To ensure metadata like objective values or feasibility, use `model.evaluate(solution)`.
679
+ - Use `encode()` and `decode()` to serialize and recover solutions.
680
+ """
681
+
682
+ def __len__(self, /) -> int: ...
683
+ def __iter__(self, /) -> ResultIterator:
684
+ """
685
+ Extract a result view from the `Solution` object.
686
+
687
+ Returns
688
+ -------
689
+ ResultView
690
+
691
+ Raises
692
+ ------
693
+ TypeError
694
+ If `item` has the wrong type.
695
+ IndexError
696
+ If the row index is out of bounds for the variable environment.
697
+ """
698
+
699
+ def __getitem__(self, item: int, /) -> ResultView:
700
+ """
701
+ Extract a result view from the `Solution` object.
702
+
703
+ Returns
704
+ -------
705
+ ResultView
706
+
707
+ Raises
708
+ ------
709
+ TypeError
710
+ If `item` has the wrong type.
711
+ IndexError
712
+ If the row index is out of bounds for the variable environment.
713
+ """
714
+
715
+ def __eq__(self, other: Solution, /) -> bool: # type: ignore
716
+ """
717
+ Check whether this solution is equal to `other`.
718
+
719
+ Parameters
720
+ ----------
721
+ other : Model
722
+
723
+ Returns
724
+ -------
725
+ bool
726
+ """
727
+
728
+ def best(self, /) -> ResultView | None:
729
+ """
730
+ Get the best result of the solution if it exists.
731
+
732
+ A best solution is defined as the result with the lowest (in case of Sense.Min)
733
+ or the highest (in case of Sense.Max) objective value that is feasible.
734
+
735
+ Returns
736
+ -------
737
+ ResultView
738
+ The best result of the solution as a view.
739
+ """
740
+
741
+ @property
742
+ def results(self, /) -> ResultIterator:
743
+ """Get an iterator over the single results of the solution."""
744
+
745
+ @property
746
+ def samples(self, /) -> Samples:
747
+ """Get a view into the samples of the solution."""
748
+
749
+ @property
750
+ def obj_values(self, /) -> NDArray:
751
+ """
752
+ Get the objective values of the single samples as a ndarray. A value will be
753
+ None if the sample hasn't yet been evaluated.
754
+ """
755
+
756
+ @property
757
+ def raw_energies(self, /) -> NDArray:
758
+ """
759
+ Get the raw energy values of the single samples as returned by the solver /
760
+ algorithm. Will be None if the solver / algorithm did not provide a value.
761
+ """
762
+
763
+ @property
764
+ def counts(self, /) -> NDArray:
765
+ """Return how often each sample occurred in the solution."""
766
+
767
+ @property
768
+ def runtime(self, /) -> Timing | None:
769
+ """Get the solver / algorithm runtime."""
770
+
771
+ @property
772
+ def best_sample_idx(self, /) -> int | None:
773
+ """Get the index of the sample with the best objective value."""
774
+
775
+ @property
776
+ def variable_names(self, /) -> list[str]:
777
+ """Get the names of all variables in the solution."""
778
+
779
+ def expectation_value(self, /) -> float:
780
+ """
781
+ Compute the expectation value of the solution.
782
+
783
+ Returns
784
+ -------
785
+ float
786
+ The expectation value.
787
+
788
+ Raises
789
+ ------
790
+ ComputationError
791
+ If the computation fails for any reason.
792
+ """
793
+
794
+ @overload
795
+ def encode(self, /) -> bytes: ...
796
+ @overload
797
+ def encode(self, /, *, compress: bool) -> bytes: ...
798
+ @overload
799
+ def encode(self, /, *, level: int) -> bytes: ...
800
+ @overload
801
+ def encode(self, /, *, compress: bool, level: int) -> bytes:
802
+ """
803
+ Serialize the solution into a compact binary format.
804
+
805
+ Parameters
806
+ ----------
807
+ compress : bool, optional
808
+ Whether to compress the binary output. Default is True.
809
+ level : int, optional
810
+ Compression level (0–9). Default is 3.
811
+
812
+ Returns
813
+ -------
814
+ bytes
815
+ Encoded model representation.
816
+
817
+ Raises
818
+ ------
819
+ IOError
820
+ If serialization fails.
821
+ """
822
+
823
+ @overload
824
+ def serialize(self, /) -> bytes: ...
825
+ @overload
826
+ def serialize(self, /, *, compress: bool) -> bytes: ...
827
+ @overload
828
+ def serialize(self, /, *, level: int) -> bytes: ...
829
+ @overload
830
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
831
+ def serialize(
832
+ self, /, compress: bool | None = ..., level: int | None = ...
833
+ ) -> bytes:
834
+ """
835
+ Alias for `encode()`.
836
+
837
+ See `encode()` for details.
838
+ """
839
+
840
+ @classmethod
841
+ def decode(cls, data: bytes) -> Solution:
842
+ """
843
+ Reconstruct a solution object from binary data.
844
+
845
+ Parameters
846
+ ----------
847
+ data : bytes
848
+ Serialized model blob created by `encode()`.
849
+
850
+ Returns
851
+ -------
852
+ Solution
853
+ The reconstructed solution.
854
+
855
+ Raises
856
+ ------
857
+ DecodeError
858
+ If decoding fails due to corruption or incompatibility.
859
+ """
860
+
861
+ @classmethod
862
+ def deserialize(cls, data: bytes) -> Solution:
863
+ """Alias for `decode()`."""
864
+
865
+ @staticmethod
866
+ def build(
867
+ component_types: list[Vtype],
868
+ *,
869
+ variable_names: list[str] | None = ...,
870
+ binary_cols: list[list[int]] | None = ...,
871
+ spin_cols: list[list[int]] | None = ...,
872
+ int_cols: list[list[int]] | None = ...,
873
+ real_cols: list[list[float]] | None = ...,
874
+ raw_energies: list[float | None] | None = ...,
875
+ timing: Timing | None = ...,
876
+ counts: list[int] | None = ...,
877
+ ) -> Solution:
878
+ """
879
+ Build a `Solution` based on the provided input data. The solution is constructed
880
+ based on a column layout of the solution. Let's take the following sample-set with three
881
+ samples as an example:
882
+
883
+ [ 0 1 -1 3 2.2 1 ]
884
+ [ 1 0 -1 6 3.8 0 ]
885
+ [ 1 1 +1 2 2.4 0 ]
886
+
887
+ Each row encodes a single sample. However, the variable types vary, the first, second, and
888
+ last columns all represent a Binary variable (index 0, 1, 5). The third column represents a
889
+ variable of type Spin (index 2). The fourth column (index 3), a variable of type Integer and
890
+ the fifth column (index 4), a real-valued variable.
891
+
892
+ Thus, the `component_types` list is:
893
+
894
+ >>> component_types = [Vtype.Binary, Vtype.Binary, Vtype.Spin, Vtype.Integer, Vtype.Real, Vtype.Binary]
895
+
896
+ Now we can extract all columns for a binary-valued variable and append them to a new list:
897
+
898
+ >>> binary_cols = [[0, 1, 1], [1, 0, 1], [1, 0, 0]]
899
+
900
+ where the first element in the list represents the first column, the second element the\
901
+ second column and the third element the fifth column.
902
+ We do the same for the remaining variable types:
903
+
904
+ >>> spin_cols = [[-1, -1, +1]]
905
+ >>> int_cols = [[3, 6, 2]]
906
+ >>> real_cols = [[2.2, 3.8, 2.4]]
907
+
908
+ If we know the raw energies, we can construct them as well:
909
+
910
+ >>> raw_energies = [-200, -100, +300]
911
+
912
+ And finally call the `build` function:
913
+
914
+ >>> sol = Solution.build(
915
+ ... component_types,
916
+ ... binary_cols,
917
+ ... spin_cols,
918
+ ... int_cols,
919
+ ... real_cols,
920
+ ... raw_energies,
921
+ ... timing,
922
+ ... counts=[1, 1, 1]
923
+ ... )
924
+ >>> sol
925
+
926
+ In this example, we could also neglect the `counts` as it defaults to `1`
927
+ for all samples if not set:
928
+
929
+ >>> sol = Solution.build(
930
+ ... component_types,
931
+ ... binary_cols,
932
+ ... spin_cols,
933
+ ... int_cols,
934
+ ... real_cols,
935
+ ... raw_energies,
936
+ ... timing
937
+ ... )
938
+ >>> sol
939
+
940
+
941
+ Parameters
942
+ ----------
943
+ component_types : list[Vtype]
944
+ The variable type each element in a sample encodes.
945
+ variable_names : list[Vtype], optional
946
+ The name of each variable in the solution.
947
+ binary_cols : list[list[int]], optional
948
+ The data of all binary valued columns. Each inner list encodes a single binary-valued
949
+ column. Required if any element in the `component_types` is `Vtype.Binary`.
950
+ spin_cols : list[list[int]], optional
951
+ The data of all spin-valued columns. Each inner list encodes a single spin-valued
952
+ column. Required if any element in the `component_types` is `Vtype.Spin`.
953
+ int_cols : list[list[int]], optional
954
+ The data of all integer-valued columns. Each inner list encodes a single integer valued
955
+ column. Required if any element in the `component_types` is `Vtype.Integer`.
956
+ real_cols : list[list[float]], optional
957
+ The data of all real-valued columns. Each inner list encodes a single real-valued
958
+ column. Required if any element in the `component_types` is `Vtype.Real`.
959
+ raw_energies : list[float, optional], optional
960
+ The data of all real valued columns. Each inner list encodes a single real-valued
961
+ column.
962
+ timing : Timing, optional
963
+ The timing data.
964
+ counts : list[int], optional
965
+ The number how often each sample in the solution has occurred. By default, 1 for all
966
+ samples.
967
+
968
+ Returns
969
+ -------
970
+ Solution
971
+ The constructed solution
972
+
973
+ Raises
974
+ ------
975
+ RuntimeError
976
+ If a sample column has an incorrect number of samples or if `counts` has
977
+ a length different from the number of samples given.
978
+ """
979
+
980
+ @overload
981
+ @staticmethod
982
+ def from_dict(data: dict[Variable, int]) -> Solution: ...
983
+ @overload
984
+ @staticmethod
985
+ def from_dict(data: dict[Variable, float]) -> Solution: ...
986
+ @overload
987
+ @staticmethod
988
+ def from_dict(data: dict[str, int]) -> Solution: ...
989
+ @overload
990
+ @staticmethod
991
+ def from_dict(data: dict[str, float]) -> Solution: ...
992
+ @overload
993
+ @staticmethod
994
+ def from_dict(data: dict[Variable | str, int | float]) -> Solution: ...
995
+ @overload
996
+ @staticmethod
997
+ def from_dict(data: dict[Variable, int], *, env: Environment) -> Solution: ...
998
+ @overload
999
+ @staticmethod
1000
+ def from_dict(data: dict[Variable, float], *, env: Environment) -> Solution: ...
1001
+ @overload
1002
+ @staticmethod
1003
+ def from_dict(data: dict[str, int], *, env: Environment) -> Solution: ...
1004
+ @overload
1005
+ @staticmethod
1006
+ def from_dict(data: dict[str, float], *, env: Environment) -> Solution: ...
1007
+ @overload
1008
+ @staticmethod
1009
+ def from_dict(
1010
+ data: dict[Variable | str, int | float], *, env: Environment
1011
+ ) -> Solution: ...
1012
+ @overload
1013
+ @staticmethod
1014
+ def from_dict(data: dict[Variable, int], *, model: Model) -> Solution: ...
1015
+ @overload
1016
+ @staticmethod
1017
+ def from_dict(data: dict[Variable, float], *, model: Model) -> Solution: ...
1018
+ @overload
1019
+ @staticmethod
1020
+ def from_dict(data: dict[str, int], *, model: Model) -> Solution: ...
1021
+ @overload
1022
+ @staticmethod
1023
+ def from_dict(data: dict[str, float], *, model: Model) -> Solution: ...
1024
+ @overload
1025
+ @staticmethod
1026
+ def from_dict(
1027
+ data: dict[Variable | str, int | float], *, model: Model
1028
+ ) -> Solution: ...
1029
+ @staticmethod
1030
+ def from_dict(
1031
+ data: dict[Variable | str, int | float],
1032
+ *,
1033
+ env: Environment | None = ...,
1034
+ model: Model | None = ...,
1035
+ timing: Timing | None = ...,
1036
+ ) -> Solution:
1037
+ """
1038
+ Create a `Solution` from a dict that maps variables or variable names to their
1039
+ assigned values.
1040
+
1041
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1042
+ there has to be an environment present to determine the correct variable types.
1043
+
1044
+ Parameters
1045
+ ----------
1046
+ data : dict[Variable | str, int | float]
1047
+ The sample that shall be part of the solution.
1048
+ env : Environment, optional
1049
+ The environment the variable types shall be determined from.
1050
+ model : Model, optional
1051
+ A model to evaluate the sample with.
1052
+
1053
+ Returns
1054
+ -------
1055
+ Solution
1056
+ The solution object created from the sample dict.
1057
+
1058
+ Raises
1059
+ ------
1060
+ NoActiveEnvironmentFoundError
1061
+ If no environment or model is passed to the method or available from the
1062
+ context.
1063
+ ValueError
1064
+ If `env` and `model` are both present. When this is the case, the user's
1065
+ intention is unclear as the model itself already contains an environment.
1066
+ SolutionTranslationError
1067
+ Generally if the sample translation fails. Might be specified by one of the
1068
+ three following errors.
1069
+ SampleIncorrectLengthErr
1070
+ If a sample has a different number of variables than the environment.
1071
+ SampleUnexpectedVariableError
1072
+ If a sample has a variable that is not present in the environment.
1073
+ ModelVtypeError
1074
+ If the result's variable types are incompatible with the model environment's
1075
+ variable types.
1076
+ """
1077
+
1078
+ @overload
1079
+ @staticmethod
1080
+ def from_dicts(data: list[dict[Variable, int]]) -> Solution: ...
1081
+ @overload
1082
+ @staticmethod
1083
+ def from_dicts(data: list[dict[Variable, float]]) -> Solution: ...
1084
+ @overload
1085
+ @staticmethod
1086
+ def from_dicts(data: list[dict[str, int]]) -> Solution: ...
1087
+ @overload
1088
+ @staticmethod
1089
+ def from_dicts(data: list[dict[str, float]]) -> Solution: ...
1090
+ @overload
1091
+ @staticmethod
1092
+ def from_dicts(data: list[dict[Variable | str, int | float]]) -> Solution: ...
1093
+ @overload
1094
+ @staticmethod
1095
+ def from_dicts(
1096
+ data: list[dict[Variable, int]], *, env: Environment
1097
+ ) -> Solution: ...
1098
+ @overload
1099
+ @staticmethod
1100
+ def from_dicts(
1101
+ data: list[dict[Variable, float]], *, env: Environment
1102
+ ) -> Solution: ...
1103
+ @overload
1104
+ @staticmethod
1105
+ def from_dicts(data: list[dict[str, int]], *, env: Environment) -> Solution: ...
1106
+ @overload
1107
+ @staticmethod
1108
+ def from_dicts(data: list[dict[str, float]], *, env: Environment) -> Solution: ...
1109
+ @overload
1110
+ @staticmethod
1111
+ def from_dicts(
1112
+ data: list[dict[Variable | str, int | float]], *, env: Environment
1113
+ ) -> Solution: ...
1114
+ @overload
1115
+ @staticmethod
1116
+ def from_dicts(data: list[dict[Variable, int]], *, model: Model) -> Solution: ...
1117
+ @overload
1118
+ @staticmethod
1119
+ def from_dicts(data: list[dict[Variable, float]], *, model: Model) -> Solution: ...
1120
+ @overload
1121
+ @staticmethod
1122
+ def from_dicts(data: list[dict[str, int]], *, model: Model) -> Solution: ...
1123
+ @overload
1124
+ @staticmethod
1125
+ def from_dicts(data: list[dict[str, float]], *, model: Model) -> Solution: ...
1126
+ @overload
1127
+ @staticmethod
1128
+ def from_dicts(
1129
+ data: list[dict[Variable | str, int | float]], *, model: Model
1130
+ ) -> Solution: ...
1131
+ @staticmethod
1132
+ def from_dicts(
1133
+ data: list[dict[Variable | str, int | float]],
1134
+ *,
1135
+ env: Environment | None = ...,
1136
+ model: Model | None = ...,
1137
+ timing: Timing | None = ...,
1138
+ ) -> Solution:
1139
+ """
1140
+ Create a `Solution` from multiple dicts that map variables or variable names to their
1141
+ assigned values. Duplicate samples contained in the `data` list are aggregated to a single
1142
+ sample.
1143
+
1144
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1145
+ there has to be an environment present to determine the correct variable types.
1146
+
1147
+ Parameters
1148
+ ----------
1149
+ data : list[dict[Variable | str, int | float]]
1150
+ The samples that shall be part of the solution.
1151
+ env : Environment, optional
1152
+ The environment the variable types shall be determined from.
1153
+ model : Model, optional
1154
+ A model to evaluate the sample with.
1155
+
1156
+ Returns
1157
+ -------
1158
+ Solution
1159
+ The solution object created from the sample dict.
1160
+
1161
+ Raises
1162
+ ------
1163
+ NoActiveEnvironmentFoundError
1164
+ If no environment or model is passed to the method or available from the
1165
+ context.
1166
+ ValueError
1167
+ If `env` and `model` are both present. When this is the case, the user's
1168
+ intention is unclear as the model itself already contains an environment.
1169
+ SolutionTranslationError
1170
+ Generally if the sample translation fails. Might be specified by one of the
1171
+ three following errors.
1172
+ SampleIncorrectLengthErr
1173
+ If a sample has a different number of variables than the environment.
1174
+ SampleUnexpectedVariableError
1175
+ If a sample has a variable that is not present in the environment.
1176
+ ModelVtypeError
1177
+ If the result's variable types are incompatible with the model environment's
1178
+ variable types.
1179
+ """
1180
+
1181
+ # _sample.pyi
1182
+ class SamplesIterator:
1183
+ """
1184
+ An iterator over a solution's samples.
1185
+
1186
+ Examples
1187
+ --------
1188
+ >>> from luna_quantum import Solution
1189
+ >>> solution: Solution = ...
1190
+
1191
+ Note: ``solution.samples`` is automatically converted into a ``SamplesIterator``.
1192
+
1193
+ >>> for sample in solution.samples:
1194
+ ... sample
1195
+ [0, -5, 0.28]
1196
+ [1, -4, -0.42]
1197
+ """
1198
+
1199
+ def __iter__(self, /) -> SamplesIterator: ...
1200
+ def __next__(self, /) -> Sample: ...
1201
+
1202
+ class SampleIterator:
1203
+ """
1204
+ An iterator over the variable assignments of a solution's sample.
1205
+
1206
+ Examples
1207
+ --------
1208
+ >>> from luna_quantum import Solution
1209
+ >>> solution: Solution = ...
1210
+ >>> sample = solution.samples[0]
1211
+
1212
+ Note: ``sample`` is automatically converted into a ``SampleIterator``.
1213
+
1214
+ >>> for var in sample:
1215
+ ... var
1216
+ 0
1217
+ -5
1218
+ 0.28
1219
+ """
1220
+
1221
+ def __iter__(self, /) -> SampleIterator: ...
1222
+ def __next__(self, /) -> int | float: ...
1223
+
1224
+ class Samples:
1225
+ """
1226
+ A samples object is simply a set-like object that contains every different sample
1227
+ of a solution.
1228
+
1229
+ The ``Samples`` class is readonly as it's merely a helper class for looking into a
1230
+ solution's different samples.
1231
+
1232
+ Examples
1233
+ --------
1234
+ >>> from luna_quantum import Model, Sample, Solution
1235
+ >>> model: Model = ...
1236
+ >>> solution: Solution = ...
1237
+ >>> samples: Samples = solution.samples
1238
+ >>> samples
1239
+ [0, -5, 0.28]
1240
+ [1, -4, -0.42]
1241
+ """
1242
+
1243
+ @overload
1244
+ def __getitem__(self, item: int, /) -> Sample: ...
1245
+ @overload
1246
+ def __getitem__(self, item: tuple[int, int], /) -> int | float:
1247
+ """
1248
+ Extract a sample or variable assignment from the ``Samples`` object.
1249
+ If ``item`` is an int, returns the sample in this row. If ``item`` is a tuple
1250
+ of ints `(i, j)`, returns the variable assignment in row `i` and column `j`.
1251
+
1252
+ Returns
1253
+ -------
1254
+ Sample or int or float
1255
+
1256
+ Raises
1257
+ ------
1258
+ TypeError
1259
+ If ``item`` has the wrong type.
1260
+ IndexError
1261
+ If the row or column index is out of bounds for the variable environment.
1262
+ """
1263
+
1264
+ def __len__(self, /) -> int:
1265
+ """
1266
+ Get the number of samples present in this sample set.
1267
+
1268
+ Returns
1269
+ -------
1270
+ int
1271
+ """
1272
+
1273
+ def __iter__(self, /) -> SamplesIterator:
1274
+ """
1275
+ Iterate over all samples of this sample set.
1276
+
1277
+ Returns
1278
+ -------
1279
+ SamplesIterator
1280
+ """
1281
+
1282
+ def tolist(self, /) -> list[list[int | float]]:
1283
+ """
1284
+ Convert the sample into a 2-dimensional list where a row constitutes a single
1285
+ sample, and a column constitutes all assignments for a single variable.
1286
+
1287
+ Returns
1288
+ -------
1289
+ list[list[int | float]]
1290
+ The samples object as a 2-dimensional list.
1291
+ """
1292
+
1293
+ class Sample:
1294
+ """
1295
+ A sample object is an assignment of an actual value to each of the models'
1296
+ variables.
1297
+
1298
+ The ``Sample`` class is readonly as it's merely a helper class for looking into a
1299
+ single sample of a solution.
1300
+
1301
+ Note: a ``Sample`` can be converted to ``list[int | float]`` simply by calling
1302
+ ``list(sample)``.
1303
+
1304
+ Examples
1305
+ --------
1306
+ >>> from luna_quantum import Model, Sample, Solution
1307
+ >>> model: Model = ...
1308
+ >>> solution: Solution = ...
1309
+ >>> sample: Sample = solution.samples[0]
1310
+ >>> sample
1311
+ [0, -5, 0.28]
1312
+ """
1313
+
1314
+ @overload
1315
+ def __getitem__(self, item: int, /) -> int | float: ...
1316
+ @overload
1317
+ def __getitem__(self, item: Variable, /) -> int | float: ...
1318
+ def __getitem__(self, item: int | Variable, /) -> int | float:
1319
+ """
1320
+ Extract a variable assignment from the ``Sample`` object.
1321
+
1322
+ Returns
1323
+ -------
1324
+ Sample or int or float
1325
+
1326
+ Raises
1327
+ ------
1328
+ TypeError
1329
+ If ``item`` has the wrong type.
1330
+ IndexError
1331
+ If the row or column index is out of bounds for the variable environment.
1332
+ """
1333
+
1334
+ def __len__(self, /) -> int:
1335
+ """
1336
+ Get the number of variables present in this sample.
1337
+
1338
+ Returns
1339
+ -------
1340
+ int
1341
+ """
1342
+
1343
+ def __iter__(self, /) -> SampleIterator:
1344
+ """
1345
+ Iterate over all variable assignments of this sample.
1346
+
1347
+ Returns
1348
+ -------
1349
+ SampleIterator
1350
+ """
1351
+
1352
+ # _result.pyi
1353
+ class ResultIterator:
1354
+ """
1355
+ An iterator over a solution's results.
1356
+
1357
+ Examples
1358
+ --------
1359
+ >>> from luna_quantum import ResultIterator, Solution
1360
+ >>> solution: Solution = ...
1361
+ >>> results: ResultIterator = solution.results
1362
+ >>> for result in results:
1363
+ ... result.sample
1364
+ [0, -5, 0.28]
1365
+ [1, -4, -0.42]
1366
+ """
1367
+
1368
+ def __iter__(self, /) -> ResultIterator: ...
1369
+ def __next__(self, /) -> ResultView: ...
1370
+
1371
+ class Result:
1372
+ """
1373
+ A result object can be understood as a solution with only one sample.
1374
+
1375
+ It can be obtained by calling `model.evaluate_sample` for a single sample.
1376
+
1377
+ Most properties available for the solution object are also available for a result,
1378
+ but in the singular form. For example, you can call `solution.obj_values`, but
1379
+ `result.obj_value`.
1380
+
1381
+ Examples
1382
+ --------
1383
+ >>> from luna_quantum import Model, Result, Solution
1384
+ >>> model: Model = ...
1385
+ >>> solution: Solution = ...
1386
+ >>> sample = solution.samples[0]
1387
+ >>> result = model.evaluate_sample(sample)
1388
+ >>> result.obj_value
1389
+ -109.42
1390
+ >>> result.sample
1391
+ [0, -5, 0.28]
1392
+ >>> result.constraints
1393
+ [True, False]
1394
+ >>> result.feasible
1395
+ False
1396
+ """
1397
+
1398
+ @property
1399
+ def sample(self, /) -> Sample:
1400
+ """Get the sample of the result."""
1401
+
1402
+ @property
1403
+ def obj_value(self, /) -> float | None:
1404
+ """Get the objective value of the result."""
1405
+
1406
+ @property
1407
+ def constraints(self, /) -> NDArray | None:
1408
+ """
1409
+ Get this result's feasibility values of all constraints. Note that
1410
+ `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1411
+ this result.
1412
+ """
1413
+
1414
+ @property
1415
+ def variable_bounds(self, /) -> NDArray | None:
1416
+ """
1417
+ Get this result's feasibility values of all variable bounds.
1418
+ """
1419
+
1420
+ @property
1421
+ def feasible(self, /) -> bool | None:
1422
+ """Return whether all constraint results are feasible for this result."""
1423
+
1424
+ class ResultView:
1425
+ """
1426
+ A result view object serves as a view into one row of a solution object.
1427
+
1428
+ The `Result` class is readonly as it's merely a helper class for looking into a
1429
+ solution's row, i.e., a single sample and this sample's metadata.
1430
+
1431
+ Most properties available for the solution object are also available for a result,
1432
+ but in the singular form. For example, you can call `solution.obj_values`, but
1433
+ `result.obj_value`.
1434
+
1435
+ Examples
1436
+ --------
1437
+ >>> from luna_quantum import ResultView, Solution
1438
+ >>> solution: Solution = ...
1439
+ >>> result: ResultView = solution[0]
1440
+ >>> result.obj_value
1441
+ -109.42
1442
+ >>> result.sample
1443
+ [0, -5, 0.28]
1444
+ >>> result.constraints
1445
+ [True, False]
1446
+ >>> result.feasible
1447
+ False
1448
+ """
1449
+
1450
+ @property
1451
+ def sample(self, /) -> Sample:
1452
+ """Get the sample of the result."""
1453
+
1454
+ @property
1455
+ def counts(self, /) -> int:
1456
+ """Return how often this result appears in the solution."""
1457
+
1458
+ @property
1459
+ def obj_value(self, /) -> float | None:
1460
+ """
1461
+ Get the objective value of this sample if present. This is the value computed
1462
+ by the corresponding AqModel.
1463
+ """
1464
+
1465
+ @property
1466
+ def raw_energy(self, /) -> float | None:
1467
+ """
1468
+ Get the raw energy returned by the algorithm if present. This value is not
1469
+ guaranteed to be accurate under consideration of the corresponding AqModel.
1470
+ """
1471
+
1472
+ @property
1473
+ def constraints(self, /) -> NDArray | None:
1474
+ """
1475
+ Get this result's feasibility values of all constraints. Note that
1476
+ `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1477
+ this result.
1478
+ """
1479
+
1480
+ @property
1481
+ def variable_bounds(self, /) -> NDArray | None:
1482
+ """
1483
+ Get this result's feasibility values of all variable bounds.
1484
+ """
1485
+
1486
+ @property
1487
+ def feasible(self, /) -> bool | None:
1488
+ """Return whether all constraint results are feasible for this result."""
1489
+
1490
+ def __eq__(self, other: ResultView, /) -> bool: ... # type: ignore
1491
+
1492
+ # _model.pyi
1493
+ class Sense(Enum):
1494
+ """
1495
+ Enumeration of optimization senses supported by the optimization system.
1496
+
1497
+ This enum defines the type of optimization used for a model. The type influences
1498
+ the domain and behavior of the model during optimization.
1499
+ """
1500
+
1501
+ Min = ...
1502
+ """Indicate the objective function to be minimized."""
1503
+
1504
+ Max = ...
1505
+ """Indicate the objective function to be maximized."""
1506
+
1507
+ class Model:
1508
+ """
1509
+ A symbolic optimization model consisting of an objective and constraints.
1510
+
1511
+ The `Model` class represents a structured symbolic optimization problem. It
1512
+ combines a scalar objective `Expression`, a collection of `Constraints`, and
1513
+ a shared `Environment` that scopes all variables used in the model.
1514
+
1515
+ Models can be constructed explicitly by passing an environment, or implicitly
1516
+ by allowing the model to create its own private environment. If constructed
1517
+ inside an active `Environment` context (via `with Environment()`), that context
1518
+ is used automatically.
1519
+
1520
+ Parameters
1521
+ ----------
1522
+ env : Environment, optional
1523
+ The environment in which variables and expressions are created. If not
1524
+ provided, the model will either use the current context (if active), or
1525
+ create a new private environment.
1526
+ name : str, optional
1527
+ An optional name assigned to the model.
1528
+
1529
+ Examples
1530
+ --------
1531
+ Basic usage:
1532
+
1533
+ >>> from luna_quantum import Model, Variable
1534
+ >>> model = Model("MyModel")
1535
+ >>> with model.environment:
1536
+ ... x = Variable("x")
1537
+ ... y = Variable("y")
1538
+ >>> model.objective = x * y + x
1539
+ >>> model.constraints += x >= 0
1540
+ >>> model.constraints += y <= 5
1541
+
1542
+ With explicit environment:
1543
+
1544
+ >>> from luna_quantum import Environment
1545
+ >>> env = Environment()
1546
+ >>> model = Model("ScopedModel", env)
1547
+ >>> with env:
1548
+ ... x = Variable("x")
1549
+ ... model.objective = x * x
1550
+
1551
+ Serialization:
1552
+
1553
+ >>> blob = model.encode()
1554
+ >>> restored = Model.decode(blob)
1555
+ >>> restored.name
1556
+ 'MyModel'
1557
+
1558
+ Notes
1559
+ -----
1560
+ - The `Model` class does not solve the optimization problem.
1561
+ - Use `.objective`, `.constraints`, and `.environment` to access the symbolic content.
1562
+ - Use `encode()` and `decode()` to serialize and recover models.
1563
+ """
1564
+
1565
+ metadata: ModelMetadata | None = ...
1566
+
1567
+ @staticmethod
1568
+ def load_luna(model_id: str, client: ILunaSolve | str | None = None) -> Model: ...
1569
+ def save_luna(self, client: ILunaSolve | str | None = None) -> None: ...
1570
+ def delete_luna(self, client: ILunaSolve | str | None = None) -> None: ...
1571
+ def load_solutions(
1572
+ self, client: ILunaSolve | str | None = None
1573
+ ) -> list[Solution]: ...
1574
+ def load_solve_jobs(
1575
+ self, client: ILunaSolve | str | None = None
1576
+ ) -> list[SolveJob]: ...
1577
+ @overload
1578
+ def __init__(self, /) -> None: ...
1579
+ @overload
1580
+ def __init__(self, /, name: str) -> None: ...
1581
+ @overload
1582
+ def __init__(self, /, name: str, *, sense: Sense) -> None: ...
1583
+ @overload
1584
+ def __init__(self, /, name: str, *, env: Environment) -> None: ...
1585
+ @overload
1586
+ def __init__(self, /, *, sense: Sense) -> None: ...
1587
+ @overload
1588
+ def __init__(self, /, *, env: Environment) -> None: ...
1589
+ @overload
1590
+ def __init__(self, /, *, sense: Sense, env: Environment) -> None: ...
1591
+ @overload
1592
+ def __init__(self, /, name: str, *, sense: Sense, env: Environment) -> None: ...
1593
+ def __init__(
1594
+ self,
1595
+ /,
1596
+ name: str | None = ...,
1597
+ *,
1598
+ sense: Sense | None = ...,
1599
+ env: Environment | None = ...,
1600
+ ) -> None:
1601
+ """
1602
+ Initialize a new symbolic model.
1603
+
1604
+ Parameters
1605
+ ----------
1606
+ name : str, optional
1607
+ An optional name for the model.
1608
+ env : Environment, optional
1609
+ The environment in which the model operates. If not provided, a new
1610
+ environment will be created or inferred from context.
1611
+ """
1612
+
1613
+ def set_sense(self, /, sense: Sense) -> None:
1614
+ """
1615
+ Set the optimization sense of a model.
1616
+
1617
+ Parameters
1618
+ ----------
1619
+ sense : Sense
1620
+ The sense of the model (minimization, maximization)
1621
+ """
1622
+
1623
+ @property
1624
+ def name(self, /) -> str:
1625
+ """Return the name of the model."""
1626
+
1627
+ @property
1628
+ def sense(self, /) -> Sense:
1629
+ """
1630
+ Get the sense of the model
1631
+
1632
+ Returns
1633
+ -------
1634
+ Sense
1635
+ The sense of the model (Min or Max).
1636
+ """
1637
+
1638
+ @property
1639
+ def objective(self, /) -> Expression:
1640
+ """Get the objective expression of the model."""
1641
+
1642
+ @objective.setter
1643
+ def objective(self, value: Expression, /):
1644
+ """Set the objective expression of the model."""
1645
+
1646
+ @property
1647
+ def constraints(self, /) -> Constraints:
1648
+ """Access the set of constraints associated with the model."""
1649
+
1650
+ @constraints.setter
1651
+ def constraints(self, value: Constraints, /):
1652
+ """Replace the model's constraints with a new set."""
1653
+
1654
+ @property
1655
+ def environment(self, /) -> Environment:
1656
+ """Get the environment in which this model is defined."""
1657
+
1658
+ @overload
1659
+ def variables(self, /) -> list[Variable]: ...
1660
+ @overload
1661
+ def variables(self, /, *, active: bool) -> list[Variable]: ...
1662
+ def variables(self, /, active: bool | None = ...) -> list[Variable]:
1663
+ """
1664
+ Get all variables that are part of this model.
1665
+
1666
+ Parameters
1667
+ ----------
1668
+ active : bool, optional
1669
+ Instead of all variables from the environment, return only those that are
1670
+ actually present in the model's objective.
1671
+
1672
+ Returns
1673
+ -------
1674
+ The model's variables as a list.
1675
+ """
1676
+
1677
+ @overload
1678
+ def add_constraint(self, /, constraint: Constraint): ...
1679
+ @overload
1680
+ def add_constraint(self, /, constraint: Constraint, name: str): ...
1681
+ def add_constraint(self, /, constraint: Constraint, name: str | None = ...):
1682
+ """
1683
+ Add a constraint to the model's constraint collection.
1684
+
1685
+ Parameters
1686
+ ----------
1687
+ constraint : Constraint
1688
+ The constraint to be added.
1689
+ name : str, optional
1690
+ The name of the constraint to be added.
1691
+ """
1692
+
1693
+ @overload
1694
+ def set_objective(self, /, expression: Expression): ...
1695
+ @overload
1696
+ def set_objective(self, /, expression: Expression, *, sense: Sense): ...
1697
+ def set_objective(self, /, expression: Expression, *, sense: Sense | None = ...):
1698
+ """
1699
+ Set the model's objective to this expression.
1700
+
1701
+ Parameters
1702
+ ----------
1703
+ expression : Expression
1704
+ The expression assigned to the model's objective.
1705
+ sense : Sense, optional
1706
+ The sense of the model for this objective, by default Sense.Min.
1707
+ """
1708
+
1709
+ @property
1710
+ def num_constraints(self, /) -> int:
1711
+ """
1712
+ Return the number of constraints defined in the model.
1713
+
1714
+ Returns
1715
+ -------
1716
+ int
1717
+ Total number of constraints.
1718
+ """
1719
+
1720
+ def evaluate(self, /, solution: Solution) -> Solution:
1721
+ """
1722
+ Evaluate the model given a solution.
1723
+
1724
+ Parameters
1725
+ ----------
1726
+ solution : Solution
1727
+ The solution used to evaluate the model with.
1728
+
1729
+ Returns
1730
+ -------
1731
+ Solution
1732
+ A new solution object with filled-out information.
1733
+ """
1734
+
1735
+ def evaluate_sample(self, /, sample: Sample) -> Result:
1736
+ """
1737
+ Evaluate the model given a single sample.
1738
+
1739
+ Parameters
1740
+ ----------
1741
+ sample : Sample
1742
+ The sample used to evaluate the model with.
1743
+
1744
+ Returns
1745
+ -------
1746
+ Result
1747
+ A result object containing the information from the evaluation process.
1748
+ """
1749
+
1750
+ @overload
1751
+ def encode(self, /) -> bytes: ...
1752
+ @overload
1753
+ def encode(self, /, *, compress: bool) -> bytes: ...
1754
+ @overload
1755
+ def encode(self, /, *, level: int) -> bytes: ...
1756
+ @overload
1757
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
1758
+ def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
1759
+ """
1760
+ Serialize the model into a compact binary format.
1761
+
1762
+ Parameters
1763
+ ----------
1764
+ compress : bool, optional
1765
+ Whether to compress the binary output. Default is True.
1766
+ level : int, optional
1767
+ Compression level (0–9). Default is 3.
1768
+
1769
+ Returns
1770
+ -------
1771
+ bytes
1772
+ Encoded model representation.
1773
+
1774
+ Raises
1775
+ ------
1776
+ IOError
1777
+ If serialization fails.
1778
+ """
1779
+
1780
+ @overload
1781
+ def serialize(self, /) -> bytes: ...
1782
+ @overload
1783
+ def serialize(self, /, *, compress: bool) -> bytes: ...
1784
+ @overload
1785
+ def serialize(self, /, *, level: int) -> bytes: ...
1786
+ @overload
1787
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
1788
+ def serialize(
1789
+ self, /, compress: bool | None = ..., level: int | None = ...
1790
+ ) -> bytes:
1791
+ """
1792
+ Alias for `encode()`.
1793
+
1794
+ See `encode()` for full documentation.
1795
+ """
1796
+
1797
+ @classmethod
1798
+ def decode(cls, data: bytes) -> Model:
1799
+ """
1800
+ Reconstruct a symbolic model from binary data.
1801
+
1802
+ Parameters
1803
+ ----------
1804
+ data : bytes
1805
+ Serialized model blob created by `encode()`.
1806
+
1807
+ Returns
1808
+ -------
1809
+ Model
1810
+ The reconstructed model.
1811
+
1812
+ Raises
1813
+ ------
1814
+ DecodeError
1815
+ If decoding fails due to corruption or incompatibility.
1816
+ """
1817
+
1818
+ @classmethod
1819
+ def deserialize(cls, data: bytes) -> Model:
1820
+ """
1821
+ Alias for `decode()`.
1822
+
1823
+ See `decode()` for full documentation.
1824
+ """
1825
+
1826
+ def __eq__(self, other: Model, /) -> bool: # type: ignore
1827
+ """
1828
+ Check whether this model is equal to `other`.
1829
+
1830
+ Parameters
1831
+ ----------
1832
+ other : Model
1833
+
1834
+ Returns
1835
+ -------
1836
+ bool
1837
+ """
1838
+
1839
+ def __hash__(self, /) -> int: ...
1840
+
1841
+ # _expression.pyi
1842
+ class Expression:
1843
+ """
1844
+ Polynomial expression supporting symbolic arithmetic, constraint creation, and encoding.
1845
+
1846
+ An `Expression` represents a real-valued mathematical function composed of variables,
1847
+ scalars, and coefficients. Expressions may include constant, linear, quadratic, and
1848
+ higher-order terms (cubic and beyond). They are used to build objective functions
1849
+ and constraints in symbolic optimization models.
1850
+
1851
+ Expressions support both regular and in-place arithmetic, including addition and
1852
+ multiplication with integers, floats, `Variable` instances, and other `Expression`s.
1853
+
1854
+ Parameters
1855
+ ----------
1856
+ env : Environment, optional
1857
+ Environment used to scope the expression when explicitly instantiating it.
1858
+ Typically, expressions are constructed implicitly via arithmetic on variables.
1859
+
1860
+ Examples
1861
+ --------
1862
+ Constructing expressions from variables:
1863
+
1864
+ >>> from luna_quantum import Environment, Variable
1865
+ >>> with Environment():
1866
+ ... x = Variable("x")
1867
+ ... y = Variable("y")
1868
+ ... expr = 1 + 2 * x + 3 * x * y + x * y * y
1869
+
1870
+ Inspecting terms:
1871
+
1872
+ >>> expr.get_offset()
1873
+ 1.0
1874
+ >>> expr.get_linear(x)
1875
+ 2.0
1876
+ >>> expr.get_quadratic(x, y)
1877
+ 3.0
1878
+ >>> expr.get_higher_order((x, y, y))
1879
+ 1.0
1880
+
1881
+ In-place arithmetic:
1882
+
1883
+ >>> expr += x
1884
+ >>> expr *= 2
1885
+
1886
+ Creating constraints:
1887
+
1888
+ >>> constraint = expr == 10.0
1889
+ >>> constraint2 = expr <= 15
1890
+
1891
+ Serialization:
1892
+
1893
+ >>> blob = expr.encode()
1894
+ >>> restored = Expression.decode(blob)
1895
+
1896
+ Supported Arithmetic
1897
+ --------------------
1898
+ The following operations are supported:
1899
+
1900
+ - Addition:
1901
+ * `expr + expr` → `Expression`
1902
+ * `expr + variable` → `Expression`
1903
+ * `expr + int | float` → `Expression`
1904
+ * `int | float + expr` → `Expression`
1905
+
1906
+ - In-place addition:
1907
+ * `expr += expr`
1908
+ * `expr += variable`
1909
+ * `expr += int | float`
1910
+
1911
+ - Multiplication:
1912
+ * `expr * expr`
1913
+ * `expr * variable`
1914
+ * `expr * int | float`
1915
+ * `int | float * expr`
1916
+
1917
+ - In-place multiplication:
1918
+ * `expr *= expr`
1919
+ * `expr *= variable`
1920
+ * `expr *= int | float`
1921
+
1922
+ - Constraint creation:
1923
+ * `expr == constant` → `Constraint`
1924
+ * `expr <= constant` → `Constraint`
1925
+ * `expr >= constant` → `Constraint`
1926
+
1927
+ Notes
1928
+ -----
1929
+ - Expressions are mutable: in-place operations (`+=`, `*=`) modify the instance.
1930
+ - Expressions are scoped to an environment via the variables they reference.
1931
+ - Comparisons like `expr == expr` return `bool`, not constraints.
1932
+ - Use `==`, `<=`, `>=` with numeric constants to create constraints.
1933
+ """
1934
+
1935
+ @overload
1936
+ def __init__(self, /) -> None: ...
1937
+ @overload
1938
+ def __init__(self, /, env: Environment) -> None: ...
1939
+ def __init__(self, /, env: Environment | None = ...) -> None:
1940
+ """
1941
+ Create a new empty expression scoped to an environment.
1942
+
1943
+ Parameters
1944
+ ----------
1945
+ env : Environment
1946
+ The environment to which this expression is bound.
1947
+
1948
+ Raises
1949
+ ------
1950
+ NoActiveEnvironmentFoundError
1951
+ If no environment is provided and none is active in the context.
1952
+ """
1953
+
1954
+ def get_offset(self, /) -> float:
1955
+ """
1956
+ Get the constant (offset) term in the expression.
1957
+
1958
+ Returns
1959
+ -------
1960
+ float
1961
+ The constant term.
1962
+ """
1963
+
1964
+ def get_linear(self, /, variable: Variable) -> float:
1965
+ """
1966
+ Get the coefficient of a linear term for a given variable.
1967
+
1968
+ Parameters
1969
+ ----------
1970
+ variable : Variable
1971
+ The variable whose linear coefficient is being queried.
1972
+
1973
+ Returns
1974
+ -------
1975
+ float
1976
+ The coefficient, or 0.0 if the variable is not present.
1977
+
1978
+ Raises
1979
+ ------
1980
+ VariableOutOfRangeError
1981
+ If the variable index is not valid in this expression's environment.
1982
+ """
1983
+
1984
+ def get_quadratic(self, /, u: Variable, v: Variable) -> float:
1985
+ """
1986
+ Get the coefficient for a quadratic term (u * v).
1987
+
1988
+ Parameters
1989
+ ----------
1990
+ u : Variable
1991
+ v : Variable
1992
+
1993
+ Returns
1994
+ -------
1995
+ float
1996
+ The coefficient, or 0.0 if not present.
1997
+
1998
+ Raises
1999
+ ------
2000
+ VariableOutOfRangeError
2001
+ If either variable is out of bounds for the expression's environment.
2002
+ """
2003
+
2004
+ def get_higher_order(self, /, variables: tuple[Variable, ...]) -> float:
2005
+ """
2006
+ Get the coefficient for a higher-order term (degree ≥ 3).
2007
+
2008
+ Parameters
2009
+ ----------
2010
+ variables : tuple of Variable
2011
+ A tuple of variables specifying the term.
2012
+
2013
+ Returns
2014
+ -------
2015
+ float
2016
+ The coefficient, or 0.0 if not present.
2017
+
2018
+ Raises
2019
+ ------
2020
+ VariableOutOfRangeError
2021
+ If any variable is out of bounds for the environment.
2022
+ """
2023
+
2024
+ @property
2025
+ def num_variables(self, /) -> int:
2026
+ """
2027
+ Return the number of distinct variables in the expression.
2028
+
2029
+ Returns
2030
+ -------
2031
+ int
2032
+ Number of variables with non-zero coefficients.
2033
+ """
2034
+
2035
+ def is_equal(self, /, other: Expression) -> bool:
2036
+ """
2037
+ Compare two expressions for equality.
2038
+
2039
+ Parameters
2040
+ ----------
2041
+ other : Expression
2042
+ The expression to which `self` is compared to.
2043
+
2044
+ Returns
2045
+ -------
2046
+ bool
2047
+ If the two expressions are equal.
2048
+ """
2049
+
2050
+ @overload
2051
+ def encode(self, /) -> bytes: ...
2052
+ @overload
2053
+ def encode(self, /, *, compress: bool) -> bytes: ...
2054
+ @overload
2055
+ def encode(self, /, *, level: int) -> bytes: ...
2056
+ @overload
2057
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
2058
+ def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
2059
+ """
2060
+ Serialize the expression into a compact binary format.
2061
+
2062
+ Parameters
2063
+ ----------
2064
+ compress : bool, optional
2065
+ Whether to compress the data. Default is True.
2066
+ level : int, optional
2067
+ Compression level (0–9). Default is 3.
2068
+
2069
+ Returns
2070
+ -------
2071
+ bytes
2072
+ Encoded representation of the expression.
2073
+
2074
+ Raises
2075
+ ------
2076
+ IOError
2077
+ If serialization fails.
2078
+ """
2079
+
2080
+ @overload
2081
+ def serialize(self, /) -> bytes: ...
2082
+ @overload
2083
+ def serialize(self, /, *, compress: bool) -> bytes: ...
2084
+ @overload
2085
+ def serialize(self, /, *, level: int) -> bytes: ...
2086
+ @overload
2087
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
2088
+ def serialize(
2089
+ self, /, compress: bool | None = ..., level: int | None = ...
2090
+ ) -> bytes:
2091
+ """
2092
+ Alias for `encode()`.
2093
+
2094
+ See `encode()` for full documentation.
2095
+ """
2096
+
2097
+ @classmethod
2098
+ def decode(cls, data: bytes) -> Expression:
2099
+ """
2100
+ Reconstruct an expression from encoded bytes.
2101
+
2102
+ Parameters
2103
+ ----------
2104
+ data : bytes
2105
+ Binary blob returned by `encode()`.
2106
+
2107
+ Returns
2108
+ -------
2109
+ Expression
2110
+ Deserialized expression object.
2111
+
2112
+ Raises
2113
+ ------
2114
+ DecodeError
2115
+ If decoding fails due to corruption or incompatibility.
2116
+ """
2117
+
2118
+ @classmethod
2119
+ def deserialize(cls, data: bytes) -> Expression:
2120
+ """
2121
+ Alias for `decode()`.
2122
+
2123
+ See `decode()` for full documentation.
2124
+ """
2125
+
2126
+ @overload
2127
+ def __add__(self, other: Expression, /) -> Expression: ...
2128
+ @overload
2129
+ def __add__(self, other: Variable, /) -> Expression: ...
2130
+ @overload
2131
+ def __add__(self, other: int, /) -> Expression: ...
2132
+ @overload
2133
+ def __add__(self, other: float, /) -> Expression: ...
2134
+ def __add__(self, other: Expression | Variable | float, /) -> Expression:
2135
+ """
2136
+ Add another expression, variable, or scalar.
2137
+
2138
+ Parameters
2139
+ ----------
2140
+ other : Expression, Variable, int, or float
2141
+
2142
+ Returns
2143
+ -------
2144
+ Expression
2145
+
2146
+ Raises
2147
+ ------
2148
+ VariablesFromDifferentEnvsError
2149
+ If operands are from different environments.
2150
+ TypeError
2151
+ If the operand type is unsupported.
2152
+ """
2153
+
2154
+ @overload
2155
+ def __radd__(self, other: Expression, /) -> Expression: ...
2156
+ @overload
2157
+ def __radd__(self, other: Variable, /) -> Expression: ...
2158
+ @overload
2159
+ def __radd__(self, other: int, /) -> Expression: ...
2160
+ @overload
2161
+ def __radd__(self, other: float, /) -> Expression: ...
2162
+ def __radd__(self, other: Expression | Variable | float, /) -> Expression:
2163
+ """
2164
+ Add this expression to a scalar or variable.
2165
+
2166
+ Parameters
2167
+ ----------
2168
+ other : int, float, or Variable
2169
+
2170
+ Returns
2171
+ -------
2172
+ Expression
2173
+
2174
+ Raises
2175
+ ------
2176
+ TypeError
2177
+ If the operand type is unsupported.
2178
+ """
2179
+
2180
+ @overload
2181
+ def __iadd__(self, other: Expression, /): ...
2182
+ @overload
2183
+ def __iadd__(self, other: Variable, /): ...
2184
+ @overload
2185
+ def __iadd__(self, other: int, /): ...
2186
+ @overload
2187
+ def __iadd__(self, other: float, /): ...
2188
+ def __iadd__(self, other: Expression | Variable | float, /) -> Expression:
2189
+ """
2190
+ In-place addition.
2191
+
2192
+ Parameters
2193
+ ----------
2194
+ other : Expression, Variable, int, or float
2195
+
2196
+ Returns
2197
+ -------
2198
+ Expression
2199
+
2200
+ Raises
2201
+ ------
2202
+ VariablesFromDifferentEnvsError
2203
+ If operands are from different environments.
2204
+ TypeError
2205
+ If the operand type is unsupported.
2206
+ """
2207
+
2208
+ @overload
2209
+ def __isub__(self, other: Expression, /): ...
2210
+ @overload
2211
+ def __isub__(self, other: Variable, /): ...
2212
+ @overload
2213
+ def __isub__(self, other: int, /): ...
2214
+ @overload
2215
+ def __isub__(self, other: float, /): ...
2216
+ def __isub__(self, other: Expression | Variable | float, /):
2217
+ """
2218
+ In-place subtraction.
2219
+
2220
+ Parameters
2221
+ ----------
2222
+ other : Expression, Variable, int, or float
2223
+
2224
+ Returns
2225
+ -------
2226
+ Expression
2227
+
2228
+ Raises
2229
+ ------
2230
+ VariablesFromDifferentEnvsError
2231
+ If operands are from different environments.
2232
+ TypeError
2233
+ If the operand type is unsupported.
2234
+ """
2235
+
2236
+ @overload
2237
+ def __sub__(self, other: Expression, /) -> Expression: ...
2238
+ @overload
2239
+ def __sub__(self, other: Variable, /) -> Expression: ...
2240
+ @overload
2241
+ def __sub__(self, other: int, /) -> Expression: ...
2242
+ @overload
2243
+ def __sub__(self, other: float, /) -> Expression: ...
2244
+ def __sub__(self, other: Expression | Variable | float, /) -> Expression:
2245
+ """
2246
+ Subtract another expression, variable, or scalar.
2247
+
2248
+ Parameters
2249
+ ----------
2250
+ other : Expression, Variable, int, or float
2251
+
2252
+ Returns
2253
+ -------
2254
+ Expression
2255
+
2256
+ Raises
2257
+ ------
2258
+ VariablesFromDifferentEnvsError
2259
+ If operands are from different environments.
2260
+ TypeError
2261
+ If the operand type is unsupported.
2262
+ """
2263
+
2264
+ @overload
2265
+ def __mul__(self, other: Expression, /) -> Expression: ...
2266
+ @overload
2267
+ def __mul__(self, other: Variable, /) -> Expression: ...
2268
+ @overload
2269
+ def __mul__(self, other: int, /) -> Expression: ...
2270
+ @overload
2271
+ def __mul__(self, other: float, /) -> Expression: ...
2272
+ def __mul__(self, other: Expression | Variable | float, /) -> Expression:
2273
+ """
2274
+ Multiply this expression by another value.
2275
+
2276
+ Parameters
2277
+ ----------
2278
+ other : Expression, Variable, int, or float
2279
+
2280
+ Returns
2281
+ -------
2282
+ Expression
2283
+
2284
+ Raises
2285
+ ------
2286
+ VariablesFromDifferentEnvsError
2287
+ If operands are from different environments.
2288
+ TypeError
2289
+ If the operand type is unsupported.
2290
+ """
2291
+
2292
+ @overload
2293
+ def __rmul__(self, other: int, /) -> Expression: ...
2294
+ @overload
2295
+ def __rmul__(self, other: float, /) -> Expression: ...
2296
+ def __rmul__(self, other: float, /) -> Expression:
2297
+ """
2298
+ Right-hand multiplication.
2299
+
2300
+ Parameters
2301
+ ----------
2302
+ other : int or float
2303
+
2304
+ Returns
2305
+ -------
2306
+ Expression
2307
+
2308
+ Raises
2309
+ ------
2310
+ TypeError
2311
+ If the operand type is unsupported.
2312
+ """
2313
+
2314
+ @overload
2315
+ def __imul__(self, other: Expression, /): ...
2316
+ @overload
2317
+ def __imul__(self, other: Variable, /): ...
2318
+ @overload
2319
+ def __imul__(self, other: int, /): ...
2320
+ @overload
2321
+ def __imul__(self, other: float, /): ...
2322
+ def __imul__(self, other: Expression | Variable | float, /):
2323
+ """
2324
+ In-place multiplication.
2325
+
2326
+ Parameters
2327
+ ----------
2328
+ other : Expression, Variable, int, or float
2329
+
2330
+ Returns
2331
+ -------
2332
+ Expression
2333
+
2334
+ Raises
2335
+ ------
2336
+ VariablesFromDifferentEnvsError
2337
+ If operands are from different environments.
2338
+ TypeError
2339
+ If the operand type is unsupported.
2340
+ """
2341
+
2342
+ def __pow__(self, other: int, /) -> Expression:
2343
+ """
2344
+ Raise the expression to the power specified by `other`.
2345
+
2346
+ Parameters
2347
+ ----------
2348
+ other : int
2349
+
2350
+ Returns
2351
+ -------
2352
+ Expression
2353
+
2354
+ Raises
2355
+ ------
2356
+ RuntimeError
2357
+ If the param `modulo` usually supported for `__pow__` is specified.
2358
+ """
2359
+
2360
+ @overload
2361
+ def __eq__(self, rhs: Expression, /) -> Constraint: ...
2362
+ @overload
2363
+ def __eq__(self, rhs: Variable, /) -> Constraint: ...
2364
+ @overload
2365
+ def __eq__(self, rhs: int, /) -> Constraint: ... # type: ignore
2366
+ @overload
2367
+ def __eq__(self, rhs: float, /) -> Constraint: ... # type: ignore
2368
+ def __eq__(self, rhs: Expression | Variable | float, /) -> Constraint: # type: ignore
2369
+ """
2370
+ Compare to a different expression or create a constraint `expression == scalar`
2371
+
2372
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
2373
+ constraint, resulting in the following constraint:
2374
+
2375
+ self - rhs == 0
2376
+
2377
+ Parameters
2378
+ ----------
2379
+ rhs : Expression or float, int, Variable or Expression
2380
+
2381
+ Returns
2382
+ -------
2383
+ bool or Constraint
2384
+
2385
+ Raises
2386
+ ------
2387
+ TypeError
2388
+ If the right-hand side is not an Expression or scalar.
2389
+ """
2390
+
2391
+ @overload
2392
+ def __le__(self, rhs: Expression, /) -> Constraint: ...
2393
+ @overload
2394
+ def __le__(self, rhs: Variable, /) -> Constraint: ...
2395
+ @overload
2396
+ def __le__(self, rhs: int, /) -> Constraint: ...
2397
+ @overload
2398
+ def __le__(self, rhs: float, /) -> Constraint: ...
2399
+ def __le__(self, rhs: Expression | Variable | float, /) -> Constraint:
2400
+ """
2401
+ Create a constraint `expression <= scalar`.
2402
+
2403
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
2404
+ constraint, resulting in the following constraint:
2405
+
2406
+ self - rhs <= 0
2407
+
2408
+ Parameters
2409
+ ----------
2410
+ rhs : float, int, Variable or Expression
2411
+
2412
+ Returns
2413
+ -------
2414
+ Constraint
2415
+
2416
+ Raises
2417
+ ------
2418
+ TypeError
2419
+ If the right-hand side is not of type float, int, Variable or Expression.
2420
+ """
2421
+
2422
+ @overload
2423
+ def __ge__(self, rhs: Expression, /) -> Constraint: ...
2424
+ @overload
2425
+ def __ge__(self, rhs: Variable, /) -> Constraint: ...
2426
+ @overload
2427
+ def __ge__(self, rhs: int, /) -> Constraint: ...
2428
+ @overload
2429
+ def __ge__(self, rhs: float, /) -> Constraint: ...
2430
+ def __ge__(self, rhs: Expression | Variable | float, /) -> Constraint:
2431
+ """
2432
+ Create a constraint: expression >= scalar.
2433
+
2434
+ If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
2435
+ constraint, resulting in the following constraint:
2436
+
2437
+ self - rhs >= 0
2438
+
2439
+ Parameters
2440
+ ----------
2441
+ rhs : float, int, Variable or Expression
2442
+
2443
+ Returns
2444
+ -------
2445
+ Constraint
2446
+
2447
+ Raises
2448
+ ------
2449
+ TypeError
2450
+ If the right-hand side is not of type float, int, Variable or Expression.
2451
+ """
2452
+
2453
+ def __neg__(self, /) -> Expression:
2454
+ """
2455
+ Negate the expression, i.e., multiply it by `-1`.
2456
+
2457
+ Returns
2458
+ -------
2459
+ Expression
2460
+ """
2461
+
2462
+ # _environment.pyi
2463
+ class Environment:
2464
+ """
2465
+ Execution context for variable creation and expression scoping.
2466
+
2467
+ An `Environment` provides the symbolic scope in which `Variable` objects are defined.
2468
+ It is required for variable construction, and ensures consistency across expressions.
2469
+ The environment does **not** store constraints or expressions — it only facilitates
2470
+ their creation by acting as a context manager and anchor for `Variable` instances.
2471
+
2472
+ Environments are best used with `with` blocks, but can also be passed manually
2473
+ to models or variables.
2474
+
2475
+ Examples
2476
+ --------
2477
+ Create variables inside an environment:
2478
+
2479
+ >>> from luna_quantum import Environment, Variable
2480
+ >>> with Environment() as env:
2481
+ ... x = Variable("x")
2482
+ ... y = Variable("y")
2483
+
2484
+ Serialize the environment state:
2485
+
2486
+ >>> data = env.encode()
2487
+ >>> expr = Environment.decode(data)
2488
+
2489
+ Notes
2490
+ -----
2491
+ - The environment is required to create `Variable` instances.
2492
+ - It does **not** own constraints or expressions — they merely reference variables tied to an environment.
2493
+ - Environments **cannot be nested**. Only one can be active at a time.
2494
+ - Use `encode()` / `decode()` to persist and recover expression trees.
2495
+ """
2496
+
2497
+ def __init__(self, /) -> None:
2498
+ """
2499
+ Initialize a new environment for variable construction.
2500
+
2501
+ It is recommended to use this in a `with` statement to ensure proper scoping.
2502
+ """
2503
+
2504
+ def __enter__(self, /) -> Any:
2505
+ """
2506
+ Activate this environment for variable creation.
2507
+
2508
+ Returns
2509
+ -------
2510
+ Environment
2511
+ The current environment (self).
2512
+
2513
+ Raises
2514
+ ------
2515
+ MultipleActiveEnvironmentsError
2516
+ If another environment is already active.
2517
+ """
2518
+
2519
+ def __exit__(self, /, exc_type, exc_value, exc_traceback) -> None:
2520
+ """
2521
+ Deactivate this environment.
2522
+
2523
+ Called automatically at the end of a `with` block.
2524
+ """
2525
+
2526
+ def get_variable(self, /, name: str) -> Variable:
2527
+ """
2528
+ Get a variable by its label (name).
2529
+
2530
+ Parameters
2531
+ ----------
2532
+ name : str
2533
+ The name/label of the variable
2534
+
2535
+ Returns
2536
+ -------
2537
+ Variable
2538
+ The variable with the specified label/name.
2539
+
2540
+ Raises
2541
+ ------
2542
+ VariableNotExistingError
2543
+ If no variable with the specified name is registered.
2544
+ """
2545
+
2546
+ @overload
2547
+ def encode(self, /) -> bytes: ...
2548
+ @overload
2549
+ def encode(self, /, *, compress: bool) -> bytes: ...
2550
+ @overload
2551
+ def encode(self, /, *, level: int) -> bytes: ...
2552
+ @overload
2553
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
2554
+ def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
2555
+ """
2556
+ Serialize the environment into a compact binary format.
2557
+
2558
+ This is the preferred method for persisting an environment's state.
2559
+
2560
+ Parameters
2561
+ ----------
2562
+ compress : bool, optional
2563
+ Whether to compress the binary output. Default is `True`.
2564
+ level : int, optional
2565
+ Compression level (e.g., from 0 to 9). Default is `3`.
2566
+
2567
+ Returns
2568
+ -------
2569
+ bytes
2570
+ Encoded binary representation of the environment.
2571
+
2572
+ Raises
2573
+ ------
2574
+ IOError
2575
+ If serialization fails.
2576
+ """
2577
+
2578
+ @overload
2579
+ def serialize(self, /) -> bytes: ...
2580
+ @overload
2581
+ def serialize(self, /, *, compress: bool) -> bytes: ...
2582
+ @overload
2583
+ def serialize(self, /, *, level: int) -> bytes: ...
2584
+ @overload
2585
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
2586
+ def serialize(
2587
+ self, /, compress: bool | None = ..., level: int | None = ...
2588
+ ) -> bytes:
2589
+ """
2590
+ Alias for `encode()`.
2591
+
2592
+ See `encode()` for full usage details.
2593
+ """
2594
+
2595
+ @classmethod
2596
+ def decode(cls, data: bytes) -> Environment:
2597
+ """
2598
+ Reconstruct an expression from a previously encoded binary blob.
2599
+
2600
+ Parameters
2601
+ ----------
2602
+ data : bytes
2603
+ The binary data returned from `Environment.encode()`.
2604
+
2605
+ Returns
2606
+ -------
2607
+ Expression
2608
+ The reconstructed symbolic expression.
2609
+
2610
+ Raises
2611
+ ------
2612
+ DecodeError
2613
+ If decoding fails due to corruption or incompatibility.
2614
+ """
2615
+
2616
+ @classmethod
2617
+ def deserialize(cls, data: bytes) -> Environment:
2618
+ """
2619
+ Alias for `decode()`.
2620
+
2621
+ See `decode()` for full usage details.
2622
+ """
2623
+
2624
+ def __eq__(self, other: Environment, /) -> bool: ... # type: ignore
2625
+
2626
+ # _constraints.pyi
2627
+ class Comparator(Enum):
2628
+ """
2629
+ Comparison operators used to define constraints.
2630
+
2631
+ This enum represents the logical relation between the left-hand side (LHS)
2632
+ and the right-hand side (RHS) of a constraint.
2633
+
2634
+ Attributes
2635
+ ----------
2636
+ Eq : Comparator
2637
+ Equality constraint (==).
2638
+ Le : Comparator
2639
+ Less-than-or-equal constraint (<=).
2640
+ Ge : Comparator
2641
+ Greater-than-or-equal constraint (>=).
2642
+
2643
+ Examples
2644
+ --------
2645
+ >>> from luna_quantum import Comparator
2646
+ >>> str(Comparator.Eq)
2647
+ '=='
2648
+ """
2649
+
2650
+ Eq = ...
2651
+ """Equality (==)"""
2652
+
2653
+ Le = ...
2654
+ """Less-than or equal (<=)"""
2655
+
2656
+ Ge = ...
2657
+ """Greater-than or equal (>=)"""
2658
+
2659
+ class Constraint:
2660
+ """
2661
+ A symbolic constraint formed by comparing an expression to a constant.
2662
+
2663
+ A `Constraint` captures a relation of the form:
2664
+ `expression comparator constant`, where the comparator is one of:
2665
+ `==`, `<=`, or `>=`.
2666
+
2667
+ While constraints are usually created by comparing an `Expression` to a scalar
2668
+ (e.g., `expr == 3.0`), they can also be constructed manually using this class.
2669
+
2670
+ Parameters
2671
+ ----------
2672
+ lhs : Expression
2673
+ The left-hand side expression.
2674
+ rhs : float
2675
+ The scalar right-hand side value.
2676
+ comparator : Comparator
2677
+ The relation between lhs and rhs (e.g., `Comparator.Eq`).
2678
+
2679
+ Examples
2680
+ --------
2681
+ >>> from luna_quantum import Environment, Variable, Constraint, Comparator
2682
+ >>> with Environment():
2683
+ ... x = Variable("x")
2684
+ ... c = Constraint(x + 2, 5.0, Comparator.Eq)
2685
+
2686
+ Or create via comparison:
2687
+
2688
+ >>> expr = 2 * x + 1
2689
+ >>> c2 = expr <= 10.0
2690
+ """
2691
+
2692
+ @overload
2693
+ def __init__(
2694
+ self, /, lhs: Expression, rhs: Expression, comparator: Comparator
2695
+ ) -> None: ...
2696
+ @overload
2697
+ def __init__(
2698
+ self, /, lhs: Expression, rhs: Variable, comparator: Comparator
2699
+ ) -> None: ...
2700
+ @overload
2701
+ def __init__(
2702
+ self, /, lhs: Expression, rhs: int, comparator: Comparator
2703
+ ) -> None: ...
2704
+ @overload
2705
+ def __init__(
2706
+ self, /, lhs: Expression, rhs: float, comparator: Comparator
2707
+ ) -> None: ...
2708
+ @overload
2709
+ def __init__(
2710
+ self, /, lhs: Expression, rhs: Expression, comparator: Comparator, name: str
2711
+ ) -> None: ...
2712
+ @overload
2713
+ def __init__(
2714
+ self, /, lhs: Expression, rhs: Variable, comparator: Comparator, name: str
2715
+ ) -> None: ...
2716
+ @overload
2717
+ def __init__(
2718
+ self, /, lhs: Expression, rhs: int, comparator: Comparator, name: str
2719
+ ) -> None: ...
2720
+ @overload
2721
+ def __init__(
2722
+ self, /, lhs: Expression, rhs: float, comparator: Comparator, name: str
2723
+ ) -> None: ...
2724
+ @overload
2725
+ def __init__(
2726
+ self, /, lhs: Variable, rhs: Expression, comparator: Comparator
2727
+ ) -> None: ...
2728
+ @overload
2729
+ def __init__(
2730
+ self, /, lhs: Variable, rhs: Variable, comparator: Comparator
2731
+ ) -> None: ...
2732
+ @overload
2733
+ def __init__(self, /, lhs: Variable, rhs: int, comparator: Comparator) -> None: ...
2734
+ @overload
2735
+ def __init__(
2736
+ self, /, lhs: Variable, rhs: float, comparator: Comparator
2737
+ ) -> None: ...
2738
+ @overload
2739
+ def __init__(
2740
+ self, /, lhs: Variable, rhs: Expression, comparator: Comparator, name: str
2741
+ ) -> None: ...
2742
+ @overload
2743
+ def __init__(
2744
+ self, /, lhs: Variable, rhs: Variable, comparator: Comparator, name: str
2745
+ ) -> None: ...
2746
+ @overload
2747
+ def __init__(
2748
+ self, /, lhs: Variable, rhs: int, comparator: Comparator, name: str
2749
+ ) -> None: ...
2750
+ @overload
2751
+ def __init__(
2752
+ self, /, lhs: Variable, rhs: float, comparator: Comparator, name: str
2753
+ ) -> None: ...
2754
+ def __init__(
2755
+ self,
2756
+ /,
2757
+ lhs: Variable | Expression,
2758
+ rhs: float | Expression | Variable,
2759
+ comparator: Comparator,
2760
+ name: str,
2761
+ ) -> None:
2762
+ """
2763
+ Construct a new symbolic constraint.
2764
+
2765
+ Parameters
2766
+ ----------
2767
+ lhs : Expression | Variable
2768
+ Left-hand side symbolic expression or variable.
2769
+ rhs : int | float | Expression | Variable
2770
+ Scalar right-hand side constant.
2771
+ comparator : Comparator
2772
+ Relational operator (e.g., Comparator.Eq, Comparator.Le).
2773
+ name : str
2774
+ The name of the constraint
2775
+
2776
+ Raises
2777
+ ------
2778
+ TypeError
2779
+ If lhs is not an Expression or rhs is not a scalar float.
2780
+ IllegalConstraintNameError
2781
+ If the constraint is tried to be created with an illegal name.
2782
+ """
2783
+
2784
+ @property
2785
+ def name(self, /) -> str | None:
2786
+ """
2787
+ Get the name of the constraint.
2788
+
2789
+ Returns
2790
+ -------
2791
+ str, optional
2792
+ Returns the name of the constraint as a string or None if it is unnamed.
2793
+ """
2794
+
2795
+ @property
2796
+ def lhs(self, /) -> Expression:
2797
+ """
2798
+ Get the left-hand side of the constraint
2799
+
2800
+ Returns
2801
+ -------
2802
+ Expression
2803
+ The left-hand side expression.
2804
+ """
2805
+
2806
+ @property
2807
+ def rhs(self, /) -> float:
2808
+ """
2809
+ Get the right-hand side of the constraint
2810
+
2811
+ Returns
2812
+ -------
2813
+ float
2814
+ The right-hand side expression.
2815
+ """
2816
+
2817
+ @property
2818
+ def comparator(self, /) -> Comparator:
2819
+ """
2820
+ Get the comparator of the constraint
2821
+
2822
+ Returns
2823
+ -------
2824
+ Comparator
2825
+ The comparator of the constraint.
2826
+ """
2827
+
2828
+ def __eq__(self, other: Constraint, /) -> bool: ... # type: ignore
2829
+
2830
+ class Constraints:
2831
+ """
2832
+ A collection of symbolic constraints used to define a model.
2833
+
2834
+ The `Constraints` object serves as a container for individual `Constraint`
2835
+ instances. It supports adding constraints programmatically and exporting
2836
+ them for serialization.
2837
+
2838
+ Constraints are typically added using `add_constraint()` or the `+=` operator.
2839
+
2840
+ Examples
2841
+ --------
2842
+ >>> from luna_quantum import Constraints, Constraint, Environment, Variable
2843
+ >>> with Environment():
2844
+ ... x = Variable("x")
2845
+ ... c = Constraint(x + 1, 0.0, Comparator.Le)
2846
+
2847
+ >>> cs = Constraints()
2848
+ >>> cs.add_constraint(c)
2849
+
2850
+ >>> cs += x >= 1.0
2851
+
2852
+ Serialization:
2853
+
2854
+ >>> blob = cs.encode()
2855
+ >>> expr = Constraints.decode(blob)
2856
+
2857
+ Notes
2858
+ -----
2859
+ - This class does not check feasibility or enforce satisfaction.
2860
+ - Use `encode()`/`decode()` to serialize constraints alongside expressions.
2861
+ """
2862
+
2863
+ def __init__(self, /) -> None: ...
2864
+ @overload
2865
+ def add_constraint(self, /, constraint: Constraint):
2866
+ """
2867
+ Add a constraint to the collection.
2868
+
2869
+ Parameters
2870
+ ----------
2871
+ constraint : Constraint
2872
+ The constraint to be added.
2873
+ name : str, optional
2874
+ The name of the constraint to be added.
2875
+ """
2876
+
2877
+ @overload
2878
+ def add_constraint(self, /, constraint: Constraint, name: str): ...
2879
+ def add_constraint(self, /, constraint: Constraint, name: str | None = ...):
2880
+ """
2881
+ Add a constraint to the collection.
2882
+
2883
+ Parameters
2884
+ ----------
2885
+ constraint : Constraint
2886
+ The constraint to be added.
2887
+ name : str, optional
2888
+ The name of the constraint to be added.
2889
+ """
2890
+
2891
+ @overload
2892
+ def encode(self, /) -> bytes: ...
2893
+ @overload
2894
+ def encode(self, /, *, compress: bool) -> bytes: ...
2895
+ @overload
2896
+ def encode(self, /, *, level: int) -> bytes: ...
2897
+ @overload
2898
+ def encode(self, /, compress: bool, level: int) -> bytes: ...
2899
+ def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
2900
+ """
2901
+ Serialize the constraint collection to a binary blob.
2902
+
2903
+ Parameters
2904
+ ----------
2905
+ compress : bool, optional
2906
+ Whether to compress the result. Default is True.
2907
+ level : int, optional
2908
+ Compression level (0–9). Default is 3.
2909
+
2910
+ Returns
2911
+ -------
2912
+ bytes
2913
+ Encoded representation of the constraints.
2914
+
2915
+ Raises
2916
+ ------
2917
+ IOError
2918
+ If serialization fails.
2919
+ """
2920
+
2921
+ @overload
2922
+ def serialize(self, /) -> bytes: ...
2923
+ @overload
2924
+ def serialize(self, /, *, compress: bool) -> bytes: ...
2925
+ @overload
2926
+ def serialize(self, /, *, level: int) -> bytes: ...
2927
+ @overload
2928
+ def serialize(self, /, compress: bool, level: int) -> bytes: ...
2929
+ def serialize(
2930
+ self, /, compress: bool | None = ..., level: int | None = ...
2931
+ ) -> bytes:
2932
+ """
2933
+ Alias for `encode()`.
2934
+
2935
+ See `encode()` for details.
2936
+ """
2937
+
2938
+ @classmethod
2939
+ def decode(cls, data: bytes, env: Environment) -> Expression:
2940
+ """
2941
+ Deserialize an expression from binary constraint data.
2942
+
2943
+ Parameters
2944
+ ----------
2945
+ data : bytes
2946
+ Encoded blob from `encode()`.
2947
+
2948
+ Returns
2949
+ -------
2950
+ Expression
2951
+ Expression reconstructed from the constraint context.
2952
+
2953
+ Raises
2954
+ ------
2955
+ DecodeError
2956
+ If decoding fails due to corruption or incompatibility.
2957
+ """
2958
+
2959
+ @classmethod
2960
+ def deserialize(cls, data: bytes, env: Environment) -> Expression:
2961
+ """
2962
+ Alias for `decode()`.
2963
+
2964
+ See `decode()` for usage.
2965
+ """
2966
+
2967
+ @overload
2968
+ def __iadd__(self, constraint: Constraint, /): ...
2969
+ @overload
2970
+ def __iadd__(self, constraint: tuple[Constraint, str], /): ...
2971
+ def __iadd__(self, constraint: Constraint | tuple[Constraint, str], /):
2972
+ """
2973
+ In-place constraint addition using `+=`.
2974
+
2975
+ Parameters
2976
+ ----------
2977
+ constraint : Constraint | tuple[Constraint, str]
2978
+ The constraint to add.
2979
+
2980
+ Returns
2981
+ -------
2982
+ Constraints
2983
+ The updated collection.
2984
+
2985
+ Raises
2986
+ ------
2987
+ TypeError
2988
+ If the value is not a `Constraint` or valid symbolic comparison.
2989
+ """
2990
+
2991
+ def __eq__(self, other: Constraints, /) -> bool: ... # type: ignore
2992
+ def __getitem__(self, item: int, /) -> Constraint: ...
2993
+
2994
+ __all__ = [
2995
+ "Bounds",
2996
+ "Comparator",
2997
+ "Constraint",
2998
+ "Constraints",
2999
+ "Environment",
3000
+ "Expression",
3001
+ "Model",
3002
+ "Result",
3003
+ "ResultIterator",
3004
+ "ResultView",
3005
+ "Sample",
3006
+ "SampleIterator",
3007
+ "Samples",
3008
+ "SamplesIterator",
3009
+ "Sense",
3010
+ "Solution",
3011
+ "Timer",
3012
+ "Timing",
3013
+ "Variable",
3014
+ "Vtype",
3015
+ "errors",
3016
+ "translator",
3017
+ ]