Skip to content

Results are limited to the current section: Application solving tools

Product news

Instances and Solutions

Two classes sit at the center of qubosolver's API: Instance, the problem to solve, and Solution, the result a solver returns. Every solver in the package (qubosolver.solving) takes an Instance and produces a Solution.

Instance represents a single QUBO problem. It wraps the coefficient matrix QQ and exposes helpers to evaluate candidate solutions and inspect the problem.

  • Store the QUBO coefficient matrix (matrix) and its size (size, also available as len(instance)).
  • Evaluate a candidate bitstring's cost via instance.cost(bitstring).
  • Serialize to and from disk.
from qubosolver import Instance, matrix, bitstring
instance = Instance(matrix.tensor([[0, 1, 2], [1, 0, 3], [2, 3, 0]]))
solution = bitstring.from_string("101")
cost = instance.cost(solution)
print(f"Solution Cost: {cost}")
Solution Cost: 4.0
import tempfile
from pathlib import Path
file = Path(tempfile.mkdtemp()) / "qubo_instance.bin"
with file.open("wb") as f:
instance.save(f)
with file.open("rb") as f:
loaded_instance = Instance.load(f)
print(loaded_instance.matrix)
tensor([[0., 1., 2.],
[1., 0., 3.],
[2., 3., 0.]])

Some preprocessing steps in qubosolver.transforms (variable fixing, zeroing, negative bitflip) reduce a QUBO problem before solving it, and record what they did as Instance subclasses. Wrapping an Instance this way keeps the applied transform attached to the problem, so it can later be used to lift a solution of the reduced problem back to a solution of the original one:

from qubosolver import Instance, matrix, solving, transforms, analysis
instance = Instance(
matrix.tensor(
[
[10.0, 1.0, 1.0],
[1.0, -3.0, 2.0],
[1.0, 2.0, -1.0],
]
)
)
reduced_instance = transforms.variable_fixing.apply_recursively(instance)
reduced_solution = solving.brute_force.solve(reduced_instance)
solution = transforms.variable_fixing.lift(reduced_solution, reduced_instance)
print(f"Reduced size: {reduced_instance.size}, original size: {instance.size}")
print(f"Best bitstring: {solution[0].string}, cost: {solution[0].cost}")
print(analysis.to_dataframe([solution]))
Reduced size: 2, original size: 3
Best bitstring: 010, cost: -3.0
labels bitstrings costs counts probs
0 0 010 -3.0 1 1.0

Instance.load dispatches automatically to whichever subclass wrote the file, so a plain Instance.load(f) correctly restores a transformed instance produced by any of these transforms.

Solution represents a collection of candidate bitstrings for a QUBO problem, together with their costs, sample counts, and probabilities. Solvers return one Solution, sorted by ascending cost, so the best candidate is always first.

  • Store candidate bitstrings, their costs, counts, and probabilities.
  • Iterable: iterating (or indexing) a Solution yields Candidate objects, one per candidate.
  • Serialize to and from disk.
from qubosolver import Instance, Solution, matrix, solving
instance = Instance(matrix.tensor([[0, 1, 2], [1, 0, 3], [2, 3, 0]]))
solution = solving.brute_force.solve(instance)
# Best candidate first
best = solution[0]
print(f"Best bitstring: {best.string}, cost: {best.cost}")
# Iterate over every candidate
for candidate in solution:
print(candidate.string, candidate.cost, candidate.probability)
Best bitstring: 001, cost: 0.0
001 0.0 1.0
import tempfile
from pathlib import Path
from qubosolver import analysis
file = Path(tempfile.mkdtemp()) / "qubo_solution.bin"
with file.open("wb") as f:
solution.save(f)
with file.open("rb") as f:
loaded_solution = Solution.load(f)
print(analysis.to_dataframe([loaded_solution]))
labels bitstrings costs counts probs
0 0 001 0.0 1 1.0