luna-quantum 1.0.0__cp313-cp313-macosx_11_0_arm64.whl → 1.0.1rc10__cp313-cp313-macosx_11_0_arm64.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 (40) hide show
  1. luna_quantum/__init__.py +32 -17
  2. luna_quantum/__init__.pyi +23 -14
  3. luna_quantum/_core.cpython-313-darwin.so +0 -0
  4. luna_quantum/_core.pyi +1041 -375
  5. luna_quantum/algorithms/__init__.py +1 -0
  6. luna_quantum/backends/__init__.py +1 -0
  7. luna_quantum/client/rest_client/qpu_token_rest_client.py +7 -3
  8. luna_quantum/client/schemas/circuit.py +5 -6
  9. luna_quantum/client/schemas/create/__init__.py +10 -1
  10. luna_quantum/client/schemas/create/circuit.py +5 -6
  11. luna_quantum/client/schemas/create/qpu_token.py +2 -5
  12. luna_quantum/client/schemas/create/qpu_token_time_quota.py +3 -6
  13. luna_quantum/client/schemas/create/qpu_token_time_quota_update.py +15 -0
  14. luna_quantum/client/schemas/create/solve_job_create.py +1 -1
  15. luna_quantum/client/schemas/qpu_token/qpu_token.py +9 -16
  16. luna_quantum/client/schemas/qpu_token/token_provider.py +3 -6
  17. luna_quantum/errors.py +34 -1
  18. luna_quantum/errors.pyi +83 -26
  19. luna_quantum/solve/domain/solve_job.py +2 -2
  20. luna_quantum/solve/parameters/algorithms/base_params/quantum_annealing_params.py +1 -0
  21. luna_quantum/solve/parameters/algorithms/base_params/scipy_optimizer.py +4 -2
  22. luna_quantum/solve/parameters/algorithms/quantum_annealing/quantum_annealing.py +38 -22
  23. luna_quantum/solve/parameters/algorithms/quantum_gate/flex_qaoa/optimizers.py +4 -2
  24. luna_quantum/solve/parameters/algorithms/quantum_gate/qaoa.py +1 -3
  25. luna_quantum/solve/parameters/algorithms/quantum_gate/vqe.py +2 -3
  26. luna_quantum/solve/parameters/algorithms/search_algorithms/dialectic_search.py +0 -16
  27. luna_quantum/solve/parameters/backends/__init__.py +1 -1
  28. luna_quantum/solve/parameters/backends/dwave_qpu.py +4 -2
  29. luna_quantum/solve/parameters/backends/ibm.py +8 -2
  30. luna_quantum/solve/parameters/backends/qctrl.py +4 -3
  31. luna_quantum/solve/use_cases/hamiltonian_cycle.py +2 -2
  32. luna_quantum/solve/usecases/solve_job_get_result_usecase.py +1 -3
  33. luna_quantum/translator.py +23 -1
  34. luna_quantum/translator.pyi +76 -43
  35. luna_quantum/utils.py +29 -11
  36. {luna_quantum-1.0.0.dist-info → luna_quantum-1.0.1rc10.dist-info}/METADATA +2 -4
  37. {luna_quantum-1.0.0.dist-info → luna_quantum-1.0.1rc10.dist-info}/RECORD +40 -37
  38. {luna_quantum-1.0.0.dist-info → luna_quantum-1.0.1rc10.dist-info}/WHEEL +1 -1
  39. {luna_quantum-1.0.0.dist-info → luna_quantum-1.0.1rc10.dist-info}/licenses/LICENSE +1 -1
  40. {luna_quantum-1.0.0.dist-info → luna_quantum-1.0.1rc10.dist-info}/licenses/NOTICE +0 -0
luna_quantum/_core.pyi CHANGED
@@ -1,15 +1,13 @@
1
1
  from datetime import datetime, timedelta
2
2
  from enum import Enum
3
- from typing import Any, overload
4
-
3
+ from types import TracebackType
4
+ from typing import Literal, Self, overload
5
5
  from numpy.typing import NDArray
6
+ from . import errors, transformations, translator
7
+ from luna_quantum.client.interfaces.services.luna_solve_i import ILunaSolve
8
+ from luna_quantum.solve.domain.model_metadata import ModelMetadata
9
+ from luna_quantum.solve.domain.solve_job import SolveJob
6
10
 
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
11
  class Vtype(Enum):
14
12
  """
15
13
  Enumeration of variable types supported by the optimization system.
@@ -45,10 +43,12 @@ class Vtype(Enum):
45
43
  """Discrete integer-valued variable. Takes integer values within bounds."""
46
44
  Binary = ...
47
45
  """Binary variable. Can only take values 0 or 1."""
48
-
49
46
  Spin = ...
50
47
  """Spin variable. Can only take values -1 or +1."""
51
48
 
49
+ def __str__(self, /) -> str: ...
50
+ def __repr__(self, /) -> str: ...
51
+
52
52
  class Unbounded: ...
53
53
 
54
54
  class Bounds:
@@ -85,33 +85,38 @@ class Bounds:
85
85
  """
86
86
 
87
87
  @overload
88
- def __init__(self, /, *, lower: float | Unbounded) -> None: ...
88
+ def __init__(self, /, *, lower: (float | type[Unbounded])) -> None: ...
89
89
  @overload
90
- def __init__(self, /, *, upper: float | type[Unbounded]) -> None: ...
90
+ def __init__(self, /, *, upper: (float | type[Unbounded])) -> None: ...
91
91
  @overload
92
92
  def __init__(
93
- self, /, lower: float | type[Unbounded], upper: float | type[Unbounded]
93
+ self, /, lower: (float | type[Unbounded]), upper: (float | type[Unbounded])
94
94
  ) -> None: ...
95
- @overload
96
95
  def __init__(
97
96
  self,
98
97
  /,
99
- lower: float | type[Unbounded] | None = ...,
100
- upper: float | type[Unbounded] | None = ...,
98
+ lower: (float | type[Unbounded] | None) = ...,
99
+ upper: (float | type[Unbounded] | None) = ...,
101
100
  ) -> None:
102
101
  """
103
102
  Create bounds for a variable.
104
103
 
105
104
  See class-level docstring for full documentation.
106
105
  """
106
+ ...
107
107
 
108
108
  @property
109
109
  def lower(self, /) -> float | Unbounded | None:
110
- """Get the lower bound"""
110
+ """Get the lower bound."""
111
+ ...
111
112
 
112
113
  @property
113
114
  def upper(self, /) -> float | Unbounded | None:
114
- """Get the upper bound"""
115
+ """Get the upper bound."""
116
+ ...
117
+
118
+ def __str__(self, /) -> str: ...
119
+ def __repr__(self, /) -> str: ...
115
120
 
116
121
  class Variable:
117
122
  """
@@ -166,6 +171,8 @@ class Variable:
166
171
  @overload
167
172
  def __init__(self, /, name: str, *, env: Environment) -> None: ...
168
173
  @overload
174
+ def __init__(self, /, name: str, *, env: Environment, vtype: Vtype) -> None: ...
175
+ @overload
169
176
  def __init__(self, /, name: str, *, vtype: Vtype) -> None: ...
170
177
  @overload
171
178
  def __init__(self, /, name: str, *, vtype: Vtype, bounds: Bounds) -> None: ...
@@ -178,9 +185,9 @@ class Variable:
178
185
  /,
179
186
  name: str,
180
187
  *,
181
- vtype: Vtype | None = ...,
182
- bounds: Bounds | None = ...,
183
- env: Environment | None = ...,
188
+ vtype: (Vtype | None) = ...,
189
+ bounds: (Bounds | None) = ...,
190
+ env: (Environment | None) = ...,
184
191
  ) -> None:
185
192
  """
186
193
  Initialize a new Variable.
@@ -196,14 +203,22 @@ class Variable:
196
203
  VariableCreationError
197
204
  If the variable is tried to be created with incompatible bounds.
198
205
  """
206
+ ...
199
207
 
200
208
  @property
201
209
  def name(self, /) -> str:
202
210
  """Get the name of the variable."""
211
+ ...
203
212
 
204
213
  @property
205
214
  def bounds(self, /) -> Bounds:
206
215
  """Get the bounds of the variable."""
216
+ ...
217
+
218
+ @property
219
+ def vtype(self, /) -> Vtype:
220
+ """Get the vtype of the variable."""
221
+ ...
207
222
 
208
223
  @overload
209
224
  def __add__(self, other: int, /) -> Expression: ...
@@ -213,7 +228,7 @@ class Variable:
213
228
  def __add__(self, other: Variable, /) -> Expression: ...
214
229
  @overload
215
230
  def __add__(self, other: Expression, /) -> Expression: ...
216
- def __add__(self, other: float | Variable | Expression, /) -> Expression:
231
+ def __add__(self, other: (int | float | Variable | Expression), /) -> Expression:
217
232
  """
218
233
  Add this variable to another value.
219
234
 
@@ -233,6 +248,7 @@ class Variable:
233
248
  TypeError
234
249
  If the operand type is unsupported.
235
250
  """
251
+ ...
236
252
 
237
253
  @overload
238
254
  def __radd__(self, other: int, /) -> Expression: ...
@@ -242,7 +258,7 @@ class Variable:
242
258
  def __radd__(self, other: Variable, /) -> Expression: ...
243
259
  @overload
244
260
  def __radd__(self, other: Expression, /) -> Expression: ...
245
- def __radd__(self, other: float | Variable | Expression, /) -> Expression:
261
+ def __radd__(self, other: (int | float | Variable | Expression), /) -> Expression:
246
262
  """
247
263
  Right-hand addition.
248
264
 
@@ -260,6 +276,7 @@ class Variable:
260
276
  TypeError
261
277
  If the operand type is unsupported.
262
278
  """
279
+ ...
263
280
 
264
281
  @overload
265
282
  def __sub__(self, other: int, /) -> Expression: ...
@@ -269,7 +286,7 @@ class Variable:
269
286
  def __sub__(self, other: Variable, /) -> Expression: ...
270
287
  @overload
271
288
  def __sub__(self, other: Expression, /) -> Expression: ...
272
- def __sub__(self, other: float | Variable | Expression, /) -> Expression:
289
+ def __sub__(self, other: (int | float | Variable | Expression), /) -> Expression:
273
290
  """
274
291
  Subtract a value from this variable.
275
292
 
@@ -289,12 +306,13 @@ class Variable:
289
306
  TypeError
290
307
  If the operand type is unsupported.
291
308
  """
309
+ ...
292
310
 
293
311
  @overload
294
312
  def __rsub__(self, other: int, /) -> Expression: ...
295
313
  @overload
296
314
  def __rsub__(self, other: float, /) -> Expression: ...
297
- def __rsub__(self, other: float, /) -> Expression:
315
+ def __rsub__(self, other: (int | float), /) -> Expression:
298
316
  """
299
317
  Subtract this variable from a scalar (right-hand subtraction).
300
318
 
@@ -312,6 +330,7 @@ class Variable:
312
330
  TypeError
313
331
  If `other` is not a scalar.
314
332
  """
333
+ ...
315
334
 
316
335
  @overload
317
336
  def __mul__(self, other: int, /) -> Expression: ...
@@ -321,7 +340,7 @@ class Variable:
321
340
  def __mul__(self, other: Variable, /) -> Expression: ...
322
341
  @overload
323
342
  def __mul__(self, other: Expression, /) -> Expression: ...
324
- def __mul__(self, other: float | Variable | Expression, /) -> Expression:
343
+ def __mul__(self, other: (int | float | Variable | Expression), /) -> Expression:
325
344
  """
326
345
  Multiply this variable by another value.
327
346
 
@@ -341,6 +360,7 @@ class Variable:
341
360
  TypeError
342
361
  If the operand type is unsupported.
343
362
  """
363
+ ...
344
364
 
345
365
  @overload
346
366
  def __rmul__(self, other: int, /) -> Expression: ...
@@ -350,7 +370,7 @@ class Variable:
350
370
  def __rmul__(self, other: Variable, /) -> Expression: ...
351
371
  @overload
352
372
  def __rmul__(self, other: Expression, /) -> Expression: ...
353
- def __rmul__(self, other: float | Variable | Expression, /) -> Expression:
373
+ def __rmul__(self, other: (int | float | Variable | Expression), /) -> Expression:
354
374
  """
355
375
  Right-hand multiplication for scalars.
356
376
 
@@ -368,6 +388,7 @@ class Variable:
368
388
  TypeError
369
389
  If the operand type is unsupported.
370
390
  """
391
+ ...
371
392
 
372
393
  def __pow__(self, other: int, /) -> Expression:
373
394
  """
@@ -386,6 +407,7 @@ class Variable:
386
407
  RuntimeError
387
408
  If the param `modulo` usually supported for `__pow__` is specified.
388
409
  """
410
+ ...
389
411
 
390
412
  @overload
391
413
  def __eq__(self, rhs: int, /) -> Constraint: ...
@@ -407,7 +429,7 @@ class Variable:
407
429
  bool
408
430
  """
