Skip to content
Pasqal Documentation

SolverConfig – Solver Configuration Reference

The SolverConfig class defines how a QUBO problem should be solved — specifying whether to use a quantum or classical approach, which backend to run on, and additional execution parameters.

This configuration is passed into any solver (e.g., QuboSolver) and guides its behavior. Note that SolverConfig uses three other configuration objects: EmbeddingConfig, ClassicalConfig and DriveShapingConfig. Besides ClassicalConfig, the other configurations represents different parts of the solver when using a quantum approach:


Bases: Config

A SolverConfig instance defines how a QUBO problem should be solved. We specify whether to use a quantum or classical approach, which backend to run on, and additional execution parameters.

ATTRIBUTE DESCRIPTION
config_name

The name of the current configuration. Defaults to ''.

TYPE: str

use_quantum

Whether to solve using a quantum approach (True) or a classical approach (False). Defaults to False.

TYPE: bool

embedding

Embedding part configuration of the solver.

TYPE: EmbeddingConfig

drive_shaping

Drive-shaping part configuration of the solver.

TYPE: DriveShapingConfig

classical

Classical part configuration of the solver.

TYPE: ClassicalConfig

backend

backend for running quantum programs. Note that parameters such as dt are directly set when creating LocalEmulator | RemoteEmulator | QPU, hence they are deprecated compared to previous qubo-solver versions. Also the number of shots is set there as well. Defaults to a LocalEmulator using qutip.

TYPE: LocalEmulator | RemoteEmulator | QPU

device

The quantum device specification. Defaults to DigitalAnalogDevice.

TYPE: Device

do_postprocessing

Whether we apply post-processing (True) or not (False). Defaults to True.

TYPE: bool

do_preprocessing

Whether we apply pre-processing (True) or not (False). Defaults to True.

TYPE: bool

activate_trivial_solutions

Whether calculate trivial solutions (True) or not (False). Defaults to True.

TYPE: bool

decompose

which decomposition configuration to use when solving large QUBOs. Defaults to None, i.e. no decomposition is applied.

TYPE: DecompositionConfig | None

Create an instance based on entries of other configs.

Note that if any of the keywords ("embedding", "drive_shaping", "classical") are present in kwargs, the values are taken directly.

RETURNS DESCRIPTION
SolverConfig

An instance from values.

TYPE: SolverConfig

Source code in qubosolver/config.py
@classmethod
def from_kwargs(cls, **kwargs: dict) -> SolverConfig:
"""Create an instance based on entries of other configs.
Note that if any of the keywords
("embedding", "drive_shaping", "classical")
are present in kwargs, the values are taken directly.
Returns:
SolverConfig: An instance from values.
"""
# Extract fields from pydantic BaseModel
embedding_fields = {k: v for k, v in kwargs.items() if k in EmbeddingConfig.model_fields}
drive_shaping_fields = {
k: v for k, v in kwargs.items() if k in DriveShapingConfig.model_fields
}
classical_fields = {k: v for k, v in kwargs.items() if k in ClassicalConfig.model_fields}
decompose_fields = {
k: v for k, v in kwargs.items() if k in DecompositionConfig.model_fields
}
solver_fields = {
k: v
for k, v in kwargs.items()
if k in cls.model_fields
and k not in ("embedding", "drive_shaping", "classical", "decompose")
}
decompose = kwargs["decompose"] if "decompose" in kwargs else None
if decompose_fields:
decompose = DecompositionConfig(**decompose_fields)
return cls(
embedding=(
EmbeddingConfig(**embedding_fields)
if "embedding" not in kwargs
else kwargs["embedding"]
),
drive_shaping=(
DriveShapingConfig(**drive_shaping_fields)
if "drive_shaping" not in kwargs
else kwargs["drive_shaping"]
),
classical=(
ClassicalConfig(**classical_fields)
if "classical" not in kwargs
else kwargs["classical"]
),
decompose=decompose,
**solver_fields,
)

Print specs.

Source code in qubosolver/config.py
def print_specs(self) -> None:
"""Print specs."""
print(self.specs())

Return the specs of the SolverConfig, that is all attributes.

RETURNS DESCRIPTION
dict

Dictionary of specs key-values.

TYPE: str

Source code in qubosolver/config.py
def specs(self) -> str:
"""Return the specs of the `SolverConfig`, that is all attributes.
Returns:
dict: Dictionary of specs key-values.
"""
return "\n".join(
f"{k}: ''" if v == "" else f"{k}: {v}" for k, v in self.model_dump().items()
)

