Skip to content

Commit 57f764a

Browse files
authored
[Documentation] Update docs qpu + adiabatic to use min_avg_amp (#75)
* update docs on qpu submissions * bump version * new adiabatic algo * reput as before adiabatic * new qoolqit dependency * use average of coefficients in adiabatic * change custom adiabatic
1 parent 8de5e85 commit 57f764a

File tree

8 files changed

+48
-12
lines changed

8 files changed

+48
-12
lines changed

docs/content/backend.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ if PASSWORD is not None:
109109
We can also target a remote QPU as follows:
110110

111111
```python exec="on" source="material-block"
112+
import qoolqit
112113
from qubosolver.config import SolverConfig, PasqalCloud, QPU
113114
from pulser_pasqal.backends import EmuFreeBackendV2, EmuMPSBackend
114115

@@ -122,9 +123,11 @@ if PASSWORD is not None:
122123
password=PASSWORD,
123124
project_id=PROJECT_ID,
124125
)
126+
# specify the QPU device
127+
device = qoolqit.devices.Device(pulser_device=connection.fetch_available_devices()["FRESNEL"])
125128
config = SolverConfig(
126129
use_quantum=True,
127-
backend = QPU(connection=connection, runs=500),
130+
backend = QPU(connection=connection, runs=500), device=device,
128131
)
129132

130133
```

docs/content/driveshaping/adiabatic.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ For the Adiabatic Drive, the values of the amplitude $\Omega$ (Rabi frequency) a
4040

4141
- Amplitude: has a sine-like shape, starting from $0$ and ending in $0$, with a maximum value being the maximum value among the off-diagonal terms of the QUBO matrix:
4242

43-
$$\Omega_{max} = max(Q_{off})$$
43+
$$\Omega_{max} = mean(Q_{ij, i != j})$$
4444

45-
If $\Omega_{max}$ reaches a value above the maximum amplitude allowed by the device, it uses the `max_amp` value taken from the device specs as $\Omega_{max}$.
45+
If $\Omega_{max}$ reaches a value above the maximum amplitude allowed by the device, it uses the `max_amp` value taken from the device specs as $\Omega_{max}$. It it is below the `min_avg_amp` value from the specs, we use `min_avg_amp`.
4646

4747
- Detuning: starts from a negative value $\delta_0$ that is the minimum value among the diagonal terms of the QUBO matrix (since diagonal terms are either negative or $0$), reaches $0$ and ends in a positive final value $\delta_f$, following a linear behavior:
4848

docs/content/driveshaping/custom.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class LimitedAdiabaticDriveShaper(BaseDriveShaper):
5353

5454
rydberg_global = self.device._device.channels["rydberg_global"]
5555

56+
mean_coeffs = torch.mean(off_diag).item()
5657
Omega = min(
57-
torch.max(off_diag).item(),
58+
max(mean_coeffs, rydberg_global.min_avg_amp),
5859
rydberg_global.max_amp - 1e-9,
5960
)
6061

docs/tutorial/00-a-tour-of-qubo.ipynb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
"outputs": [],
371371
"source": [
372372
"from qubosolver.config import QPU, PasqalCloud\n",
373+
"import qoolqit\n",
373374
"\n",
374375
"# Replace with your username, project id and password on the Pasqal Cloud.\n",
375376
"USERNAME=\"username\"\n",
@@ -382,7 +383,9 @@
382383
" password=PASSWORD,\n",
383384
" project_id=PROJECT_ID,\n",
384385
" )\n",
385-
" config = SolverConfig(use_quantum=True, backend=QPU(connection=connection))\n",
386+
" # specify the QPU device\n",
387+
" device = qoolqit.devices.Device(pulser_device=connection.fetch_available_devices()[\"FRESNEL\"])\n",
388+
" config = SolverConfig(use_quantum=True, backend=QPU(connection=connection), device=device)\n",
386389
" # Run the solver\n",
387390
" solver = QuboSolver(instance, config)\n",
388391
" solutions = solver.solve()\n",

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
66
name = "qubo-solver"
77
description = "A Quadratic Unconstrained Binary Optimization (QUBO) solver library using quantum and classical approaches."
88
readme = "README.md"
9-
version = "0.4.0"
9+
version = "0.4.1"
1010
requires-python = ">=3.10,<3.13"
1111
license = { text = "MIT-derived" }
1212
keywords = ["quantum", "qubo", "solver", "optimization", "combinatorial"]
@@ -42,7 +42,7 @@ dependencies = [
4242
"PyMaxflow",
4343
"cplex",
4444
"pydantic>=2",
45-
"qoolqit[solvers]==0.3.0",
45+
"qoolqit[solvers]==0.3.1",
4646
"shapely",
4747
]
4848

qubosolver/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ def _set_greedy_traps_greedy_spacing_from_device(self) -> SolverConfig:
450450
if hasattr(device, "min_layout_traps"):
451451
if self.embedding.greedy_traps < device.min_layout_traps:
452452
self.embedding = self.embedding.model_copy(
453-
update={"greedy_traps": device.value.min_layout_traps}
453+
update={"greedy_traps": device.min_layout_traps}
454454
)
455455
if hasattr(device, "min_atom_distance"):
456456
greedy_spacing_device = float(device.min_atom_distance)

qubosolver/pipeline/basesolver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def preprocess(self) -> None:
214214
if (
215215
self.fixtures.reduced_qubo.coefficients is not None
216216
and len(self.fixtures.reduced_qubo.coefficients) > 0
217+
and self.fixtures.n_fixed_variables < self.instance.size # type:ignore[operator]
217218
):
218219

219220
self.instance = self.fixtures.reduced_qubo

qubosolver/pipeline/drive.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,31 @@ class AdiabaticDriveShaper(BaseDriveShaper):
8383
A Standard Adiabatic Drive shaper.
8484
"""
8585

86+
def _find_max_interaction_coeff_vectorized(self) -> float:
87+
"""
88+
Finds the maximum q_ij such that q_ii + q_jj + q_ij + q_ji < 0,
89+
using vectorized operations.
90+
91+
Returns:
92+
float: The maximum q_ij value found, inf if no value satisfies
93+
the confition.
94+
"""
95+
Q = self.instance.coefficients
96+
n = Q.shape[0]
97+
i_indices, j_indices = torch.meshgrid(torch.arange(n), torch.arange(n), indexing="ij")
98+
q_ii = Q[i_indices, i_indices]
99+
q_jj = Q[j_indices, j_indices]
100+
101+
q_ij = Q[i_indices, j_indices]
102+
q_ji = Q[j_indices, i_indices]
103+
104+
condition_mask = (q_ii + q_jj + q_ij + q_ji) < 0
105+
valid_q_ij_values = Q[condition_mask]
106+
if valid_q_ij_values.numel() == 0:
107+
return float("inf")
108+
109+
return float(torch.max(valid_q_ij_values).cpu().item())
110+
86111
def generate(
87112
self,
88113
register: Register,
@@ -113,22 +138,25 @@ def generate(
113138
max_node_weight = max(weights_list)
114139
norm_weights_list = [(1 - (w / max_node_weight)) / TIME for w in weights_list]
115140

141+
rydberg_global = self.device._device.channels["rydberg_global"]
142+
116143
off_diag = QUBO[
117144
~torch.eye(QUBO.shape[0], dtype=torch.bool)
118145
] # Selecting off-diagonal terms of the Qubo with a mask
119146

120-
rydberg_global = self.device._device.channels["rydberg_global"]
121-
147+
mean_coeffs = torch.mean(off_diag).item()
122148
Omega = min(
123-
torch.max(off_diag).item(),
149+
max(mean_coeffs, rydberg_global.min_avg_amp),
124150
rydberg_global.max_amp - 1e-9,
125151
)
126152

127153
delta_0 = torch.min(torch.diag(QUBO)).item()
128154
delta_f = -delta_0
129155

130156
# enforces AnalogDevice max sequence duration since Digital's has no max duration
131-
max_seq_duration = AnalogDevice.max_sequence_duration
157+
max_seq_duration = (
158+
self.device._device.max_sequence_duration or AnalogDevice.max_sequence_duration
159+
)
132160
assert max_seq_duration is not None
133161

134162
max_seq_duration /= TIME

0 commit comments

Comments
 (0)