409
431
 
410
- def __eq__(self, rhs: float | Expression, /) -> Constraint: # type: ignore
432
+ def __eq__(self, rhs: (int | float | Expression), /) -> Constraint:
411
433
  """
412
434
  Create a constraint: Variable == float | int | Expression.
413
435
 
@@ -438,7 +460,7 @@ class Variable:
438
460
  def __le__(self, rhs: Variable, /) -> Constraint: ...
439
461
  @overload
440
462
  def __le__(self, rhs: Expression, /) -> Constraint: ...
441
- def __le__(self, rhs: float | Variable | Expression, /) -> Constraint: # type: ignore
463
+ def __le__(self, rhs: (int | float | Variable | Expression), /) -> Constraint:
442
464
  """
443
465
  Create a constraint: Variable <= scalar.
444
466
 
@@ -460,6 +482,7 @@ class Variable:
460
482
  TypeError
461
483
  If the right-hand side is not of type float, int, Variable or Expression.
462
484
  """
485
+ ...
463
486
 
464
487
  @overload
465
488
  def __ge__(self, rhs: int, /) -> Constraint: ...
@@ -469,7 +492,7 @@ class Variable:
469
492
  def __ge__(self, rhs: Variable, /) -> Constraint: ...
470
493
  @overload
471
494
  def __ge__(self, rhs: Expression, /) -> Constraint: ...
472
- def __ge__(self, rhs: float | Variable | Expression, /) -> Constraint:
495
+ def __ge__(self, rhs: (int | float | Variable | Expression), /) -> Constraint:
473
496
  """
474
497
  Create a constraint: Variable >= scalar.
475
498
 
@@ -491,6 +514,7 @@ class Variable:
491
514
  TypeError
492
515
  If the right-hand side is not of type float, int, Variable or Expression.
493
516
  """
517
+ ...
494
518
 
495
519
  def __neg__(self, /) -> Expression:
496
520
  """
@@ -500,10 +524,122 @@ class Variable:
500
524
  -------
501
525
  Expression
502
526
  """
527
+ ...
528
+
529
+ @property
530
+ def _environment(self, /) -> Environment:
531
+ """Get this variables's environment."""
532
+ ...
503
533
 
504
534
  def __hash__(self, /) -> int: ...
535
+ def __str__(self, /) -> str: ...
536
+ def __repr__(self, /) -> str: ...
537
+
538
+ class Constant:
539
+ """A constant expression.
540
+
541
+ Convenience class to indicate the empty set of variables of an expression's
542
+ constant term when iterating over the expression's components.
543
+
544
+ Note that the bias corresponding to the constant part is not part of this class.
545
+
546
+ Examples
547
+ --------
548
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
549
+ >>> expr: Expression = ...
550
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
551
+ >>> bias: float
552
+ >>> for vars, bias in expr.items():
553
+ >>> match vars:
554
+ >>> case Constant(): do_something_with_constant(bias)
555
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
556
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
557
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
558
+ """
559
+
560
+ class Linear:
561
+ """A linear expression.
562
+
563
+ Convenience class to indicate the variable of an expression's linear term when
564
+ iterating over the expression's components.
565
+
566
+ Note that the bias corresponding to this variable is not part of this class.
567
+
568
+ Examples
569
+ --------
570
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
571
+ >>> expr: Expression = ...
572
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
573
+ >>> bias: float
574
+ >>> for vars, bias in expr.items():
575
+ >>> match vars:
576
+ >>> case Constant(): do_something_with_constant(bias)
577
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
578
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
579
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
580
+ """
581
+
582
+ __match_args__ = ("var",)
583
+
584
+ @property
585
+ def var(self) -> Variable: ...
586
+
587
+ class Quadratic:
588
+ """A quadratic expression.
589
+
590
+ Convenience class to indicate the variables of an expression's quadratic term when
591
+ iterating over the expression's components.
592
+
593
+ Note that the bias corresponding to these two variables is not part of this class.
594
+
595
+ Examples
596
+ --------
597
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
598
+ >>> expr: Expression = ...
599
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
600
+ >>> bias: float
601
+ >>> for vars, bias in expr.items():
602
+ >>> match vars:
603
+ >>> case Constant(): do_something_with_constant(bias)
604
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
605
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
606
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
607
+ """
608
+
609
+ __match_args__ = "var_a", "var_b"
610
+
611
+ @property
612
+ def var_a(self) -> Variable: ...
613
+ @property
614
+ def var_b(self) -> Variable: ...
615
+
616
+ class HigherOrder:
617
+ """A higher-order expression.
618
+
619
+ Convenience class to indicate the set of variables of an expression's higher-order
620
+ term when iterating over the expression's components.
621
+
622
+ Note that the bias corresponding to these variables is not part of this class.
623
+
624
+ Examples
625
+ --------
626
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
627
+ >>> expr: Expression = ...
628
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
629
+ >>> bias: float
630
+ >>> for vars, bias in expr.items():
631
+ >>> match vars:
632
+ >>> case Constant(): do_something_with_constant(bias)
633
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
634
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
635
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
636
+ """
637
+
638
+ __match_args__ = ("vars",)
639
+
640
+ @property
641
+ def vars(self) -> list[Variable]: ...
505
642
 
506
- # _timing.pyi
507
643
  class Timing:
508
644
  """
509
645
  The object that holds information about an algorithm's runtime.
@@ -532,10 +668,12 @@ class Timing:
532
668
  @property
533
669
  def start(self, /) -> datetime:
534
670
  """The starting time of the algorithm."""
671
+ ...
535
672
 
536
673
  @property
537
674
  def end(self, /) -> datetime:
538
675
  """The end, or finishing, time of the algorithm."""
676
+ ...
539
677
 
540
678
  @property
541
679
  def total(self, /) -> timedelta:
@@ -547,25 +685,30 @@ class Timing:
547
685
  RuntimeError
548
686
  If total cannot be computed due to an inconsistent start or end time.
549
687
  """
688
+ ...
550
689
 
551
690
  @property
552
691
  def total_seconds(self, /) -> float:
553
692
  """
554
- The total time in seconds an algorithm needed to run. Computed as the
555
- difference of end and start time.
693
+ The total time in seconds an algorithm needed to run.
694
+
695
+ Computed as the difference of end and start time.
556
696
 
557
697
  Raises
558
698
  ------
559
699
  RuntimeError
560
- If total_seconds cannot be computed due to an inconsistent start or end time.
700
+ If `total_seconds` cannot be computed due to an inconsistent start or
701
+ end time.
561
702
  """
703
+ ...
562
704
 
563
705
  @property
564
706
  def qpu(self, /) -> float | None:
565
707
  """The qpu usage time of the algorithm this timing object was created for."""
708
+ ...
566
709
 
567
710
  @qpu.setter
568
- def qpu(self, /, value: float | None):
711
+ def qpu(self, /, value: (float | None)) -> None:
569
712
  """
570
713
  Set the qpu usage time.
571
714
 
@@ -574,11 +717,13 @@ class Timing:
574
717
  ValueError
575
718
  If `value` is negative.
576
719
  """
720
+ ...
577
721
 
578
- def add_qpu(self, /, value: float):
722
+ def add_qpu(self, /, value: float) -> None:
579
723
  """
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.
724
+ Add qpu usage time to the qpu usage time already present.
725
+
726
+ If the current value is None, this method acts like a setter.
582
727
 
583
728
  Parameters
584
729
  ----------
@@ -590,6 +735,7 @@ class Timing:
590
735
  ValueError
591
736
  If `value` is negative.
592
737
  """
738
+ ...
593
739
 
594
740
  class Timer:
595
741
  """
@@ -618,6 +764,7 @@ class Timer:
618
764
  Timer
619
765
  The timer.
620
766
  """
767
+ ...
621
768
 
622
769
  def stop(self, /) -> Timing:
623
770
  """
@@ -628,8 +775,8 @@ class Timer:
628
775
  Timing
629
776
  The timing object that holds the start and end time.
630
777
  """
778
+ ...
631
779
 
632
- # _solution.pyi
633
780
  class Solution:
634
781
  """
635
782
  The solution object that is obtained by running an algorihtm.
@@ -639,9 +786,10 @@ class Solution:
639
786
  returned by the algorithm, metadata about the solution quality, e.g., the objective
640
787
  value, and the runtime of the algorithm.
641
788
 
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.
789
+ A `Solution` can be constructed explicitly using `from_dict` or by obtaining a
790
+ solution from an algorithm or by converting a different solution format with one of
791
+ the available translators. Note that the latter requires the environment the model
792
+ was created in.
645
793
 
646
794
  Examples
647
795
  --------
@@ -675,10 +823,13 @@ class Solution:
675
823
 
676
824
  Notes
677
825
  -----
678
- - To ensure metadata like objective values or feasibility, use `model.evaluate(solution)`.
826
+ - To ensure metadata like objective values or feasibility, use
827
+ `model.evaluate(solution)`.
679
828
  - Use `encode()` and `decode()` to serialize and recover solutions.
680
829
  """
681
830
 
831
+ def __str__(self, /) -> str: ...
832
+ def __repr__(self, /) -> str: ...
682
833
  def __len__(self, /) -> int: ...
683
834
  def __iter__(self, /) -> ResultIterator:
684
835
  """
@@ -695,6 +846,7 @@ class Solution:
695
846
  IndexError
696
847
  If the row index is out of bounds for the variable environment.
697
848
  """
849
+ ...
698
850
 
699
851
  def __getitem__(self, item: int, /) -> ResultView:
700
852
  """
@@ -711,8 +863,9 @@ class Solution:
711
863
  IndexError
712
864
  If the row index is out of bounds for the variable environment.
713
865
  """
866
+ ...
714
867
 
715
- def __eq__(self, other: Solution, /) -> bool: # type: ignore
868
+ def __eq__(self, other: Solution, /) -> bool:
716
869
  """
717
870
  Check whether this solution is equal to `other`.
718
871
 
@@ -724,6 +877,7 @@ class Solution:
724
877
  -------
725
878
  bool
726
879
  """
880
+ ...
727
881
 
728
882
  def best(self, /) -> ResultView | None:
729
883
  """
@@ -737,44 +891,60 @@ class Solution:
737
891
  ResultView
738
892
  The best result of the solution as a view.
739
893
  """
894
+ ...
740
895
 
741
896
  @property
742
897
  def results(self, /) -> ResultIterator:
743
898
  """Get an iterator over the single results of the solution."""
899
+ ...
744
900
 
745
901
  @property
746
902
  def samples(self, /) -> Samples:
747
903
  """Get a view into the samples of the solution."""
904
+ ...
748
905
 
749
906
  @property
750
907
  def obj_values(self, /) -> NDArray:
751
908
  """
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.
909
+ Get the objective values of the single samples as a ndarray.
910
+
911
+ A value will be None if the sample hasn't yet been evaluated.
754
912
  """
913
+ ...
755
914
 
756
915
  @property
757
916
  def raw_energies(self, /) -> NDArray:
758
- """
917
+ """Get the raw energies.
918
+
759
919
  Get the raw energy values of the single samples as returned by the solver /
760
920
  algorithm. Will be None if the solver / algorithm did not provide a value.
761
921
  """
922
+ ...
762
923
 
763
924
  @property
764
925
  def counts(self, /) -> NDArray:
765
926
  """Return how often each sample occurred in the solution."""
927
+ ...
766
928
 
767
929
  @property
768
930
  def runtime(self, /) -> Timing | None:
769
931
  """Get the solver / algorithm runtime."""
932
+ ...
933
+
934
+ @property
935
+ def sense(self, /) -> Sense:
936
+ """Get the optimization sense."""
937
+ ...
770
938
 
771
939
  @property
772
940
  def best_sample_idx(self, /) -> int | None:
773
941
  """Get the index of the sample with the best objective value."""
942
+ ...
774
943
 
775
944
  @property
776
945
  def variable_names(self, /) -> list[str]:
777
946
  """Get the names of all variables in the solution."""
947
+ ...
778
948
 
779
949
  def expectation_value(self, /) -> float:
780
950
  """
@@ -790,6 +960,55 @@ class Solution:
790
960
  ComputationError
791
961
  If the computation fails for any reason.