When solving with a quantum approach, we need to define an embedding method, that is how we define the geometry (register) of atoms based on the QUBO instance and compatibility with a device. The embedding configuration part (the embedding field of SolverConfig) is divided into several attributes that concerns the embedding_method chosen (BLaDE or Greedy, for which a prefix enables defning to which method they belong):

Bases: Config

A EmbeddingConfig instance defines the embedding part of a SolverConfig.

ATTRIBUTE DESCRIPTION
embedding_method

The type of embedding method used to place atoms on the register according to the QUBO problem. Defaults to EmbedderType.GREEDY.

TYPE: str | EmbedderType | type[BaseEmbedder]

greedy_layout

Layout type for the greedy embedder method. Defaults to LayoutType.TRIANGULAR.

TYPE: LayoutType | str

greedy_traps

The number of traps on the register. Defaults to pulser.DigitalAnalogDevice.min_layout_traps.

TYPE: int

greedy_spacing

The minimum distance between atoms. Defaults to pulser.PulserDigitalAnalogDevice.min_atom_distance.

TYPE: float

greedy_density

The estimated density of the QUBO matrix. Defaults to None.

TYPE: float

blade_steps_per_round

The number of steps for each layer of dimension for BLaDE. Defaults to 200.

TYPE: int

blade_starting_positions

The starting parameters according to the specified dimensions. Defaults to None.

TYPE: Tensor | None

blade_dimensions

A list of dimension degrees to explore one after the other (default is [5, 4, 3, 2, 2, 2]).

TYPE: list[int]

draw_steps

Show generated graph at each step of the optimization. Defaults to False.

TYPE: bool

animation_save_path

If provided, path to save animation. Defaults to None.

TYPE: str | None

Quantum devices can be programmed by specifying a Drive. A program in the Rydberg analog model is defined as a time-dependent drive Hamiltonian that is imposed on the qubits. The drive shaping configuration part (the drive_shaping field of SolverConfig) is set via the DriveShapingConfig class, and defines how the drive parameters are constructed (in an adiabatic fashion, via bayesian optimization, ...). Note, for parameters concerning exclusively the optimized drive shaping method (bayesian optimization), an optimized_ prefix is present.

Bases: Config

A DriveShapingConfig instance defines the drive shaping part of a SolverConfig.

ATTRIBUTE DESCRIPTION
drive_shaping_method

Drive shaping method used. Defauts to DriveType.ADIABATIC.

TYPE: str | DriveType | type[BaseDriveShaper]

dmm

Whether to use a detuning map when applying drive shaping or not. This adds WeightedDetuning with a Constant Waveform. Defaults to True, which applies DMM.

TYPE: bool

optimized_re_execute_opt_drive

Whether to re-run the optimal drive sequence after optimization. Defaults to False.

TYPE: bool

optimized_n_calls

Number of calls for the optimization process. Defaults to 20. Note the optimizer accepts a minimal value of 12.

TYPE: int

optimized_initial_omega_parameters

Default initial omega parameters for the drive. Defaults to Omega = (5, 10, 5).

TYPE: List[float]

optimized_initial_detuning_parameters

Default initial detuning parameters for the drive. Defaults to delta = (-10, 0, 10).

TYPE: List[float]

optimized_custom_qubo_cost

Apply a different qubo cost evaluation than the default QUBO evaluation defined in qubosolver/pipeline/drive.py:OptimizedDriveShaper.compute_qubo_cost. Must be defined as: def optimized_custom_qubo_cost(bitstring: str, QUBO: torch.Tensor) -> float. Defaults to None, meaning we use the default QUBO evaluation.

TYPE: Callable[[str, Tensor], float]

optimized_custom_objective_fn

For bayesian optimization, one can change the output of qubosolver/pipeline/drive.py:OptimizedDriveShaper.run_simulation to optimize differently. Instead of using the best cost out of the samples, one can change the objective for an average, or any function out of the form cost_eval = optimized_custom_objective_fn(bitstrings, counts, probabilities, costs, best_cost, best_bitstring) Defaults to None, which means we optimize using the best cost out of the samples.

TYPE: Callable[[list, list, list, list, float, str], float]

optimized_callback_objective

