Skip to content
Pasqal Documentation

Embedding workflow

Here is a breakdown of the current workflow implementation for the embedding of a QUBO matrix, using several embedding methods and parameters.

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.

import torch
from qubosolver.config import SolverConfig, EmbeddingConfig, BackendConfig
from qubosolver.solver import QUBOInstance, QuboSolver
# define qubo matrix
coefficients = torch.tensor([[0, 1, 2], [1, 0, 3], [2, 3, 0]])
# Instantiate a QUBOInstance with coefficients
instance = QUBOInstance(coefficients)
# define the solver with default configuration
default_config = SolverConfig()
solver = QuboSolver(instance, default_config)
geometry = solver.embedding()
# draw the register
# geometry.register.draw()

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()

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()

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 ii is mapped into a qubit lying on a horizontal line (with coordinates [i,0][i, 0]).

import typing
from qubosolver.pipeline.embedder import BaseEmbedder
from qubosolver.pipeline.targets import Register as TargetRegister
from 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),
)