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