Apply a callback during bayesian optimization. Only accepts one input dictionary created during optimization d = {"x": x, "cost_eval": cost_eval} hence should be defined as: def callback_fn(d: dict) -> None: Defaults to None, which means no callback is applied.

TYPE: Callable[..., None]

For the classical solver, its configuration can be set via the ClassicalConfig class:

Bases: Config

A ClassicalConfig instance defines the classical part of a SolverConfig.

ATTRIBUTE DESCRIPTION
classical_solver_type

Classical solver type. Defaults to "simulated_annealing_tabu_search".

TYPE: str | ClassicalSolverType

cplex_maxtime

CPLEX maximum runtime. Defaults to 600s.

TYPE: float

cplex_log_path

CPLEX log path. Default to solver.log.

TYPE: str

max_iter

Maximum number of iterations to perform for simulated annealing or tabu search.

TYPE: int

max_bitstrings

Maximal number of bitstrings returned as solutions.

TYPE: int

sa_initial_temp

Starting temperature (controls exploration).

TYPE: float

sa_final_temp

Minimum temperature threshold for stopping.

TYPE: float

sa_cooling_rate

Cooling rate - should be slightly below 1 (e.g., 0.95–0.99).

TYPE: float

sa_seed

Random seed for reproducibility.

TYPE: int

sa_start

Optioanl initial bitstring of shape (n,).

TYPE: Tensor | None

sa_energy_tol

Energy tolerance for considering two solutions as equivalent.

TYPE: float

tabu_x0

The initial binary solution tensor of shape (n,).

TYPE: Tensor | None

tabu_tenure

Number of iterations a move (bit flip) remains tabu.

TYPE: int

tabu_max_no_improve

Maximum number of consecutive iterations without improvement before termination.

TYPE: int

Note, for parameters concerning exclusively simulated annealing, an sa_ prefix is present. Similarly for tabu search, the prefix is tabu_.

We can also apply preprocessing of the QUBO instance (to reduce it to another smaller instance) or postprocessing the solution after solving.

Field Type Description
do_postprocessing bool Whether we apply post-processing (True) or not (False).
do_preprocessing bool Whether we apply pre-processing (True) or not (False).

The SolverConfig is designed in such way that all parameters have a default value which fulfilled the minimum required configuration to execute the necessary steps to solve a QUBO.

All the parameters are optional which allows for running SolverConfig without specifying any parameter:

from qubosolver.config import SolverConfig
from qubosolver.qubo_types import EmbedderType
config = SolverConfig()
print(config.specs())
config_name: '' use_quantum: False embedding: {'embedding_method':, 'draw_steps': False, 'animation_save_path': None, 'greedy_layout':>, 'greedy_traps': 1, 'greedy_spacing': 4.0, 'greedy_density': None} drive_shaping: {'drive_shaping_method':, 'dmm': True} classical: {'classical_solver_type': 'simulated_annealing_tabu_search', 'max_iter': 100, 'max_bitstrings': 1, 'sa_initial_temp': 10.0, 'sa_final_temp': 0.1, 'sa_cooling_rate': None, 'sa_seed': None, 'sa_start': None, 'sa_energy_tol': 0.0, 'tabu_x0': None, 'tabu_tenure': 7, 'tabu_max_no_improve': 20} backend:device: DigitalAnalogDevice do_postprocessing: False do_preprocessing: False activate_trivial_solutions: True decompose: NoneAlthough the default configuration is straightforward, all parameters can be modified by the user to better suit the specific QUBO instance. Below is an example of a configuration that uses a different embedder with customized parameters on a specific device:
from qubosolver import QUBOInstance
from qubosolver.config import SolverConfig, EmbeddingConfig
coefficients = [[0, 1, 2], [1, 0, 3], [2, 3, 0]]
instance = QUBOInstance(coefficients=coefficients)
embedding_config = EmbeddingConfig(embedding_method="greedy", greedy_traps=instance.size)
config = SolverConfig(
config_name="my_config",
use_quantum=True,
embedding = embedding_config,
)

Equivalently, one can instantiate a SolverConfig simply using the keyword arguments of the other configs via the SolverConfig.from_kwargs method:

from qubosolver import QUBOInstance
from qubosolver.config import SolverConfig
coefficients = [[0, 1, 2], [1, 0, 3], [2, 3, 0]]
instance = QUBOInstance(coefficients=coefficients)
config = SolverConfig.from_kwargs(
config_name="my_config",
use_quantum=True,
embedding_method="greedy",
greedy_traps=instance.size
)