Skip to content
Pasqal Documentation

BLaDE

from qubosolver.algorithms.blade.blade import em_blade
from qubosolver.algorithms.blade._helpers import interaction_matrix_from_positions, normalized_distance
from pulser.devices import AnalogDevice
import numpy as np
from pulser import Register
import matplotlib.pyplot as plt
np.set_printoptions(precision=1, suppress=True)
np.random.seed(0)
device = AnalogDevice
%matplotlib inline
qubo = np.array(
[[ 0., 3., 13., 211., 49., 5., 12., 0., 0.],
[ 0., 0., 23., 0., 0., 4., 0., 63., 2.],
[ 0., 0., 0., 5., 0., 1., 0., 1., 0.],
[ 0., 0., 0., 0., 37., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0., 1., 34., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 35., 9., 34.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 1.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 70.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
positions = em_blade(qubo, device=device)
interactions = interaction_matrix_from_positions(positions, device=device)
interactions
normalized_distance(target=qubo, actual=interactions)

A feature of BLaDE is that it can go beyond 2 dimensions when evolving the positions. Higher dimensions allow to escape trapped situations, and lower dimensions allow to return to the often desired 2-dimensional space, since we need a register in 2 dimensions. To do so, we can set the parameter dimensions as a list of integers, from the starting number of dimensions up to the final one.

On each period of the algorithm, when evolving from a number of dimensions to another one, a specific number of steps is performed. This can also be tuned by setting the parameter steps_per_round.

Here is an example where we use fewer dimensions and a smaller number of steps per round than the default values of the function. As a result, it returns very quickly:

positions = em_blade(qubo, device=device, dimensions=[3,2,2], steps_per_round=50)
interactions = interaction_matrix_from_positions(positions, device=device)
interactions
normalized_distance(target=qubo, actual=interactions)
positions = em_blade(qubo, starting_positions=positions, device=device, dimensions=[2,4,4,2], steps_per_round=100)
interactions = interaction_matrix_from_positions(positions, device=device)
interactions
normalized_distance(target=qubo, actual=interactions)

By default, BLaDE applies forces to make each interaction as close as possible to the corresponding coefficient of the target matrix. However, there is sometimes no solution to meet all these objectives simulataneously. In such cases, it can be judicious to give up on some objectives, to better meet other ones. It allows the solution to have a smaller distance from the target overall.

For example, let us take the following target matrix:

qubo = np.array([
[0, 1, 0, 0, 1, 1],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
])
positions = em_blade(qubo, device=device)
interactions = interaction_matrix_from_positions(positions, device=device)
interactions
normalized_distance(target=qubo, actual=interactions)
def compute_max_distance_to_walk(x, _):
unit_dist = device.rydberg_blockade_radius(1)
return unit_dist * (1 - np.sin(x * np.pi / 2))
x = np.linspace(0, 1, 50)
y = [compute_max_distance_to_walk(x_val, None) for x_val in x]
plt.plot(x, y)
plt.xlabel('Steps progress fraction')
plt.ylabel('Maximum distance to walk')
plt.show()
positions = em_blade(qubo, device=device, compute_max_distance_to_walk=compute_max_distance_to_walk)
interactions = interaction_matrix_from_positions(positions, device=device)
interactions
normalized_distance(target=qubo, actual=interactions)

Real device have constraints on the coordinates: atoms can not get closer than a given distance, and they must all fit in a disk under a given radius. Since a QUBO matrix can be multiplied by any non zero factor and remains equivalent, distances can also be scaled. Thus, the actual constraint to satisfy is the ratio between the radius and the minimum pairwise distance. The device for this tutorial has the following ratio:

device.max_radial_distance / device.min_atom_distance
size = 25
qubo = np.diag(np.ones(size-1), k=1) + np.diag(np.ones(size-2), k=2)
qubo
positions = em_blade(qubo, device=device)
Register.from_coordinates(positions).draw()
positions = em_blade(qubo, device=device, enforce_min_max_dist_ratio=True)
interactions = interaction_matrix_from_positions(positions, device=device)
Register.from_coordinates(positions).draw()