Embedding workflow
Here is a breakdown of the current workflow implementation for the embedding of a QUBO matrix, using several embedding methods and parameters.
Using default configuration
Section titled “Using default configuration”The SolverConfig()
without argument has a default behavior (e.g. Solver Config page) that allows for embedding using the minimum configuration according to the device.
Code example
Section titled “Code example”import torch
from qubosolver.config import SolverConfig, EmbeddingConfig, BackendConfigfrom qubosolver.solver import QUBOInstance, QuboSolver
# define qubo matrixcoefficients = torch.tensor([[0, 1, 2], [1, 0, 3], [2, 3, 0]])
# Instantiate a QUBOInstance with coefficientsinstance = QUBOInstance(coefficients)
# define the solver with default configurationdefault_config = SolverConfig()solver = QuboSolver(instance, default_config)geometry = solver.embedding()
# draw the register# geometry.register.draw()
BLaDE config
Section titled “BLaDE config”The following configuration uses the BLaDE method with specific dimension layers and a number of steps per round (i.e. the number of iterations per layer). Starting positions are implicitely defined but it can be set here, as long as it matches the first dimension layer.
embedconfig = EmbeddingConfig(embedding_method="blade", blade_dimensions=[5, 4, 3, 2], blade_steps_per_round=300)blade_config = SolverConfig( use_quantum=True, embedding=embedconfig,)
solver = QuboSolver(instance, blade_config)geometry = solver.embedding()
# geometry.register.draw()
Greedy embedder config
Section titled “Greedy embedder config”The following uses the greedy embedding method on a triangular lattice layout with a number of traps equals to the size of the QUBO. It also allows working on a square lattice layout, as well as increasing the number of traps according to the device specifications.
from qoolqit._solvers.types import DeviceType
embedconfig = EmbeddingConfig(embedding_method="greedy", traps=instance.size, layout_greedy_embedder="triangular",)backend = BackendConfig(device=DeviceType.ANALOG_DEVICE)greedy_config = SolverConfig( use_quantum=True, embedding=embedconfig, backend_config = backend,)
solver = QuboSolver(instance, greedy_config)geometry = solver.embedding()
# geometry.register.draw()
Custom embedder config
Section titled “Custom embedder config”If one desires to develop his own embedding method, a subclass of qubosolver.pipeline.embedder.BaseEmbedder
should be implemented with a mandatory embed
method.
The embed
method def embed(self) -> qubosolver.pipeline.targets.Register
specify how the problem is mapped into a register of qubits when running using a quantum device. Let us show a simple example where each variable is mapped into a qubit lying on a horizontal line (with coordinates ).
import typingfrom qubosolver.pipeline.embedder import BaseEmbedderfrom qubosolver.pipeline.targets import Register as TargetRegisterfrom qubosolver.config import ( EmbeddingConfig, SolverConfig,)from pulser.register import Register as PulserRegister
class FixedEmbedder(BaseEmbedder):
@typing.no_type_check def embed(self) -> TargetRegister: qubits = {f"q{i}": (i,0) for i in range(self.instance.coefficients.shape[0])} register = PulserRegister(qubits) return TargetRegister(self.config.backend_config.device, register)
config = SolverConfig( use_quantum=True, embedding=EmbeddingConfig(embedding_method=FixedEmbedder),)