792
962
  """
963
+ ...
964
+
965
+ def feasibility_ratio(self, /) -> float:
966
+ """
967
+ Compute the feasibility ratio of the solution.
968
+
969
+ Returns
970
+ -------
971
+ float
972
+ The feasibility ratio.
973
+
974
+ Raises
975
+ ------
976
+ ComputationError
977
+ If the computation fails for any reason.
978
+ """
979
+ ...
980
+
981
+ def filter_feasible(self, /) -> Solution:
982
+ """
983
+ Get a new solution with all infeasible samples removed.
984
+
985
+ Returns
986
+ -------
987
+ The new solution with only feasible samples.
988
+
989
+ Raises
990
+ ------
991
+ ComputationError
992
+ If the computation fails for any reason.
993
+ """
994
+ ...
995
+
996
+ def highest_constraint_violation(self, /) -> int | None:
997
+ """
998
+ Get the index of the constraint with the highest number of violations.
999
+
1000
+ Returns
1001
+ -------
1002
+ int | None
1003
+ The index of the constraint with the most violations. None, if the solution
1004
+ was created for an unconstrained model.
1005
+
1006
+ Raises
1007
+ ------
1008
+ ComputationError
1009
+ If the computation fails for any reason.
1010
+ """
1011
+ ...
793
1012
 
794
1013
  @overload
795
1014
  def encode(self, /) -> bytes: ...
@@ -798,7 +1017,8 @@ class Solution:
798
1017
  @overload
799
1018
  def encode(self, /, *, level: int) -> bytes: ...
800
1019
  @overload
801
- def encode(self, /, *, compress: bool, level: int) -> bytes:
1020
+ def encode(self, /, *, compress: bool, level: int) -> bytes: ...
1021
+ def encode(self, /, *, compress: bool = True, level: int = 3) -> bytes:
802
1022
  """
803
1023
  Serialize the solution into a compact binary format.
804
1024
 
@@ -807,7 +1027,7 @@ class Solution:
807
1027
  compress : bool, optional
808
1028
  Whether to compress the binary output. Default is True.
809
1029
  level : int, optional
810
- Compression level (09). Default is 3.
1030
+ Compression level (0-9). Default is 3.
811
1031
 
812
1032
  Returns
813
1033
  -------
@@ -819,6 +1039,7 @@ class Solution:
819
1039
  IOError
820
1040
  If serialization fails.
821
1041
  """
1042
+ ...
822
1043
 
823
1044
  @overload
824
1045
  def serialize(self, /) -> bytes: ...
@@ -829,13 +1050,14 @@ class Solution:
829
1050
  @overload
830
1051
  def serialize(self, /, compress: bool, level: int) -> bytes: ...
831
1052
  def serialize(
832
- self, /, compress: bool | None = ..., level: int | None = ...
1053
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
833
1054
  ) -> bytes:
834
1055
  """
835
1056
  Alias for `encode()`.
836
1057
 
837
1058
  See `encode()` for details.
838
1059
  """
1060
+ ...
839
1061
 
840
1062
  @classmethod
841
1063
  def decode(cls, data: bytes) -> Solution:
@@ -857,184 +1079,124 @@ class Solution:
857
1079
  DecodeError
858
1080
  If decoding fails due to corruption or incompatibility.
859
1081
  """
1082
+ ...
860
1083
 
861
1084
  @classmethod
862
1085
  def deserialize(cls, data: bytes) -> Solution:
863
1086
  """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
- """
1087
+ ...
979
1088
 
980
1089
  @overload
981
1090
  @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: ...
1091
+ def from_dict(
1092
+ data: dict[Variable, int],
1093
+ *,
1094
+ env: Environment = ...,
1095
+ model: Model = ...,
1096
+ timing: Timing = ...,
1097
+ counts: int = ...,
1098
+ sense: Sense = ...,
1099
+ ) -> Solution: ...
1001
1100
  @overload
1002
1101
  @staticmethod
1003
- def from_dict(data: dict[str, int], *, env: Environment) -> Solution: ...
1102
+ def from_dict(
1103
+ data: dict[Variable, float],
1104
+ *,
1105
+ env: Environment = ...,
1106
+ model: Model = ...,
1107
+ timing: Timing = ...,
1108
+ counts: int = ...,
1109
+ sense: Sense = ...,
1110
+ ) -> Solution: ...
1004
1111
  @overload
1005
1112
  @staticmethod
1006
- def from_dict(data: dict[str, float], *, env: Environment) -> Solution: ...
1113
+ def from_dict(
1114
+ data: dict[str, int],
1115
+ *,
1116
+ env: Environment = ...,
1117
+ model: Model = ...,
1118
+ timing: Timing = ...,
1119
+ counts: int = ...,
1120
+ sense: Sense = ...,
1121
+ ) -> Solution: ...
1007
1122
  @overload
1008
1123
  @staticmethod
1009
1124
  def from_dict(
1010
- data: dict[Variable | str, int | float], *, env: Environment
1125
+ data: dict[str, float],
1126
+ *,
1127
+ env: Environment = ...,
1128
+ model: Model = ...,
1129
+ timing: Timing = ...,
1130
+ counts: int = ...,
1131
+ sense: Sense = ...,
1011
1132
  ) -> Solution: ...
1012
1133
  @overload
1013
1134
  @staticmethod
1014
- def from_dict(data: dict[Variable, int], *, model: Model) -> Solution: ...
1135
+ def from_dict(
1136
+ data: dict[Variable | str, int],
1137
+ *,
1138
+ env: Environment = ...,
1139
+ model: Model = ...,
1140
+ timing: Timing = ...,
1141
+ counts: int = ...,
1142
+ sense: Sense = ...,
1143
+ ) -> Solution: ...
1015
1144
  @overload
1016
1145
  @staticmethod
1017
- def from_dict(data: dict[Variable, float], *, model: Model) -> Solution: ...
1146
+ def from_dict(
1147
+ data: dict[Variable | str, float],
1148
+ *,
1149
+ env: Environment = ...,
1150
+ model: Model = ...,
1151
+ timing: Timing = ...,
1152
+ counts: int = ...,
1153
+ sense: Sense = ...,
1154
+ ) -> Solution: ...
1018
1155
  @overload
1019
1156
  @staticmethod
1020
- def from_dict(data: dict[str, int], *, model: Model) -> Solution: ...
1157
+ def from_dict(
1158
+ data: dict[Variable, int | float],
1159
+ *,
1160
+ env: Environment = ...,
1161
+ model: Model = ...,
1162
+ timing: Timing = ...,
1163
+ counts: int = ...,
1164
+ sense: Sense = ...,
1165
+ ) -> Solution: ...
1021
1166
  @overload
1022
1167
  @staticmethod
1023
- def from_dict(data: dict[str, float], *, model: Model) -> Solution: ...
1168
+ def from_dict(
1169
+ data: dict[str, int | float],
1170
+ *,
1171
+ env: Environment = ...,
1172
+ model: Model = ...,
1173
+ timing: Timing = ...,
1174
+ counts: int = ...,
1175
+ sense: Sense = ...,
1176
+ ) -> Solution: ...
1024
1177
  @overload
1025
1178
  @staticmethod
1026
1179
  def from_dict(
1027
- data: dict[Variable | str, int | float], *, model: Model
1180
+ data: dict[Variable | str, int | float],
1181
+ *,
1182
+ env: Environment = ...,
1183
+ model: Model = ...,
1184
+ timing: Timing = ...,
1185
+ counts: int = ...,
1186
+ sense: Sense = ...,
1028
1187
  ) -> Solution: ...
1029
1188
  @staticmethod
1030
1189
  def from_dict(
1031
1190
  data: dict[Variable | str, int | float],
1032
1191
  *,
1033
- env: Environment | None = ...,
1034
- model: Model | None = ...,
1035
- timing: Timing | None = ...,
1192
+ env: (Environment | None) = ...,
1193
+ model: (Model | None) = ...,
1194
+ timing: (Timing | None) = ...,
1195
+ counts: (int | None) = ...,
1196
+ sense: (Sense | None) = ...,
1036
1197
  ) -> Solution:
1037
- """
1198
+ """Create a `Solution` from a dict.
1199
+
1038
1200
  Create a `Solution` from a dict that maps variables or variable names to their
1039
1201
  assigned values.
1040
1202
 
@@ -1049,6 +1211,8 @@ class Solution:
1049
1211
  The environment the variable types shall be determined from.
1050
1212
  model : Model, optional
1051
1213
  A model to evaluate the sample with.
1214
+ counts : int, optional
1215
+ The number of occurrences of this sample.
1052
1216
 
1053
1217
  Returns
1054
1218
  -------
@@ -1063,6 +1227,7 @@ class Solution:
1063
1227
  ValueError
1064
1228
  If `env` and `model` are both present. When this is the case, the user's
1065
1229
  intention is unclear as the model itself already contains an environment.
1230
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1066
1231
  SolutionTranslationError
1067
1232
  Generally if the sample translation fails. Might be specified by one of the
1068
1233
  three following errors.
@@ -1074,72 +1239,122 @@ class Solution:
1074
1239
  If the result's variable types are incompatible with the model environment's
1075
1240
  variable types.
1076
1241
  """
1242
+ ...
1077
1243
 
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
1244
  @overload
1094
1245
  @staticmethod
1095
1246
  def from_dicts(
1096
- data: list[dict[Variable, int]], *, env: Environment
1247
+ data: list[dict[Variable, int]],
1248
+ *,
1249
+ env: Environment = ...,
1250
+ model: Model = ...,
1251
+ timing: Timing = ...,
1252
+ counts: list[int] = ...,
1253
+ sense: Sense = ...,
1097
1254
  ) -> Solution: ...
1098
1255
  @overload
1099
1256
  @staticmethod
1100
1257
  def from_dicts(
1101
- data: list[dict[Variable, float]], *, env: Environment
1258
+ data: list[dict[Variable, float]],
1259
+ *,
1260
+ env: Environment = ...,
1261
+ model: Model = ...,
1262
+ timing: Timing = ...,
1263
+ counts: list[int] = ...,
1264
+ sense: Sense = ...,
1102
1265
  ) -> Solution: ...
1103
1266
  @overload
1104
1267
  @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: ...
1268
+ def from_dicts(
1269
+ data: list[dict[str, int]],
1270
+ *,
1271
+ env: Environment = ...,
1272
+ model: Model = ...,
1273
+ timing: Timing = ...,
1274
+ counts: list[int] = ...,
1275
+ sense: Sense = ...,
1276
+ ) -> Solution: ...
1109
1277
  @overload
1110
1278
  @staticmethod
1111
1279
  def from_dicts(
1112
- data: list[dict[Variable | str, int | float]], *, env: Environment
1280
+ data: list[dict[str, float]],
1281
+ *,
1282
+ env: Environment = ...,
1283
+ model: Model = ...,
1284
+ timing: Timing = ...,
1285
+ counts: list[int] = ...,
1286
+ sense: Sense = ...,
1113
1287
  ) -> Solution: ...
1114
1288
  @overload
1115
1289
  @staticmethod
1116
- def from_dicts(data: list[dict[Variable, int]], *, model: Model) -> Solution: ...
1290
+ def from_dicts(
1291
+ data: list[dict[Variable | str, int]],
1292
+ *,
1293
+ env: Environment = ...,
1294
+ model: Model = ...,
1295
+ timing: Timing = ...,
1296
+ counts: list[int] = ...,
1297
+ sense: Sense = ...,
1298
+ ) -> Solution: ...
1117
1299
  @overload
1118
1300
  @staticmethod
1119
- def from_dicts(data: list[dict[Variable, float]], *, model: Model) -> Solution: ...
1301
+ def from_dicts(
1302
+ data: list[dict[Variable | str, float]],
1303
+ *,
1304
+ env: Environment = ...,
1305
+ model: Model = ...,
1306
+ timing: Timing = ...,
1307
+ counts: list[int] = ...,
1308
+ sense: Sense = ...,
1309
+ ) -> Solution: ...
1120
1310
  @overload
1121
1311
  @staticmethod
1122
- def from_dicts(data: list[dict[str, int]], *, model: Model) -> Solution: ...
1312
+ def from_dicts(
1313
+ data: list[dict[Variable, int | float]],
1314
+ *,
1315
+ env: Environment = ...,
1316
+ model: Model = ...,
1317
+ timing: Timing = ...,
1318
+ counts: list[int] = ...,
1319
+ sense: Sense = ...,
1320
+ ) -> Solution: ...
1123
1321
  @overload
1124
1322
  @staticmethod
1125
- def from_dicts(data: list[dict[str, float]], *, model: Model) -> Solution: ...
1323
+ def from_dicts(
1324
+ data: list[dict[str, int | float]],
1325
+ *,
1326
+ env: Environment = ...,
1327
+ model: Model = ...,
1328
+ timing: Timing = ...,
1329
+ counts: list[int] = ...,
1330
+ sense: Sense = ...,
1331
+ ) -> Solution: ...
1126
1332
  @overload
1127
1333
  @staticmethod
1128
1334
  def from_dicts(
1129
- data: list[dict[Variable | str, int | float]], *, model: Model
1335
+ data: list[dict[Variable | str, int | float]],
1336
+ *,
1337
+ env: Environment = ...,
1338
+ model: Model = ...,
1339
+ timing: Timing = ...,
1340
+ counts: list[int] = ...,
1341
+ sense: Sense = ...,
1130
1342
  ) -> Solution: ...
1131
1343
  @staticmethod
1132
1344
  def from_dicts(
1133
1345
  data: list[dict[Variable | str, int | float]],
1134
1346
  *,
1135
- env: Environment | None = ...,
1136
- model: Model | None = ...,
1137
- timing: Timing | None = ...,
1347
+ env: (Environment | None) = ...,
1348
+ model: (Model | None) = ...,
1349
+ timing: (Timing | None) = ...,
1350
+ counts: (list[int] | None) = ...,
1351
+ sense: (Sense | None) = ...,
1138
1352
  ) -> 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.
1353
+ """Create a `Solution` from multiple dicts.
1354
+
1355
+ Create a `Solution` from multiple dicts that map variables or variable names to
1356
+ their assigned values. Duplicate samples contained in the `data` list are
1357
+ aggregated to a single sample.
1143
1358
 
