Classical Solvers
Classical solvers tackle a QUBO Instance entirely on CPU, without going through the quantum pipeline. Each one is a standalone function under solving, callable directly with the instance and its own set of keyword arguments β no Solver/SolverConfig wiring required. They differ in the trade-off between solution quality, runtime, and whether they need a starting point.
Without an initial solution
Section titled βWithout an initial solutionβThese solvers start from scratch:
solving.cplex.solveβ exact MIP solve via IBM CPLEX.solving.random_sampling.solveβ uniform random sampling baseline.
With an initial solution
Section titled βWith an initial solutionβThese solvers take a starting point. Because they accept a starting point, they can also be used to refine a previous solution β for example, post-processing a quantum solver's output:
solving.tabu_search.solveβ neighborhood search with a tabu memory. See example below.solving.simulated_annealing.solveβ stochastic temperature-cooling search.solving.iterative_bitflip_local_search.solveβ greedy local search that iteratively flips bits until no single flip improves the solution.
Example: Tabu Search
Section titled βExample: Tabu Searchβfrom qubosolver import Instance, solving, matrix, bitstrings, torch_rng, analysis
instance = Instance( matrix.tensor( [ [-2.0, 1.0, 0.0, 1.5, 0.0], [1.0, -1.5, 1.0, 0.0, 0.5], [0.0, 1.0, -2.0, 1.0, 1.0], [1.5, 0.0, 1.0, -1.0, 0.5], [0.0, 0.5, 1.0, 0.5, -1.5], ] ))
starts = bitstrings.rand(5, instance.size, rng=torch_rng(15))solution = solving.tabu_search.solve(instance, starts=starts, time_limit=10.0)
print("Tabu Search solution:")print(analysis.to_dataframe([solution]))Tabu Search solution: labels bitstrings costs counts probs0 0 10100 -4.0 5 1.0Example: refining a quantum solution
Section titled βExample: refining a quantum solutionβRun the quantum pipeline (see quantum solving), then try local bitflips for refinement:
from qubosolver import ( Instance, Solution, LocalEmulator, embedding, drive_shaping, solving, matrix, analysis,)import qoolqit
instance = Instance( matrix.tensor( [ [-2.0, 1.0, 0.0, 1.5, 0.0], [1.0, -1.5, 1.0, 0.0, 0.5], [0.0, 1.0, -2.0, 1.0, 1.0], [1.5, 0.0, 1.0, -1.0, 0.5], [0.0, 0.5, 1.0, 0.5, -1.5], ] ))device = qoolqit.AnalogDeviceWithDMM()backend = LocalEmulator()
register = embedding.blade.embed_for_device(instance, device)drive = drive_shaping.proportional_diagonal.build_drive(instance, register, device=device, dmm=True)program = solving.analog_quantum_sampling.compile(register, drive, device)job = backend.run(program)quantum_solution = Solution.from_results(job.results(), instance)
print("Quantum solution:")print(analysis.to_dataframe([quantum_solution]))
# Refine the quantum solution classically.refined_solution = solving.iterative_bitflip_local_search.solve(instance, starts=quantum_solution)
print("Refined solution:")print(analysis.to_dataframe([refined_solution]))Quantum solution: labels bitstrings costs counts probs0 0 10100 -4.0 380 0.3801 0 10101 -3.5 170 0.1702 0 10001 -3.5 330 0.3303 0 01010 -2.5 4 0.0044 0 01001 -2.0 32 0.0325 0 11001 -2.0 70 0.0706 0 01011 -2.0 7 0.0077 0 10000 -2.0 5 0.0058 0 00001 -1.5 1 0.0019 0 10011 -0.5 1 0.001Refined solution: labels bitstrings costs counts probs0 0 10100 -4.0 555 0.5551 0 10001 -3.5 401 0.4012 0 01010 -2.5 12 0.0123 0 01001 -2.0 32 0.032For full parameter details, see the classical solvers API reference.
The Solver shortcut
Section titled βThe Solver shortcutβFor the common case, SolverConfig and Solver wrap solver selection through ClassicalSolvingConfig, and (optionally) the initial-solution sampling into a single call:
from qubosolver import ( Instance, Solver, SolverConfig, ClassicalSolvingConfig, matrix, analysis,)from dataclasses import asdictimport pprint
instance = Instance( matrix.tensor( [ [-2.0, 1.0, 0.0, 1.5, 0.0], [1.0, -1.5, 1.0, 0.0, 0.5], [0.0, 1.0, -2.0, 1.0, 1.0], [1.5, 0.0, 1.0, -1.0, 0.5], [0.0, 0.5, 1.0, 0.5, -1.5], ] ))
classical_config = ClassicalSolvingConfig( algorithm="tabu_search", # algorithm="simulated_annealing", # algorithm="cplex",)solver_config = SolverConfig(solving=classical_config)solver = Solver(instance, solver_config)solution = solver.solve()
print(pprint.pformat(asdict(solver_config.solving)))print()print(analysis.to_dataframe([solution])){'algorithm': 'tabu_search', 'max_bitstrings': 1, 'max_iter': 100, 'time_limit': inf}
labels bitstrings costs counts probs0 0 10100 -4.0 1 1.0