Código fuente para qilisdk.utils.classical_solvers.brute_force_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.
import itertools
from loguru import logger
from qilisdk.core import Model
from qilisdk.core.variables import BinaryVariable, Variable
from .base_solver import ClassicalSolver, ClassicalSolverResult, _assert_real
[documentos]
class BruteForceSolver(ClassicalSolver):
"""Classical solver that uses brute-force search.
Example:
.. code-block:: python
from qilisdk.core import Model
from qilisdk.utils.classical_solvers import BruteForceSolver
model = Model.knapsack(values=[5, 4], weights=[3, 2], max_weight=3)
result = BruteForceSolver().solve(model)
"""
[documentos]
def solve(self, model: Model) -> ClassicalSolverResult: # ruff: ignore[no-self-use]
"""Solve the given model by brute-force enumeration of all variable assignments.
Binary variables are assigned values from {0, 1}. Any other ``Variable`` is decomposed
via its encoding, all bit patterns are enumerated and decoded to their representable
values, so the search covers every value the encoding can express regardless of domain.
Args:
model: The ``Model`` instance to solve.
Returns:
ClassicalSolverResult: the results of the optimization, including the objective value and best solution.
Raises:
ValueError: if the model contains a variable that has no encoding (i.e. is not a
BinaryVariable or a bounded Variable).
"""
variables = model.variables()
domains = []
for v in variables:
if isinstance(v, BinaryVariable):
domains.append([0, 1])
elif isinstance(v, Variable):
n_bits = v.num_binary_equivalent()
seen = set()
vals = []
for bits_int in range(2**n_bits):
bits = [(bits_int >> b) & 1 for b in range(n_bits)]
val = v.evaluate({v: bits})
if val not in seen:
seen.add(val)
vals.append(val)
domains.append(vals)
else:
raise ValueError(f"Brute-force enumeration is not supported for variable {v} of domain {v.domain}.")
total_combinations = 1
for d in domains:
total_combinations *= len(d)
MAX_COMBINATIONS = 8192
if total_combinations > MAX_COMBINATIONS:
logger.warning(
"[ClassicalSolvers] Model has {} combinations, brute-force enumeration may take a long time.",
total_combinations,
)
best_sample = {}
best_objective_value = float("inf")
for values in itertools.product(*domains):
sample = dict(zip(variables, values))
results = model.evaluate(sample)
objective_value = _assert_real(results[model.objective.label])
penalty = sum(_assert_real(results[c.label]) for c in model.constraints)
if objective_value + penalty < best_objective_value:
best_objective_value = objective_value + penalty
best_sample = sample
return ClassicalSolverResult.from_model(model, best_sample)