1144
1359
  If a Model is passed, the solution will be evaluated immediately. Otherwise,
1145
1360
  there has to be an environment present to determine the correct variable types.
@@ -1152,6 +1367,10 @@ class Solution:
1152
1367
  The environment the variable types shall be determined from.
1153
1368
  model : Model, optional
1154
1369
  A model to evaluate the sample with.
1370
+ counts : int, optional
1371
+ The number of occurrences for each sample.
1372
+ sense: Sense, optional
1373
+ The sense of the optimization problem.
1155
1374
 
1156
1375
  Returns
1157
1376
  -------
@@ -1166,6 +1385,8 @@ class Solution:
1166
1385
  ValueError
1167
1386
  If `env` and `model` are both present. When this is the case, the user's
1168
1387
  intention is unclear as the model itself already contains an environment.
1388
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1389
+ Or if the the number of samples and the number of counts do not match.
1169
1390
  SolutionTranslationError
1170
1391
  Generally if the sample translation fails. Might be specified by one of the
1171
1392
  three following errors.
@@ -1177,8 +1398,130 @@ class Solution:
1177
1398
  If the result's variable types are incompatible with the model environment's
1178
1399
  variable types.
1179
1400
  """
1401
+ ...
1402
+
1403
+ @staticmethod
1404
+ def from_counts(
1405
+ data: dict[str, int],
1406
+ *,
1407
+ env: (Environment | None) = ...,
1408
+ model: (Model | None) = ...,
1409
+ timing: (Timing | None) = ...,
1410
+ sense: (Sense | None) = ...,
1411
+ bit_order: Literal["LTR", "RTL"] = "RTL",
1412
+ ) -> Solution:
1413
+ """
1414
+ Create a `Solution` from a dict that maps measured bitstrings to counts.
1415
+
1416
+ If a Model is passed, the solution will be evaluated immediately. Otherwise,
1417
+ there has to be an environment present to determine the correct variable types.
1418
+ Only applicable to binary or spin models.
1419
+
1420
+ Parameters
1421
+ ----------
1422
+ data : dict[str, int]
1423
+ The counts that shall be part of the solution.
1424
+ env : Environment, optional
1425
+ The environment the variable types shall be determined from.
1426
+ model : Model, optional
1427
+ A model to evaluate the sample with.
1428
+ timing : Timing, optional
1429
+ The timing for acquiring the solution.
1430
+ sense : Sense, optional
1431
+ The sense the model the solution belongs to. Default: Sense.Min
1432
+ bit_order : Literal["LTR", "RTL"]
1433
+ The order of the bits in the bitstring. Default "RTL".
1434
+
1435
+ Returns
1436
+ -------
1437
+ Solution
1438
+ The solution object created from the sample dict.
1439
+
1440
+ Raises
1441
+ ------
1442
+ NoActiveEnvironmentFoundError
1443
+ If no environment or model is passed to the method or available from the
1444
+ context.
1445
+ ValueError
1446
+ If `env` and `model` are both present. When this is the case, the user's
1447
+ intention is unclear as the model itself already contains an environment.
1448
+ Or if `sense` and `model` are both present as the sense is then ambiguous.
1449
+ Or if the the environment contains non-(binary or spin) variables.
1450
+ Or if a bitstring contains chars other than '0' and '1'.
1451
+ SolutionTranslationError
1452
+ Generally if the sample translation fails. Might be specified by one of the
1453
+ three following errors.
1454
+ SampleIncorrectLengthErr
1455
+ If a sample has a different number of variables than the environment.
1456
+ """
1457
+ ...
1458
+
1459
+ def print(
1460
+ self,
1461
+ /,
1462
+ layout: Literal["row", "column"] = "column",
1463
+ max_line_length: int = 80,
1464
+ max_column_length: int = 5,
1465
+ max_lines: int = 10,
1466
+ max_var_name_length: int = 10,
1467
+ show_metadata: Literal["before", "after", "hide"] = "after",
1468
+ ) -> None:
1469
+ """
1470
+ Show a solution object as a human-readable string.
1471
+
1472
+ This method provides various ways to customize the way the solution is
1473
+ represented as a string.
1474
+
1475
+ Parameters
1476
+ ----------
1477
+ layout : Literal["row", "column"]
1478
+ With `"row"` layout, all assignments to one variable across different
1479
+ samples are shown in the same *row*, and each sample is shown in one
1480
+ column.
1481
+ With `"column"` layout, all assignments to one variable across different
1482
+ samples are shown in the same *column*, and each sample is shown in one row.
1483
+ max_line_length : int
1484
+ The max number of chars shown in one line or, in other words, the max width
1485
+ of a row.
1486
+ max_column_length : int
1487
+ The maximal number of chars in one column. For both the row and column
1488
+ layout, this controls the max number of chars a single variable assignment
1489
+ may be shown with. For the column layout, this also controls the max number
1490
+ of chars that a variable name is shown with.
1491
+ Note: the max column length cannot always be adhered to. This is
1492
+ specifically the case when a variable assignment is so high that the max
1493
+ column length is not sufficient to show the number correctly.
1494
+ max_lines : int
1495
+ The max number of lines used for showing the samples. Note that this
1496
+ parameter does not influence how metadata are shown, s.t. the total number
1497
+ of lines may be higher than `max_lines`.
1498
+ max_var_name_length : int
1499
+ The max number of chars that a variable is shown with in row layout. This
1500
+ parameter is ignored in column layout.
1501
+ show_metadata : Literal["before", "after", "hide"]
1502
+ Whether and where to show sample-specific metadata such as feasibility and
1503
+ objective value. Note that this parameter only controls how sample-specific
1504
+ metadata are shown. Other metadata, like the solution timing will be shown
1505
+ after the samples regardless of the value of this parameter.
1506
+
1507
+ - `"before"`: show metadata before the actual sample, i.e., above the
1508
+ sample in row layout, and left of the sample in column layout.
1509
+ - `"after"`: show metadata after the actual sample, i.e., below the
1510
+ sample in row layout, and right of the sample in column layout.
1511
+ - "hide": do not show sample-specific metadata.
1512
+
1513
+ Returns
1514
+ -------
1515
+ str
1516
+ The solution represented as a string.
1517
+
1518
+ Raises
1519
+ ------
1520
+ ValueError
1521
+ If at least one of the params has an invalid value.
1522
+ """
1523
+ ...
1180
1524
 
1181
- # _sample.pyi
1182
1525
  class SamplesIterator:
1183
1526
  """
1184
1527
  An iterator over a solution's samples.
@@ -1222,12 +1565,11 @@ class SampleIterator:
1222
1565
  def __next__(self, /) -> int | float: ...
1223
1566
 
1224
1567
  class Samples:
1225
- """
1226
- A samples object is simply a set-like object that contains every different sample
1227
- of a solution.
1568
+ """A set-like object containing every different sample of a solution.
1228
1569
 
1229
- The ``Samples`` class is readonly as it's merely a helper class for looking into a
1230
- solution's different samples.
1570
+ A samples object is simply a set-like object that contains every different sample
1571
+ of a solution. The ``Samples`` class is readonly as it's merely a helper class for
1572
+ looking into a solution's different samples.
1231
1573
 
1232
1574
  Examples
1233
1575
  --------
@@ -1240,12 +1582,14 @@ class Samples:
1240
1582
  [1, -4, -0.42]
1241
1583
  """
1242
1584
 
1585
+ def __str__(self, /) -> str: ...
1243
1586
  @overload
1244
1587
  def __getitem__(self, item: int, /) -> Sample: ...
1245
1588
  @overload
1246
- def __getitem__(self, item: tuple[int, int], /) -> int | float:
1247
- """
1248
- Extract a sample or variable assignment from the ``Samples`` object.
1589
+ def __getitem__(self, item: tuple[int, int], /) -> int | float: ...
1590
+ def __getitem__(self, item: (int | tuple[int, int]), /) -> int | float:
1591
+ """Extract a sample or variable assignment from the ``Samples`` object.
1592
+
1249
1593
  If ``item`` is an int, returns the sample in this row. If ``item`` is a tuple
1250
1594
  of ints `(i, j)`, returns the variable assignment in row `i` and column `j`.
1251
1595
 
@@ -1260,6 +1604,7 @@ class Samples:
1260
1604
  IndexError
1261
1605
  If the row or column index is out of bounds for the variable environment.
1262
1606
  """
1607
+ ...
1263
1608
 
1264
1609
  def __len__(self, /) -> int:
1265
1610
  """
@@ -1269,6 +1614,7 @@ class Samples:
1269
1614
  -------
1270
1615
  int
1271
1616
  """
1617
+ ...
1272
1618
 
1273
1619
  def __iter__(self, /) -> SamplesIterator:
1274
1620
  """
@@ -1278,9 +1624,11 @@ class Samples:
1278
1624
  -------
1279
1625
  SamplesIterator
1280
1626
  """
1627
+ ...
1281
1628
 
1282
1629
  def tolist(self, /) -> list[list[int | float]]:
1283
- """
1630
+ """Convert sample into a 2-dimensional list.
1631
+
1284
1632
  Convert the sample into a 2-dimensional list where a row constitutes a single
1285
1633
  sample, and a column constitutes all assignments for a single variable.
1286
1634
 
@@ -1289,10 +1637,12 @@ class Samples:
1289
1637
  list[list[int | float]]
1290
1638
  The samples object as a 2-dimensional list.
1291
1639
  """
1640
+ ...
1292
1641
 
1293
1642
  class Sample:
1294
- """
1295
- A sample object is an assignment of an actual value to each of the models'
1643
+ """Assignment of actual values to the model's variables.
1644
+
1645
+ A sample object is an assignment of an actual value to each of the model's
1296
1646
  variables.
1297
1647
 
1298
1648
  The ``Sample`` class is readonly as it's merely a helper class for looking into a
@@ -1311,17 +1661,20 @@ class Sample:
1311
1661
  [0, -5, 0.28]
1312
1662
  """
1313
1663
 
1664
+ def __str__(self, /) -> str: ...
1314
1665
  @overload
1315
1666
  def __getitem__(self, item: int, /) -> int | float: ...
1316
1667
  @overload
1317
1668
  def __getitem__(self, item: Variable, /) -> int | float: ...
1318
- def __getitem__(self, item: int | Variable, /) -> int | float:
1669
+ @overload
1670
+ def __getitem__(self, item: str, /) -> int | float: ...
1671
+ def __getitem__(self, item: (int | Variable | str), /) -> int | float:
1319
1672
  """
1320
1673
  Extract a variable assignment from the ``Sample`` object.
1321
1674
 
1322
1675
  Returns
1323
1676
  -------
1324
- Sample or int or float
1677
+ int or float
1325
1678
 
1326
1679
  Raises
1327
1680
  ------
@@ -1330,6 +1683,7 @@ class Sample:
1330
1683
  IndexError
1331
1684
  If the row or column index is out of bounds for the variable environment.
1332
1685
  """
1686
+ ...
1333
1687
 
1334
1688
  def __len__(self, /) -> int:
1335
1689
  """
@@ -1339,6 +1693,7 @@ class Sample:
1339
1693
  -------
1340
1694
  int
1341
1695
  """
1696
+ ...
1342
1697
 
1343
1698
  def __iter__(self, /) -> SampleIterator:
1344
1699
  """
@@ -1348,8 +1703,18 @@ class Sample:
1348
1703
  -------
1349
1704
  SampleIterator
1350
1705
  """
1706
+ ...
1707
+
1708
+ def to_dict(self, /) -> dict[str, int | float]:
1709
+ """Convert the sample to a dictionary.
1710
+
1711
+ Returns
1712
+ -------
1713
+ dict
1714
+ A dictionary representation of the sample, where the keys are the
1715
+ variable names and the values are the variables' assignments.
1716
+ """
1351
1717
 
1352
- # _result.pyi
1353
1718
  class ResultIterator:
