Skip to content
Pasqal Documentation

Solvers

Bases: BaseSolver

Dispatcher that selects the appropriate solver (quantum or classical) based on the SolverConfig and delegates execution to it.

Source code in qubosolver/solver.py
def __init__(self, instance: QUBOInstance, config: SolverConfig | None = None):
super().__init__(instance, config)
self._solver: BaseSolver
if config is None:
self._solver = QuboSolverClassical(instance, self.config)
else:
if config.use_quantum:
self._solver = QuboSolverQuantum(instance, config)
else:
self._solver = QuboSolverClassical(instance, config)
self.n_fixed_variables_preprocessing = 0

QuboSolverClassical(instance, config=None)

Section titled “ QuboSolverClassical(instance, config=None) ”

Bases: BaseSolver

Classical solver for QUBO problems. This implementation delegates the classical solving task to the external classical solver module (e.g., CPLEX, D-Wave SA, or D-Wave Tabu), as selected via the SolverConfig.

After obtaining the raw solution, postprocessing (e.g., bit-flip local search) is applied.

Source code in qubosolver/solver.py
def __init__(self, instance: QUBOInstance, config: SolverConfig | None = None):
super().__init__(instance, config)
# Optionally, you could instantiate Fixtures here for postprocessing:
self.fixtures = Fixtures(self.instance, self.config)

QuboSolverQuantum(instance, config=None)

Section titled “ QuboSolverQuantum(instance, config=None) ”

Bases: BaseSolver

Quantum solver that orchestrates the solving of a QUBO problem using embedding, pulse shaping, and quantum execution pipelines.

Initialize the QuboSolver with the given problem and configuration.

PARAMETER DESCRIPTION
instance

The QUBO problem to solve.

TYPE: QUBOInstance

config

Solver settings including backend and device.

TYPE: SolverConfig DEFAULT: None

Source code in qubosolver/solver.py
def __init__(self, instance: QUBOInstance, config: SolverConfig | None = None):
"""
Initialize the QuboSolver with the given problem and configuration.
Args:
instance (QUBOInstance): The QUBO problem to solve.
config (SolverConfig): Solver settings including backend and device.
"""
super().__init__(instance, config or SolverConfig(use_quantum=True))
self._check_size_limit()
self.fixtures = Fixtures(self.instance, self.config)
self.backend = get_backend(self.config.backend_config)
self.embedder = get_embedder(self.instance, self.config, self.backend)
self.pulse_shaper = get_pulse_shaper(self.instance, self.config, self.backend)
self._register: Register | None = None
self._pulse: Pulse | None = None

Generate a physical embedding (register) for the QUBO variables.

RETURNS DESCRIPTION
Register

Atom layout suitable for quantum hardware.

TYPE: Register

Source code in qubosolver/solver.py
def embedding(self) -> Register:
"""
Generate a physical embedding (register) for the QUBO variables.
Returns:
Register: Atom layout suitable for quantum hardware.
"""
self.embedder.instance = self.instance
self._register = self.embedder.embed()
return self._register

Generate the pulse sequence based on the given embedding.

PARAMETER DESCRIPTION
embedding

The embedded register layout.

TYPE: Register

RETURNS DESCRIPTION
tuple

A tuple of - Pulse: Pulse schedule for quantum execution. - QUBOSolution: Initial solution of generated from pulse shaper

TYPE: tuple

Source code in qubosolver/solver.py
def pulse(self, embedding: Register) -> tuple:
"""
Generate the pulse sequence based on the given embedding.
Args:
embedding (Register): The embedded register layout.
Returns:
tuple:
A tuple of
- Pulse: Pulse schedule for quantum execution.
- QUBOSolution: Initial solution of generated from pulse shaper
"""
pulse, qubo_solution = self.pulse_shaper.generate(embedding, self.instance)
self._pulse = pulse
return pulse, qubo_solution

Execute the full quantum pipeline: preprocess, embed, pulse, execute, postprocess.

RETURNS DESCRIPTION
QUBOSolution

Final result after execution and postprocessing.

TYPE: QUBOSolution

Source code in qubosolver/solver.py
def solve(self) -> QUBOSolution:
"""
Execute the full quantum pipeline: preprocess, embed, pulse, execute, postprocess.
Returns:
QUBOSolution: Final result after execution and postprocessing.
"""
# 1) try trivial and verify size
trivial = self._trivial_solution()
if trivial is not None and self.config.activate_trivial_solutions:
return trivial
self._check_size_limit()
# 2) else delegate to quantum or classical solver
# Delegate the solving task to the appropriate classical solver using the factory
if self.config.do_preprocessing:
# Apply preprocessing and change the solved QUBO by the reduced one
self.fixtures.preprocess()
if (
self.fixtures.reduced_qubo.coefficients is not None
and len(self.fixtures.reduced_qubo.coefficients) > 0
):
self.instance = self.fixtures.reduced_qubo
self.n_fixed_variables_preprocessing = self.fixtures.n_fixed_variables
embedding = self.embedding()
pulse, qubo_solution = self.pulse(embedding)
bitstrings, counts, _, _ = (
qubo_solution.bitstrings,
qubo_solution.counts,
qubo_solution.probabilities,
qubo_solution.costs,
)
if (
len(bitstrings) == 0 and qubo_solution.counts is None
) or self.config.pulse_shaping.re_execute_opt_pulse:
bitstrings, counts = self.execute(pulse, embedding)
bitstring_strs = bitstrings
bitstrings_tensor = torch.tensor(
[list(map(int, bs)) for bs in bitstring_strs], dtype=torch.float32
)
if counts is None:
counts_tensor = torch.empty((0,), dtype=torch.int32)
elif isinstance(counts, dict) or isinstance(counts, Counter):
count_values = [counts.get(bs, 0) for bs in bitstring_strs]
counts_tensor = torch.tensor(count_values, dtype=torch.int32)
else:
counts_tensor = counts
solution = QUBOSolution(
bitstrings=bitstrings_tensor,
counts=counts_tensor,
costs=torch.Tensor(),
probabilities=None,
)
# Post-process fixations of the preprocessing and restore the original QUBO
if self.config.do_preprocessing:
solution = self.fixtures.post_process_fixation(solution)
self.instance = self.fixtures.instance
solution.costs = solution.compute_costs(self.instance)
solution.probabilities = solution.compute_probabilities()
solution.sort_by_cost()
if self.config.do_postprocessing:
solution = self.fixtures.postprocess(solution)
return solution