Codi font per a qilisdk.utils.classical_solvers.base_solver

# Copyright 2026 Qilimanjaro Quantum Tech
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from collections.abc import Mapping

from qilisdk.core import Model
from qilisdk.core.result import Result
from qilisdk.core.variables import BaseVariable, Number, RealNumber
from qilisdk.settings import get_settings
from qilisdk.yaml import yaml


def _assert_real(number: complex) -> float:
    if isinstance(number, complex):
        if abs(number.imag) < get_settings().atol:
            return number.real
        raise ValueError("Complex Number encountered when expecting only real values to be present.")
    return number


def _variable_bounds(variable: BaseVariable) -> tuple[float, float]:
    lower, upper = variable.bounds
    lower = variable.domain.min() if lower is None else lower
    upper = variable.domain.max() if upper is None else upper
    return float(lower), float(upper)


@yaml.register_class
[documents] class ClassicalSolverResult(Result): """ The solution a :class:`ClassicalSolver` found for a model. Example: .. code-block:: python from qilisdk.core import Model from qilisdk.utils.classical_solvers import BruteForceSolver model = Model.random_ising(4) result = BruteForceSolver().solve(model) result.objective # the value of the objective at the solution result.sample # the value each variable takes in the solution result.results # the objective and every constraint, by label """ def __init__( self, results: Mapping[str, Number], sample: Mapping[BaseVariable, RealNumber], objective_label: str, ) -> None: """ Create a new classical solver result. Args: results (Mapping[str, Number]): the model's objective and constraints, evaluated at ``sample`` and keyed by their labels. sample (Mapping[BaseVariable, RealNumber]): the value each model variable takes in the solution. objective_label (str): the label the objective is stored under in ``results``, i.e. ``model.objective.label``. """ self._results = dict(results) self._sample = dict(sample) self._objective_label = objective_label @classmethod
[documents] def from_model(cls, model: Model, sample: Mapping[BaseVariable, RealNumber]) -> "ClassicalSolverResult": """ Build a result by evaluating a model at the solution a solver found. Args: model (Model): the model that was solved. sample (Mapping[BaseVariable, RealNumber]): the value each model variable takes in the solution. Returns: ClassicalSolverResult: the model evaluated at ``sample``, together with ``sample``. """ return cls(model.evaluate(sample), sample, model.objective.label)
@property
[documents] def results(self) -> dict[str, Number]: """The model's objective and constraints, evaluated at :attr:`sample` and keyed by label.""" return dict(self._results)
@property
[documents] def sample(self) -> dict[BaseVariable, RealNumber]: """The value each model variable takes in the solution.""" return dict(self._sample)
@property
[documents] def objective_label(self) -> str: """The label the objective is stored under in :attr:`results`.""" return self._objective_label
@property
[documents] def objective(self) -> Number: """The value of the model's objective at :attr:`sample`. Raises: KeyError: if the objective's label is absent from :attr:`results`. """ return self._results[self._objective_label]
@property
[documents] def constraints(self) -> dict[str, Number]: """The value of each of the model's constraints at :attr:`sample`, keyed by label. A constraint evaluates to zero when it is satisfied, and to its penalty otherwise. """ return {label: value for label, value in self._results.items() if label != self._objective_label}
def __repr__(self) -> str: """Return a formatted string representation for debugging.""" return f"ClassicalSolverResult(objective={self.objective}, sample={self._sample}, results={self._results})"
[documents] class ClassicalSolver: """Base class for classical solvers."""
[documents] def solve(self, model: Model) -> ClassicalSolverResult: """Solve the given model.""" raise NotImplementedError("ClassicalSolver is an abstract base class.")