1354
1719
  """
1355
1720
  An iterator over a solution's results.
@@ -1398,28 +1763,35 @@ class Result:
1398
1763
  @property
1399
1764
  def sample(self, /) -> Sample:
1400
1765
  """Get the sample of the result."""
1766
+ ...
1401
1767
 
1402
1768
  @property
1403
1769
  def obj_value(self, /) -> float | None:
1404
1770
  """Get the objective value of the result."""
1771
+ ...
1405
1772
 
1406
1773
  @property
1407
1774
  def constraints(self, /) -> NDArray | None:
1408
- """
1775
+ """The result's feasibility of all constraints.
1776
+
1409
1777
  Get this result's feasibility values of all constraints. Note that
1410
1778
  `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1411
1779
  this result.
1412
1780
  """
1781
+ ...
1413
1782
 
1414
1783
  @property
1415
1784
  def variable_bounds(self, /) -> NDArray | None:
1416
- """
1417
- Get this result's feasibility values of all variable bounds.
1418
- """
1785
+ """Get this result's feasibility values of all variable bounds."""
1786
+ ...
1419
1787
 
1420
1788
  @property
1421
1789
  def feasible(self, /) -> bool | None:
1422
1790
  """Return whether all constraint results are feasible for this result."""
1791
+ ...
1792
+
1793
+ def __str__(self, /) -> str: ...
1794
+ def __repr__(self, /) -> str: ...
1423
1795
 
1424
1796
  class ResultView:
1425
1797
  """
@@ -1450,46 +1822,56 @@ class ResultView:
1450
1822
  @property
1451
1823
  def sample(self, /) -> Sample:
1452
1824
  """Get the sample of the result."""
1825
+ ...
1453
1826
 
1454
1827
  @property
1455
1828
  def counts(self, /) -> int:
1456
1829
  """Return how often this result appears in the solution."""
1830
+ ...
1457
1831
 
1458
1832
  @property
1459
1833
  def obj_value(self, /) -> float | None:
1460
1834
  """
1461
- Get the objective value of this sample if present. This is the value computed
1462
- by the corresponding AqModel.
1835
+ Get the objective value of this sample if present.
1836
+
1837
+ This is the value computed by the corresponding AqModel.
1463
1838
  """
1839
+ ...
1464
1840
 
1465
1841
  @property
1466
1842
  def raw_energy(self, /) -> float | None:
1467
1843
  """
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.
1844
+ Get the raw energy returned by the algorithm if present.
1845
+
1846
+ This value is not guaranteed to be accurate under consideration of the
1847
+ corresponding AqModel.
1470
1848
  """
1849
+ ...
1471
1850
 
1472
1851
  @property
1473
1852
  def constraints(self, /) -> NDArray | None:
1474
1853
  """
1475
- Get this result's feasibility values of all constraints. Note that
1476
- `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1854
+ Get this result's feasibility values of all constraints.
1855
+
1856
+ Note that `results.constraints[i]` iff. `model.constraints[i]` is feasible for
1477
1857
  this result.
1478
1858
  """
1859
+ ...
1479
1860
 
1480
1861
  @property
1481
1862
  def variable_bounds(self, /) -> NDArray | None:
1482
- """
1483
- Get this result's feasibility values of all variable bounds.
1484
- """
1863
+ """Get this result's feasibility values of all variable bounds."""
1864
+ ...
1485
1865
 
1486
1866
  @property
1487
1867
  def feasible(self, /) -> bool | None:
1488
1868
  """Return whether all constraint results are feasible for this result."""
1869
+ ...
1489
1870
 
1490
- def __eq__(self, other: ResultView, /) -> bool: ... # type: ignore
1871
+ def __str__(self, /) -> str: ...
1872
+ def __repr__(self, /) -> str: ...
1873
+ def __eq__(self, other: ResultView, /) -> bool: ...
1491
1874
 
1492
- # _model.pyi
1493
1875
  class Sense(Enum):
1494
1876
  """
1495
1877
  Enumeration of optimization senses supported by the optimization system.
@@ -1500,7 +1882,6 @@ class Sense(Enum):
1500
1882
 
1501
1883
  Min = ...
1502
1884
  """Indicate the objective function to be minimized."""
1503
-
1504
1885
  Max = ...
1505
1886
  """Indicate the objective function to be maximized."""
1506
1887
 
@@ -1558,22 +1939,11 @@ class Model:
1558
1939
  Notes
1559
1940
  -----
1560
1941
  - The `Model` class does not solve the optimization problem.
1561
- - Use `.objective`, `.constraints`, and `.environment` to access the symbolic content.
1942
+ - Use `.objective`, `.constraints`, and `.environment` to access the symbolic
1943
+ content.
1562
1944
  - Use `encode()` and `decode()` to serialize and recover models.
1563
1945
  """
1564
1946
 
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
1947
  @overload
1578
1948
  def __init__(self, /) -> None: ...
1579
1949
  @overload
@@ -1593,10 +1963,10 @@ class Model:
1593
1963
  def __init__(
1594
1964
  self,
1595
1965
  /,
1596
- name: str | None = ...,
1966
+ name: (str | None) = ...,
1597
1967
  *,
1598
- sense: Sense | None = ...,
1599
- env: Environment | None = ...,
1968
+ sense: (Sense | None) = ...,
1969
+ env: (Environment | None) = ...,
1600
1970
  ) -> None:
1601
1971
  """
1602
1972
  Initialize a new symbolic model.
@@ -1609,6 +1979,7 @@ class Model:
1609
1979
  The environment in which the model operates. If not provided, a new
1610
1980
  environment will be created or inferred from context.
1611
1981
  """
1982
+ ...
1612
1983
 
1613
1984
  def set_sense(self, /, sense: Sense) -> None:
1614
1985
  """
@@ -1619,47 +1990,130 @@ class Model:
1619
1990
  sense : Sense
1620
1991
  The sense of the model (minimization, maximization)
1621
1992
  """
1993
+ ...
1994
+
1995
+ @overload
1996
+ def add_variable(self, name: str, /) -> Variable: ...
1997
+ @overload
1998
+ def add_variable(self, name: str, /, vtype: (Vtype | None) = ...) -> Variable: ...
1999
+ @overload
2000
+ def add_variable(
2001
+ self, name: str, /, vtype: Vtype, *, lower: (float | type[Unbounded])
2002
+ ) -> Variable: ...
2003
+ @overload
2004
+ def add_variable(
2005
+ self, name: str, /, vtype: Vtype, *, upper: (float | type[Unbounded])
2006
+ ) -> Variable: ...
2007
+ @overload
2008
+ def add_variable(
2009
+ self,
2010
+ name: str,
2011
+ /,
2012
+ vtype: Vtype,
2013
+ *,
2014
+ lower: (float | type[Unbounded]),
2015
+ upper: (float | type[Unbounded]),
2016
+ ) -> Variable: ...
2017
+ def add_variable(
2018
+ self,
2019
+ name: str,
2020
+ /,
2021
+ vtype: (Vtype | None) = ...,
2022
+ *,
2023
+ lower: (float | type[Unbounded] | None) = ...,
2024
+ upper: (float | type[Unbounded] | None) = ...,
2025
+ ) -> Variable:
2026
+ """
2027
+ Add a new variable to the model.
2028
+
2029
+ Parameters
2030
+ ----------
2031
+ name : str
2032
+ The name of the variable.
2033
+ vtype : Vtype, optional
2034
+ The variable type (e.g., `Vtype.Real`, `Vtype.Integer`, etc.).
2035
+ Defaults to `Vtype.Binary`.
2036
+ lower: float, optional
2037
+ The lower bound restricts the range of the variable. Only applicable for
2038
+ `Real` and `Integer` variables.
2039
+ upper: float, optional
2040
+ The upper bound restricts the range of the variable. Only applicable for
2041
+ `Real` and `Integer` variables.
2042
+
2043
+ Returns
2044
+ -------
2045
+ Variable
2046
+ The variable added to the model.
2047
+ """
2048
+ ...
2049
+
2050
+ def get_variable(self, name: str, /) -> Variable:
2051
+ """Get a variable by its label (name).
2052
+
2053
+ Parameters
2054
+ ----------
2055
+ label : str
2056
+ The name/label of the variable
2057
+
2058
+ Returns
2059
+ -------
2060
+ Variable
2061
+ The variable with the specified label/name.
2062
+
2063
+ Raises
2064
+ ------
2065
+ VariableNotExistingError
2066
+ If no variable with the specified name is registered.
2067
+ """
2068
+ ...
1622
2069
 
1623
2070
  @property
1624
2071
  def name(self, /) -> str:
1625
2072
  """Return the name of the model."""
2073
+ ...
1626
2074
 
1627
2075
  @property
1628
2076
  def sense(self, /) -> Sense:
1629
2077
  """
1630
- Get the sense of the model
2078
+ Get the sense of the model.
1631
2079
 
1632
2080
  Returns
1633
2081
  -------
1634
2082
  Sense
1635
2083
  The sense of the model (Min or Max).
1636
2084
  """
2085
+ ...
1637
2086
 
1638
2087
  @property
1639
2088
  def objective(self, /) -> Expression:
1640
2089
  """Get the objective expression of the model."""
2090
+ ...
1641
2091
 
1642
2092
  @objective.setter
1643
- def objective(self, value: Expression, /):
2093
+ def objective(self, value: Expression, /) -> None:
1644
2094
  """Set the objective expression of the model."""
2095
+ ...
1645
2096
 
1646
2097
  @property
1647
2098
  def constraints(self, /) -> Constraints:
1648
2099
  """Access the set of constraints associated with the model."""
2100
+ ...
1649
2101
 
1650
2102
  @constraints.setter
1651
- def constraints(self, value: Constraints, /):
2103
+ def constraints(self, value: Constraints, /) -> None:
1652
2104
  """Replace the model's constraints with a new set."""
2105
+ ...
1653
2106
 
1654
2107
  @property
1655
2108
  def environment(self, /) -> Environment:
1656
2109
  """Get the environment in which this model is defined."""
2110
+ ...
1657
2111
 
1658
2112
  @overload
1659
2113
  def variables(self, /) -> list[Variable]: ...
1660
2114
  @overload
1661
2115
  def variables(self, /, *, active: bool) -> list[Variable]: ...
1662
- def variables(self, /, active: bool | None = ...) -> list[Variable]:
2116
+ def variables(self, /, active: (bool | None) = ...) -> list[Variable]:
1663
2117
  """
1664
2118
  Get all variables that are part of this model.
1665
2119
 
@@ -1673,12 +2127,15 @@ class Model:
1673
2127
  -------
1674
2128
  The model's variables as a list.
1675
2129
  """
2130
+ ...
1676
2131
 
1677
2132
  @overload
1678
- def add_constraint(self, /, constraint: Constraint): ...
2133
+ def add_constraint(self, /, constraint: Constraint) -> None: ...
1679
2134
  @overload
1680
- def add_constraint(self, /, constraint: Constraint, name: str): ...
1681
- def add_constraint(self, /, constraint: Constraint, name: str | None = ...):
2135
+ def add_constraint(self, /, constraint: Constraint, name: str) -> None: ...
2136
+ def add_constraint(
2137
+ self, /, constraint: Constraint, name: (str | None) = ...
2138
+ ) -> None:
1682
2139
  """
1683
2140
  Add a constraint to the model's constraint collection.
1684
2141
 
@@ -1689,12 +2146,15 @@ class Model:
1689
2146
  name : str, optional
1690
2147
  The name of the constraint to be added.
1691
2148
  """
2149
+ ...
1692
2150
 
1693
2151
  @overload
1694
- def set_objective(self, /, expression: Expression): ...
2152
+ def set_objective(self, /, expression: Expression) -> None: ...
1695
2153
  @overload
1696
- def set_objective(self, /, expression: Expression, *, sense: Sense): ...
1697
- def set_objective(self, /, expression: Expression, *, sense: Sense | None = ...):
2154
+ def set_objective(self, /, expression: Expression, *, sense: Sense) -> None: ...
2155
+ def set_objective(
2156
+ self, /, expression: Expression, *, sense: (Sense | None) = ...
2157
+ ) -> None:
1698
2158
  """
1699
2159
  Set the model's objective to this expression.
1700
2160
 
@@ -1705,6 +2165,7 @@ class Model:
1705
2165
  sense : Sense, optional
1706
2166
  The sense of the model for this objective, by default Sense.Min.
1707
2167
  """
2168
+ ...
1708
2169
 
1709
2170
  @property
1710
2171
  def num_constraints(self, /) -> int:
@@ -1716,6 +2177,7 @@ class Model:
1716
2177
  int
1717
2178
  Total number of constraints.
1718
2179
  """
2180
+ ...
1719
2181
 
1720
2182
  def evaluate(self, /, solution: Solution) -> Solution:
1721
2183
  """
@@ -1731,6 +2193,7 @@ class Model:
1731
2193
  Solution
1732
2194
  A new solution object with filled-out information.
1733
2195
  """
2196
+ ...
1734
2197
 
1735
2198
  def evaluate_sample(self, /, sample: Sample) -> Result:
1736
2199
  """
@@ -1746,6 +2209,55 @@ class Model:
1746
2209
  Result
1747
2210
  A result object containing the information from the evaluation process.
1748
2211
  """
2212
+ ...
2213
+
2214
+ def violated_constraints(self, /, sample: Sample) -> Constraints:
2215
+ """
2216
+ Get all model constraints that are violated by the given sample.
2217
+
2218
+ Parameters
2219
+ ----------
2220
+ sample : Sample
2221
+ The sample to check constraint feasibility for.
2222
+
2223
+ Returns
2224
+ -------
2225
+ Constraints
2226
+ The constraints violated by the given sample.
2227
+ """
2228
+ ...
2229
+
2230
+ def substitute(
2231
+ self, /, target: Variable, replacement: (Expression | Variable)
2232
+ ) -> None:
2233
+ """Substitute every occurrence of variable.
2234
+
2235
+ Substitute every occurrence of a variable in the model's objective and
2236
+ constraint expressions with another expression.
2237
+
2238
+ Given a `Model` instance `self`, this method replaces all occurrences of
2239
+ `target` with `replacement` for the objective and each constraint.
2240
+ If any substitution would cross differing environments (e.g. captures from two
2241
+ different scopes), it raises a `DifferentEnvsError`.
2242
+
2243
+ Parameters
2244
+ ----------
2245
+ target : VarRef
2246
+ The variable reference to replace.
2247
+ replacement : Expression
2248
+ The expression to insert in place of `target`.
2249
+
2250
+ Returns
2251
+ -------
2252
+ None
2253
+ Performs substitution in place; no return value.
2254
+
2255
+ Raises
2256
+ ------
2257
+ DifferentEnvsError
2258
+ If the environments of `self`, `target`, and `replacement`
2259
+ are not compatible.
2260
+ """
1749
2261
 
1750
2262
  @overload
1751
2263
  def encode(self, /) -> bytes: ...
@@ -1755,7 +2267,9 @@ class Model:
1755
2267
  def encode(self, /, *, level: int) -> bytes: ...
1756
2268
  @overload
1757
2269
  def encode(self, /, compress: bool, level: int) -> bytes: ...
1758
- def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
2270
+ def encode(
2271
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
2272
+ ) -> bytes:
1759
2273
  """
1760
2274
  Serialize the model into a compact binary format.
1761
2275
 
@@ -1764,7 +2278,7 @@ class Model:
1764
2278
  compress : bool, optional
1765
2279
  Whether to compress the binary output. Default is True.
1766
2280
  level : int, optional
1767
- Compression level (09). Default is 3.
2281
+ Compression level (0-9). Default is 3.
1768
2282
 
1769
2283
  Returns
1770
2284
  -------
@@ -1776,6 +2290,7 @@ class Model:
1776
2290
  IOError
1777
2291
  If serialization fails.
1778
2292
  """
2293
+ ...
1779
2294
 
1780
2295
  @overload
1781
2296
  def serialize(self, /) -> bytes: ...
@@ -1786,13 +2301,14 @@ class Model:
1786
2301
  @overload
1787
2302
  def serialize(self, /, compress: bool, level: int) -> bytes: ...
1788
2303
  def serialize(
1789
- self, /, compress: bool | None = ..., level: int | None = ...
2304
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
1790
2305
  ) -> bytes:
1791
2306
  """
1792
2307
  Alias for `encode()`.
1793
2308
 
1794
2309
  See `encode()` for full documentation.
1795
2310
  """
2311
+ ...
1796
2312
 
1797
2313
  @classmethod
1798
2314
  def decode(cls, data: bytes) -> Model:
@@ -1814,6 +2330,7 @@ class Model:
1814
2330
  DecodeError
1815
2331
  If decoding fails due to corruption or incompatibility.
1816
2332
  """
2333
+ ...
1817
2334
 
1818
2335
  @classmethod
1819
2336
  def deserialize(cls, data: bytes) -> Model:
@@ -1822,8 +2339,9 @@ class Model:
1822
2339
 
1823
2340
  See `decode()` for full documentation.
1824
2341
  """
2342
+ ...
1825
2343
 
1826
- def __eq__(self, other: Model, /) -> bool: # type: ignore
2344
+ def __eq__(self, other: Model, /) -> bool:
1827
2345
  """
1828
2346
  Check whether this model is equal to `other`.
1829
2347
 
@@ -1835,18 +2353,32 @@ class Model:
1835
2353
  -------
1836
2354
  bool
1837
2355
  """
2356
+ ...
1838
2357
 
2358
+ def __str__(self, /) -> str: ...
2359
+ def __repr__(self, /) -> str: ...
1839
2360
  def __hash__(self, /) -> int: ...
2361
+ metadata: ModelMetadata | None = ...
2362
+
2363
+ @staticmethod
2364
+ def load_luna(model_id: str, client: (ILunaSolve | str | None) = None) -> Model: ...
2365
+ def save_luna(self, client: (ILunaSolve | str | None) = None) -> None: ...
2366
+ def delete_luna(self, client: (ILunaSolve | str | None) = None) -> None: ...
2367
+ def load_solutions(
2368
+ self, client: (ILunaSolve | str | None) = None
2369
+ ) -> list[Solution]: ...
2370
+ def load_solve_jobs(
2371
+ self, client: (ILunaSolve | str | None) = None
2372
+ ) -> list[SolveJob]: ...
1840
2373
 
1841
- # _expression.pyi
1842
2374
  class Expression:
1843
2375
  """
1844
- Polynomial expression supporting symbolic arithmetic, constraint creation, and encoding.
2376
+ Polynomial expression supporting symbolic arithmetic, constraint creation.
1845
2377
 
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.
2378
+ An `Expression` represents a real-valued mathematical function composed of
2379
+ variables, scalars, and coefficients. Expressions may include constant, linear,
2380
+ quadratic, and higher-order terms (cubic and beyond). They are used to build
2381
+ objective functions and constraints in symbolic optimization models.
1850
2382
 
1851
2383
  Expressions support both regular and in-place arithmetic, including addition and
1852
2384
  multiplication with integers, floats, `Variable` instances, and other `Expression`s.
@@ -1936,20 +2468,21 @@ class Expression:
1936
2468
  def __init__(self, /) -> None: ...
1937
2469
  @overload
1938
2470
  def __init__(self, /, env: Environment) -> None: ...
1939
- def __init__(self, /, env: Environment | None = ...) -> None:
2471
+ def __init__(self, /, env: (Environment | None) = ...) -> None:
1940
2472
  """
1941
- Create a new empty expression scoped to an environment.
2473
+ Create a new empty expression scoped to an environment.
1942
2474
 
1943
2475
  Parameters
1944
2476
  ----------
1945
- env : Environment
1946
- The environment to which this expression is bound.
2477
+ env : Environment
2478
+ The environment to which this expression is bound.
1947
2479
 
1948
2480
  Raises
1949
2481
  ------
1950
- NoActiveEnvironmentFoundError
1951
- If no environment is provided and none is active in the context.
2482
+ NoActiveEnvironmentFoundError
2483
+ If no environment is provided and none is active in the context.
1952
2484
  """
2485
+ ...
1953
2486
 
1954
2487
  def get_offset(self, /) -> float:
1955
2488
  """
@@ -1960,6 +2493,7 @@ class Expression:
1960
2493
  float
1961
2494
  The constant term.
1962
2495
  """
2496
+ ...
1963
2497
 
1964
2498
  def get_linear(self, /, variable: Variable) -> float:
1965
2499
  """
@@ -1980,6 +2514,7 @@ class Expression:
1980
2514
  VariableOutOfRangeError
1981
2515
  If the variable index is not valid in this expression's environment.
1982
2516
  """
2517
+ ...
1983
2518
 
1984
2519
  def get_quadratic(self, /, u: Variable, v: Variable) -> float:
1985
2520
  """
@@ -2000,6 +2535,7 @@ class Expression:
2000
2535
  VariableOutOfRangeError
2001
2536
  If either variable is out of bounds for the expression's environment.
2002
2537
  """
2538
+ ...
2003
2539
 
2004
2540
  def get_higher_order(self, /, variables: tuple[Variable, ...]) -> float:
2005
2541
  """
@@ -2020,6 +2556,21 @@ class Expression:
2020
2556
  VariableOutOfRangeError
2021
2557
  If any variable is out of bounds for the environment.
2022
2558
  """
2559
+ ...
2560
+
2561
+ def items(self, /) -> ExpressionIterator:
2562
+ """
2563
+ Iterate over the single components of an expression.
2564
+
2565
+ An *component* refers to
2566
+ a single constant, linear, quadratic, or higher-order term of an expression.
2567
+
2568
+ Returns
2569
+ -------
2570
+ ExpressionIterator
2571
+ The iterator over the expression's components.
2572
+ """
2573
+ ...
2023
2574
 
2024
2575
  @property
2025
2576
  def num_variables(self, /) -> int:
@@ -2031,6 +2582,7 @@ class Expression:
2031
2582
  int
2032
2583
  Number of variables with non-zero coefficients.
2033
2584
  """
2585
+ ...
2034
2586
 
2035
2587
  def is_equal(self, /, other: Expression) -> bool:
2036
2588
  """
@@ -2046,6 +2598,37 @@ class Expression:
2046
2598
  bool
2047
2599
  If the two expressions are equal.
2048
2600
  """
2601
+ ...
2602
+
2603
+ def substitute(
2604
+ self, /, target: Variable, replacement: (Expression | Variable)
2605
+ ) -> Expression:
2606
+ """
2607
+ Substitute every occurrence of a variable with another expression.
2608
+
2609
+ Given an expression `self`, this method replaces all occurrences of `target`
2610
+ with `replacement`. If the substitution would cross differing environments
2611
+ (e.g. captures from two different scopes), it returns a `DifferentEnvsErr`.
2612
+
2613
+ Parameters
2614
+ ----------
2615
+ target : VarRef
2616
+ The variable reference to replace.
2617
+ replacement : Expression
2618
+ The expression to insert in place of `target`.
2619
+
2620
+ Returns
2621
+ -------
2622
+ Expression
2623
+ The resulting expression after substitution.
2624
+
2625
+ Raises
2626
+ ------
2627
+ DifferentEnvsErr
2628
+ If the environments of `self`, `target` and `replacement`
2629
+ are not compatible.
2630
+ """
2631
+ ...
2049
2632
 
2050
2633
  @overload
2051
2634
  def encode(self, /) -> bytes: ...
@@ -2055,7 +2638,9 @@ class Expression:
2055
2638
  def encode(self, /, *, level: int) -> bytes: ...
2056
2639
  @overload
2057
2640
  def encode(self, /, compress: bool, level: int) -> bytes: ...
2058
- def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
2641
+ def encode(
2642
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
2643
+ ) -> bytes:
2059
2644
  """
2060
2645
  Serialize the expression into a compact binary format.
2061
2646
 
@@ -2064,7 +2649,7 @@ class Expression:
2064
2649
  compress : bool, optional
2065
2650
  Whether to compress the data. Default is True.
2066
2651
  level : int, optional
2067
- Compression level (09). Default is 3.
2652
+ Compression level (0-9). Default is 3.
2068
2653
 
2069
2654
  Returns
2070
2655
  -------
@@ -2076,6 +2661,7 @@ class Expression:
2076
2661
  IOError
2077
2662
  If serialization fails.
2078
2663
  """
2664
+ ...
2079
2665
 
2080
2666
  @overload
2081
2667
  def serialize(self, /) -> bytes: ...
@@ -2086,13 +2672,14 @@ class Expression:
2086
2672
  @overload
2087
2673
  def serialize(self, /, compress: bool, level: int) -> bytes: ...
2088
2674
  def serialize(
2089
- self, /, compress: bool | None = ..., level: int | None = ...
2675
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
2090
2676
  ) -> bytes:
2091
2677
  """
2092
2678
  Alias for `encode()`.
2093
2679
 
2094
2680
  See `encode()` for full documentation.
2095
2681
  """
2682
+ ...
2096
2683
 
2097
2684
  @classmethod
2098
2685
  def decode(cls, data: bytes) -> Expression:
@@ -2114,6 +2701,7 @@ class Expression:
2114
2701
  DecodeError
2115
2702
  If decoding fails due to corruption or incompatibility.
2116
2703
  """
2704
+ ...
2117
2705
 
2118
2706
  @classmethod
2119
2707
  def deserialize(cls, data: bytes) -> Expression:
@@ -2122,6 +2710,7 @@ class Expression:
2122
2710
 
2123
2711
  See `decode()` for full documentation.
2124
2712
  """
2713
+ ...
2125
2714
 
2126
2715
  @overload
2127
2716
  def __add__(self, other: Expression, /) -> Expression: ...
@@ -2131,7 +2720,7 @@ class Expression:
2131
2720
  def __add__(self, other: int, /) -> Expression: ...
2132
2721
  @overload
2133
2722
  def __add__(self, other: float, /) -> Expression: ...
2134
- def __add__(self, other: Expression | Variable | float, /) -> Expression:
2723
+ def __add__(self, other: (Expression | Variable | int | float), /) -> Expression:
2135
2724
  """
2136
2725
  Add another expression, variable, or scalar.
2137
2726
 
@@ -2150,6 +2739,7 @@ class Expression:
2150
2739
  TypeError
2151
2740
  If the operand type is unsupported.
2152
2741
  """
2742
+ ...
2153
2743
 
2154
2744
  @overload
2155
2745
  def __radd__(self, other: Expression, /) -> Expression: ...
@@ -2159,7 +2749,7 @@ class Expression:
2159
2749
  def __radd__(self, other: int, /) -> Expression: ...
2160
2750
  @overload
2161
2751
  def __radd__(self, other: float, /) -> Expression: ...
2162
- def __radd__(self, other: Expression | Variable | float, /) -> Expression:
2752
+ def __radd__(self, other: (Expression | Variable | int | float), /) -> Expression:
2163
2753
  """
2164
2754
  Add this expression to a scalar or variable.
2165
2755
 
@@ -2176,16 +2766,17 @@ class Expression:
2176
2766
  TypeError
2177
2767
  If the operand type is unsupported.
2178
2768
  """
2769
+ ...
2179
2770
 
2180
2771
  @overload
2181
- def __iadd__(self, other: Expression, /): ...
2772
+ def __iadd__(self, other: Expression, /) -> Self: ...
2182
2773
  @overload
2183
- def __iadd__(self, other: Variable, /): ...
2774
+ def __iadd__(self, other: Variable, /) -> Self: ...
2184
2775
  @overload
2185
- def __iadd__(self, other: int, /): ...
2776
+ def __iadd__(self, other: int, /) -> Self: ...
2186
2777
  @overload
2187
- def __iadd__(self, other: float, /): ...
2188
- def __iadd__(self, other: Expression | Variable | float, /) -> Expression:
2778
+ def __iadd__(self, other: float, /) -> Self: ...
2779
+ def __iadd__(self, other: (Expression | Variable | int | float), /) -> Self:
2189
2780
  """
2190
2781
  In-place addition.
2191
2782
 
@@ -2195,7 +2786,7 @@ class Expression:
2195
2786
 
2196
2787
  Returns
2197
2788
  -------
2198
- Expression
2789
+ Self
2199
2790
 
2200
2791
  Raises
2201
2792
  ------
@@ -2204,16 +2795,17 @@ class Expression:
2204
2795
  TypeError
2205
2796
  If the operand type is unsupported.
2206
2797
  """
2798
+ ...
2207
2799
 
2208
2800
  @overload
2209
- def __isub__(self, other: Expression, /): ...
2801
+ def __isub__(self, other: Expression, /) -> Self: ...
2210
2802
  @overload
2211
- def __isub__(self, other: Variable, /): ...
2803
+ def __isub__(self, other: Variable, /) -> Self: ...
2212
2804
  @overload
2213
- def __isub__(self, other: int, /): ...
2805
+ def __isub__(self, other: int, /) -> Self: ...
2214
2806
  @overload
2215
- def __isub__(self, other: float, /): ...
2216
- def __isub__(self, other: Expression | Variable | float, /):
2807
+ def __isub__(self, other: float, /) -> Self: ...
2808
+ def __isub__(self, other: (Expression | Variable | int | float), /) -> Self:
2217
2809
  """
2218
2810
  In-place subtraction.
2219
2811
 
@@ -2223,7 +2815,7 @@ class Expression:
2223
2815
 
2224
2816
  Returns
2225
2817
  -------
2226
- Expression
2818
+ Self
2227
2819
 
2228
2820
  Raises
2229
2821
  ------
@@ -2232,6 +2824,7 @@ class Expression:
2232
2824
  TypeError
2233
2825
  If the operand type is unsupported.
2234
2826
  """
2827
+ ...
2235
2828
 
2236
2829
  @overload
2237
2830
  def __sub__(self, other: Expression, /) -> Expression: ...
@@ -2241,7 +2834,7 @@ class Expression:
2241
2834
  def __sub__(self, other: int, /) -> Expression: ...
2242
2835
  @overload
2243
2836
  def __sub__(self, other: float, /) -> Expression: ...
2244
- def __sub__(self, other: Expression | Variable | float, /) -> Expression:
2837
+ def __sub__(self, other: (Expression | Variable | int | float), /) -> Expression:
2245
2838
  """
2246
2839
  Subtract another expression, variable, or scalar.
2247
2840
 
@@ -2260,6 +2853,7 @@ class Expression:
2260
2853
  TypeError
2261
2854
  If the operand type is unsupported.
2262
2855
  """
2856
+ ...
2263
2857
 
2264
2858
  @overload
2265
2859
  def __mul__(self, other: Expression, /) -> Expression: ...
@@ -2269,7 +2863,7 @@ class Expression:
2269
2863
  def __mul__(self, other: int, /) -> Expression: ...
2270
2864
  @overload
2271
2865
  def __mul__(self, other: float, /) -> Expression: ...
2272
- def __mul__(self, other: Expression | Variable | float, /) -> Expression:
2866
+ def __mul__(self, other: (Expression | Variable | int | float), /) -> Expression:
2273
2867
  """
2274
2868
  Multiply this expression by another value.
2275
2869
 
@@ -2288,12 +2882,13 @@ class Expression:
2288
2882
  TypeError
2289
2883
  If the operand type is unsupported.
2290
2884
  """
2885
+ ...
2291
2886
 
2292
2887
  @overload
2293
2888
  def __rmul__(self, other: int, /) -> Expression: ...
2294
2889
  @overload
2295
2890
  def __rmul__(self, other: float, /) -> Expression: ...
2296
- def __rmul__(self, other: float, /) -> Expression:
2891
+ def __rmul__(self, other: (int | float), /) -> Expression:
2297
2892
  """
2298
2893
  Right-hand multiplication.
2299
2894
 
@@ -2310,16 +2905,17 @@ class Expression:
2310
2905
  TypeError
2311
2906
  If the operand type is unsupported.
2312
2907
  """
2908
+ ...
2313
2909
 
2314
2910
  @overload
2315
- def __imul__(self, other: Expression, /): ...
2911
+ def __imul__(self, other: Expression, /) -> Self: ...
2316
2912
  @overload
2317
- def __imul__(self, other: Variable, /): ...
2913
+ def __imul__(self, other: Variable, /) -> Self: ...
2318
2914
  @overload
2319
- def __imul__(self, other: int, /): ...
2915
+ def __imul__(self, other: int, /) -> Self: ...
2320
2916
  @overload
2321
- def __imul__(self, other: float, /): ...
2322
- def __imul__(self, other: Expression | Variable | float, /):
2917
+ def __imul__(self, other: float, /) -> Self: ...
2918
+ def __imul__(self, other: (Expression | Variable | int | float), /) -> Self:
2323
2919
  """
2324
2920
  In-place multiplication.
2325
2921
 
@@ -2329,7 +2925,7 @@ class Expression:
2329
2925
 
2330
2926
  Returns
2331
2927
  -------
2332
- Expression
2928
+ Self
2333
2929
 
2334
2930
  Raises
2335
2931
  ------
@@ -2338,6 +2934,7 @@ class Expression:
2338
2934
  TypeError
2339
2935
  If the operand type is unsupported.
2340
2936
  """
2937
+ ...
2341
2938
 
2342
2939
  def __pow__(self, other: int, /) -> Expression:
2343
2940
  """
@@ -2356,18 +2953,19 @@ class Expression:
2356
2953
  RuntimeError
2357
2954
  If the param `modulo` usually supported for `__pow__` is specified.
2358
2955
  """
2956
+ ...
2359
2957
 
2360
2958
  @overload
2361
2959
  def __eq__(self, rhs: Expression, /) -> Constraint: ...
2362
2960
  @overload
2363
2961
  def __eq__(self, rhs: Variable, /) -> Constraint: ...
2364
2962
  @overload
2365
- def __eq__(self, rhs: int, /) -> Constraint: ... # type: ignore
2963
+ def __eq__(self, rhs: int, /) -> Constraint: ...
2366
2964
  @overload
2367
- def __eq__(self, rhs: float, /) -> Constraint: ... # type: ignore
2368
- def __eq__(self, rhs: Expression | Variable | float, /) -> Constraint: # type: ignore
2965
+ def __eq__(self, rhs: float, /) -> Constraint: ...
2966
+ def __eq__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
2369
2967
  """
2370
- Compare to a different expression or create a constraint `expression == scalar`
2968
+ Compare to a different expression or create a constraint `expression == scalar`.
2371
2969
 
2372
2970
  If `rhs` is of type `Variable` or `Expression` it is moved to the `lhs` in the
2373
2971
  constraint, resulting in the following constraint:
@@ -2387,6 +2985,7 @@ class Expression:
2387
2985
  TypeError
2388
2986
  If the right-hand side is not an Expression or scalar.
2389
2987
  """
2988
+ ...
2390
2989
 
2391
2990
  @overload
2392
2991
  def __le__(self, rhs: Expression, /) -> Constraint: ...
@@ -2396,7 +2995,7 @@ class Expression:
2396
2995
  def __le__(self, rhs: int, /) -> Constraint: ...
2397
2996
  @overload
2398
2997
  def __le__(self, rhs: float, /) -> Constraint: ...
2399
- def __le__(self, rhs: Expression | Variable | float, /) -> Constraint:
2998
+ def __le__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
2400
2999
  """
2401
3000
  Create a constraint `expression <= scalar`.
2402
3001
 
@@ -2418,6 +3017,7 @@ class Expression:
2418
3017
  TypeError
2419
3018
  If the right-hand side is not of type float, int, Variable or Expression.
2420
3019
  """
3020
+ ...
2421
3021
 
2422
3022
  @overload
2423
3023
  def __ge__(self, rhs: Expression, /) -> Constraint: ...
@@ -2427,7 +3027,7 @@ class Expression:
2427
3027
  def __ge__(self, rhs: int, /) -> Constraint: ...
2428
3028
  @overload
2429
3029
  def __ge__(self, rhs: float, /) -> Constraint: ...
2430
- def __ge__(self, rhs: Expression | Variable | float, /) -> Constraint:
3030
+ def __ge__(self, rhs: (Expression | Variable | int | float), /) -> Constraint:
2431
3031
  """
2432
3032
  Create a constraint: expression >= scalar.
2433
3033
 
@@ -2449,6 +3049,7 @@ class Expression:
2449
3049
  TypeError
2450
3050
  If the right-hand side is not of type float, int, Variable or Expression.
2451
3051
  """
3052
+ ...
2452
3053
 
2453
3054
  def __neg__(self, /) -> Expression:
2454
3055
  """
@@ -2458,14 +3059,43 @@ class Expression:
2458
3059
  -------
2459
3060
  Expression
2460
3061
  """
3062
+ ...
3063
+
3064
+ @property
3065
+ def _environment(self, /) -> Environment:
3066
+ """Get this expression's environment."""
3067
+ ...
3068
+
3069
+ def __str__(self, /) -> str: ...
3070
+ def __repr__(self, /) -> str: ...
3071
+
3072
+ class ExpressionIterator:
3073
+ """
3074
+ Iterate over the single components of an expression.
3075
+
3076
+ Examples
3077
+ --------
3078
+ >>> from luna_quantum import Constant, Expression, HigherOrder, Linear, Quadratic
3079
+ >>> expr: Expression = ...
3080
+ >>> vars: Constant | Linear | Quadratic | HigherOrder
3081
+ >>> bias: float
3082
+ >>> for vars, bias in expr.items():
3083
+ >>> match vars:
3084
+ >>> case Constant(): do_something_with_constant(bias)
3085
+ >>> case Linear(x): do_something_with_linear_var(x, bias)
3086
+ >>> case Quadratic(x, y): do_something_with_quadratic_vars(x, y, bias)
3087
+ >>> case HigherOrder(ho): do_something_with_higher_order_vars(ho, bias)
3088
+ """
3089
+
3090
+ def __next__(self) -> tuple[Constant | Linear | Quadratic | HigherOrder, float]: ...
3091
+ def __iter__(self) -> ExpressionIterator: ...
2461
3092
 
2462
- # _environment.pyi
2463
3093
  class Environment:
2464
3094
  """
2465
3095
  Execution context for variable creation and expression scoping.
2466
3096
 
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.
3097
+ An `Environment` provides the symbolic scope in which `Variable`s are defined.
3098
+ It is required for constructing variables ensuring consistency across expressions.
2469
3099
  The environment does **not** store constraints or expressions — it only facilitates
2470
3100
  their creation by acting as a context manager and anchor for `Variable` instances.
2471
3101
 
@@ -2489,7 +3119,8 @@ class Environment:
2489
3119
  Notes
2490
3120
  -----
2491
3121
  - The environment is required to create `Variable` instances.
2492
- - It does **not** own constraints or expressions — they merely reference variables tied to an environment.
3122
+ - It does **not** own constraints or expressions — they merely reference variables
3123
+ tied to an environment.
2493
3124
  - Environments **cannot be nested**. Only one can be active at a time.
2494
3125
  - Use `encode()` / `decode()` to persist and recover expression trees.
2495
3126
  """
@@ -2500,8 +3131,9 @@ class Environment:
2500
3131
 
2501
3132
  It is recommended to use this in a `with` statement to ensure proper scoping.
2502
3133
  """
3134
+ ...
2503
3135
 
2504
- def __enter__(self, /) -> Any:
3136
+ def __enter__(self, /) -> Self:
2505
3137
  """
2506
3138
  Activate this environment for variable creation.
2507
3139
 
@@ -2515,13 +3147,21 @@ class Environment:
2515
3147
  MultipleActiveEnvironmentsError
2516
3148
  If another environment is already active.
2517
3149
  """
3150
+ ...
2518
3151
 
2519
- def __exit__(self, /, exc_type, exc_value, exc_traceback) -> None:
3152
+ def __exit__(
3153
+ self,
3154
+ /,
3155
+ exc_type: (type[BaseException] | None) = ...,
3156
+ exc_value: (BaseException | None) = ...,
3157
+ exc_traceback: (TracebackType | None) = ...,
3158
+ ) -> None:
2520
3159
  """
2521
3160
  Deactivate this environment.
2522
3161
 
2523
3162
  Called automatically at the end of a `with` block.
2524
3163
  """
3164
+ ...
2525
3165
 
2526
3166
  def get_variable(self, /, name: str) -> Variable:
2527
3167
  """
@@ -2542,6 +3182,7 @@ class Environment:
2542
3182
  VariableNotExistingError
2543
3183
  If no variable with the specified name is registered.
2544
3184
  """
3185
+ ...
2545
3186
 
2546
3187
  @overload
2547
3188
  def encode(self, /) -> bytes: ...
@@ -2551,7 +3192,9 @@ class Environment:
2551
3192
  def encode(self, /, *, level: int) -> bytes: ...
2552
3193
  @overload
2553
3194
  def encode(self, /, compress: bool, level: int) -> bytes: ...
2554
- def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
3195
+ def encode(
3196
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
3197
+ ) -> bytes:
2555
3198
  """
2556
3199
  Serialize the environment into a compact binary format.
2557
3200
 
@@ -2574,6 +3217,7 @@ class Environment:
2574
3217
  IOError
2575
3218
  If serialization fails.
2576
3219
  """
3220
+ ...
2577
3221
 
2578
3222
  @overload
2579
3223
  def serialize(self, /) -> bytes: ...
@@ -2584,13 +3228,14 @@ class Environment:
2584
3228
  @overload
2585
3229
  def serialize(self, /, compress: bool, level: int) -> bytes: ...
2586
3230
  def serialize(
2587
- self, /, compress: bool | None = ..., level: int | None = ...
3231
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
2588
3232
  ) -> bytes:
2589
3233
  """
2590
3234
  Alias for `encode()`.
2591
3235
 
2592
3236
  See `encode()` for full usage details.
2593
3237
  """
3238
+ ...
2594
3239
 
2595
3240
  @classmethod
2596
3241
  def decode(cls, data: bytes) -> Environment:
@@ -2612,6 +3257,7 @@ class Environment:
2612
3257
  DecodeError
2613
3258
  If decoding fails due to corruption or incompatibility.
2614
3259
  """
3260
+ ...
2615
3261
 
2616
3262
  @classmethod
2617
3263
  def deserialize(cls, data: bytes) -> Environment:
@@ -2620,10 +3266,12 @@ class Environment:
2620
3266
 
2621
3267
  See `decode()` for full usage details.
2622
3268
  """
3269
+ ...
2623
3270
 
2624
- def __eq__(self, other: Environment, /) -> bool: ... # type: ignore
3271
+ def __eq__(self, other: Environment, /) -> bool: ...
3272
+ def __str__(self, /) -> str: ...
3273
+ def __repr__(self, /) -> str: ...
2625
3274
 
2626
- # _constraints.pyi
2627
3275
  class Comparator(Enum):
2628
3276
  """
2629
3277
  Comparison operators used to define constraints.
@@ -2649,13 +3297,14 @@ class Comparator(Enum):
2649
3297
 
2650
3298
  Eq = ...
2651
3299
  """Equality (==)"""
2652
-
2653
3300
  Le = ...
2654
3301
  """Less-than or equal (<=)"""
2655
-
2656
3302
  Ge = ...
2657
3303
  """Greater-than or equal (>=)"""
2658
3304
 
3305
+ def __str__(self, /) -> str: ...
3306
+ def __repr__(self, /) -> str: ...
3307
+
2659
3308
  class Constraint:
2660
3309
  """
2661
3310
  A symbolic constraint formed by comparing an expression to a constant.
@@ -2754,8 +3403,8 @@ class Constraint:
2754
3403
  def __init__(
2755
3404
  self,
2756
3405
  /,
2757
- lhs: Variable | Expression,
2758
- rhs: float | Expression | Variable,
3406
+ lhs: (Variable | Expression),
3407
+ rhs: (int | float | Expression | Variable),
2759
3408
  comparator: Comparator,
2760
3409
  name: str,
2761
3410
  ) -> None:
@@ -2780,6 +3429,7 @@ class Constraint:
2780
3429
  IllegalConstraintNameError
2781
3430
  If the constraint is tried to be created with an illegal name.
2782
3431
  """
3432
+ ...
2783
3433
 
2784
3434
  @property
2785
3435
  def name(self, /) -> str | None:
@@ -2791,41 +3441,47 @@ class Constraint:
2791
3441
  str, optional
2792
3442
  Returns the name of the constraint as a string or None if it is unnamed.
2793
3443
  """
3444
+ ...
2794
3445
 
2795
3446
  @property
2796
3447
  def lhs(self, /) -> Expression:
2797
3448
  """
2798
- Get the left-hand side of the constraint
3449
+ Get the left-hand side of the constraint.
2799
3450
 
2800
3451
  Returns
2801
3452
  -------
2802
3453
  Expression
2803
3454
  The left-hand side expression.
2804
3455
  """
3456
+ ...
2805
3457
 
2806
3458
  @property
2807
3459
  def rhs(self, /) -> float:
2808
3460
  """
2809
- Get the right-hand side of the constraint
3461
+ Get the right-hand side of the constraint.
2810
3462
 
2811
3463
  Returns
2812
3464
  -------
2813
3465
  float
2814
3466
  The right-hand side expression.
2815
3467
  """
3468
+ ...
2816
3469
 
2817
3470
  @property
2818
3471
  def comparator(self, /) -> Comparator:
2819
3472
  """
2820
- Get the comparator of the constraint
3473
+ Get the comparator of the constraint.
2821
3474
 
2822
3475
  Returns
2823
3476
  -------
2824
3477
  Comparator
2825
3478
  The comparator of the constraint.
2826
3479
  """
3480
+ ...
2827
3481
 
2828
- def __eq__(self, other: Constraint, /) -> bool: ... # type: ignore
3482
+ def __eq__(self, other: Constraint, /) -> bool: ...
3483
+ def __str__(self, /) -> str: ...
3484
+ def __repr__(self, /) -> str: ...
2829
3485
 
2830
3486
  class Constraints:
2831
3487
  """
@@ -2845,8 +3501,6 @@ class Constraints:
2845
3501
  ... c = Constraint(x + 1, 0.0, Comparator.Le)
2846
3502
 
2847
3503
  >>> cs = Constraints()
2848
- >>> cs.add_constraint(c)
2849
-
2850
3504
  >>> cs += x >= 1.0
2851
3505
 
2852
3506
  Serialization:
@@ -2862,21 +3516,12 @@ class Constraints:
2862
3516
 
2863
3517
  def __init__(self, /) -> None: ...
2864
3518
  @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
-
3519
+ def add_constraint(self, /, constraint: Constraint) -> None: ...
2877
3520
  @overload
2878
- def add_constraint(self, /, constraint: Constraint, name: str): ...
2879
- def add_constraint(self, /, constraint: Constraint, name: str | None = ...):
3521
+ def add_constraint(self, /, constraint: Constraint, name: str) -> None: ...
3522
+ def add_constraint(
3523
+ self, /, constraint: Constraint, name: (str | None) = ...
3524
+ ) -> None:
2880
3525
  """
2881
3526
  Add a constraint to the collection.
2882
3527
 
@@ -2887,6 +3532,7 @@ class Constraints:
2887
3532
  name : str, optional
2888
3533
  The name of the constraint to be added.
2889
3534
  """
3535
+ ...
2890
3536
 
2891
3537
  @overload
2892
3538
  def encode(self, /) -> bytes: ...
@@ -2896,7 +3542,9 @@ class Constraints:
2896
3542
  def encode(self, /, *, level: int) -> bytes: ...
2897
3543
  @overload
2898
3544
  def encode(self, /, compress: bool, level: int) -> bytes: ...
2899
- def encode(self, /, compress: bool | None = ..., level: int | None = ...) -> bytes:
3545
+ def encode(
3546
+ self, /, compress: (bool | None) = True, level: (int | None) = 3
3547
+ ) -> bytes:
2900
3548
  """
2901
3549
  Serialize the constraint collection to a binary blob.
2902
3550
 
@@ -2905,7 +3553,7 @@ class Constraints:
2905
3553
  compress : bool, optional
2906
3554
  Whether to compress the result. Default is True.
2907
3555
  level : int, optional
2908
- Compression level (09). Default is 3.
3556
+ Compression level (0-9). Default is 3.
2909
3557
 
2910
3558
  Returns
2911
3559
  -------
@@ -2917,6 +3565,7 @@ class Constraints:
2917
3565
  IOError
2918
3566
  If serialization fails.
2919
3567
  """
3568
+ ...
2920
3569
 
2921
3570
  @overload
2922
3571
  def serialize(self, /) -> bytes: ...
@@ -2927,13 +3576,14 @@ class Constraints:
2927
3576
  @overload
2928
3577
  def serialize(self, /, compress: bool, level: int) -> bytes: ...
2929
3578
  def serialize(
2930
- self, /, compress: bool | None = ..., level: int | None = ...
3579
+ self, /, compress: (bool | None) = ..., level: (int | None) = ...
2931
3580
  ) -> bytes:
2932
3581
  """
2933
3582
  Alias for `encode()`.
2934
3583
 
2935
3584
  See `encode()` for details.
2936
3585
  """
3586
+ ...
2937
3587
 
2938
3588
  @classmethod
2939
3589
  def decode(cls, data: bytes, env: Environment) -> Expression:
@@ -2955,6 +3605,7 @@ class Constraints:
2955
3605
  DecodeError
2956
3606
  If decoding fails due to corruption or incompatibility.
2957
3607
  """
3608
+ ...
2958
3609
 
2959
3610
  @classmethod
2960
3611
  def deserialize(cls, data: bytes, env: Environment) -> Expression:
@@ -2963,12 +3614,13 @@ class Constraints:
2963
3614
 
2964
3615
  See `decode()` for usage.
2965
3616
  """
3617
+ ...
2966
3618
 
2967
3619
  @overload
2968
- def __iadd__(self, constraint: Constraint, /): ...
3620
+ def __iadd__(self, constraint: Constraint, /) -> Self: ...
2969
3621
  @overload
2970
- def __iadd__(self, constraint: tuple[Constraint, str], /): ...
2971
- def __iadd__(self, constraint: Constraint | tuple[Constraint, str], /):
3622
+ def __iadd__(self, constraint: tuple[Constraint, str], /) -> Self: ...
3623
+ def __iadd__(self, constraint: (Constraint | tuple[Constraint, str]), /) -> Self:
2972
3624
  """
2973
3625
  In-place constraint addition using `+=`.
2974
3626
 
@@ -2987,9 +3639,22 @@ class Constraints:
2987
3639
  TypeError
2988
3640
  If the value is not a `Constraint` or valid symbolic comparison.
2989
3641
  """
3642
+ ...
2990
3643
 
2991
- def __eq__(self, other: Constraints, /) -> bool: ... # type: ignore
3644
+ def __eq__(self, other: Constraints, /) -> bool: ...
3645
+ def __str__(self, /) -> str: ...
3646
+ def __repr__(self, /) -> str: ...
2992
3647
  def __getitem__(self, item: int, /) -> Constraint: ...
3648
+ def __len__(self, /) -> int:
3649
+ """
3650
+ Get the number of constraints.
3651
+
3652
+ Returns
3653
+ -------
3654
+ int
3655
+ The number of constraints associated with this `Constraints` object.
3656
+ """
3657
+ ...
2993
3658
 
2994
3659
  __all__ = [
2995
3660
  "Bounds",
@@ -3013,5 +3678,6 @@ __all__ = [
3013
3678
  "Variable",
3014
3679
  "Vtype",
3015
3680
  "errors",
3681
+ "transformations",
3016
3682
  "translator",
3017
3683
  ]