Skip to content

Commit afa74d9

Browse files
authored
[API] Hiding pulser.Pulse and pulser.Register - breaking change (#92)
For the sake of pedagogy, we're trying to hide pulser as much as possible. With this PR, we remove all visible references to pulser.Pulse and pulser.Register, replacing them withour own ir.Pulse and ir.Register.
1 parent 2c32d08 commit afa74d9

10 files changed

Lines changed: 131 additions & 65 deletions

examples/tutorial 1a - Using a Quantum Device to Extract Machine-Learning Features - low-level.ipynb

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
"example_graph, example_register, example_pulse = compiled[64]\n",
132132
"\n",
133133
"# The molecule, as laid out on the Quantum Device.\n",
134-
"example_register.draw(blockade_radius=pl.AnalogDevice.min_atom_distance + 0.01)\n",
134+
"example_register.draw()\n",
135135
"\n",
136136
"# The laser pulse used to control its state evolution.\n",
137137
"example_pulse.draw()"
@@ -143,7 +143,7 @@
143143
"source": [
144144
"## Experimenting with registers and pulses\n",
145145
"\n",
146-
"You can, of course, adopt different registers or pulses, using the underlying Pulser Framework."
146+
"You may experiment with different pulses, by passing arguments to `compile_pulse`."
147147
]
148148
},
149149
{
@@ -152,24 +152,15 @@
152152
"metadata": {},
153153
"outputs": [],
154154
"source": [
155-
"import pulser\n",
156-
"\n",
157-
"example_register = pulser.Register({\"q0\": (0, 0)})\n",
158-
"example_pulse = pulser.Pulse.ArbitraryPhase(\n",
159-
" amplitude=pulser.waveforms.RampWaveform(duration=150, start=100, stop=300),\n",
160-
" phase=pulser.waveforms.ConstantWaveform(duration=150, value=15),\n",
161-
" post_phase_shift=5\n",
162-
")\n",
163-
"\n",
164-
"example_register.draw()\n",
165-
"example_pulse.draw()"
155+
"example_pulse = graphs_to_compile[0].compile_pulse(normalized_amplitude=0.1, normalized_duration=0.1) # arbitrary values\n",
156+
"example_pulse.draw()\n"
166157
]
167158
},
168159
{
169160
"cell_type": "markdown",
170161
"metadata": {},
171162
"source": [
172-
"For this, you'll probably want to take a look at [the documentation of Pulser](https://pulser.readthedocs.io/)."
163+
"You can experiment further, using arbitrary pulses and registers, but for this, you'll have to use the low-level Pulser framework, which goes beyond the scope of this tutorial. You may find further details on pulses and registers in [the documentation of Pulser](https://pulser.readthedocs.io/)."
173164
]
174165
},
175166
{
@@ -189,7 +180,7 @@
189180
"outputs": [],
190181
"source": [
191182
"from qek.data.processed_data import ProcessedData\n",
192-
"from qek.backends import QutipBackend\n",
183+
"from qek.target.backends import QutipBackend\n",
193184
"\n",
194185
"# In this tutorial, to make things faster, we'll only run the graphs that require 5 qubits or less.\n",
195186
"# If you wish to run more entries, feel free to increase this value.\n",
@@ -203,10 +194,10 @@
203194
"processed_dataset = []\n",
204195
"backend = QutipBackend(device=pl.AnalogDevice)\n",
205196
"for graph, register, pulse in tqdm(compiled):\n",
206-
" if len(register.qubits) > MAX_QUBITS:\n",
197+
" if len(register) > MAX_QUBITS:\n",
207198
" continue\n",
208199
" states = await backend.run(register=register, pulse=pulse)\n",
209-
" processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=pl.AnalogDevice, state_dict=states, target=graph.target))"
200+
" processed_dataset.append(ProcessedData.custom(register=register, pulse=pulse, device=pl.AnalogDevice, state_dict=states, target=graph.target))"
210201
]
211202
},
212203
{
@@ -241,7 +232,7 @@
241232
"HAVE_PASQAL_ACCOUNT = False # If you have a PASQAL Cloud account, fill in the details and set this to `True`.\n",
242233
"\n",
243234
"if HAVE_PASQAL_ACCOUNT: \n",
244-
" from qek.backends import RemoteQPUBackend\n",
235+
" from qek.target.backends import RemoteQPUBackend\n",
245236
" processed_dataset = []\n",
246237
"\n",
247238
" # Initialize connection\n",
@@ -281,7 +272,7 @@
281272
"\n",
282273
" # Send the work to the QPU and await the result\n",
283274
" states = await backend.run(register=register, pulse=pulse)\n",
284-
" processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=device, state_dict=states, target=graph.target))"
275+
" processed_dataset.append(ProcessedData.custom(register=register, pulse=pulse, device=device, state_dict=states, target=graph.target))"
285276
]
286277
},
287278
{

examples/tutorial 1b - Training SVM QEK - low-level - generic dataset.ipynb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
"outputs": [],
240240
"source": [
241241
"example_graph, example_register, example_pulse = compiled[53]\n",
242-
"example_register.draw(blockade_radius=pl.AnalogDevice.min_atom_distance + 0.01)\n",
242+
"example_register.draw()\n",
243243
"example_pulse.draw()"
244244
]
245245
},
@@ -268,7 +268,7 @@
268268
"outputs": [],
269269
"source": [
270270
"from qek.data.processed_data import ProcessedData\n",
271-
"from qek.backends import QutipBackend\n",
271+
"from qek.target.backends import QutipBackend\n",
272272
"\n",
273273
"# In this tutorial, to make things faster, we'll only run 5 qubits or less.\n",
274274
"# If you wish to run more entries, feel free to increase this value.\n",
@@ -282,10 +282,10 @@
282282
"processed_dataset = []\n",
283283
"executor = QutipBackend(device=pl.AnalogDevice)\n",
284284
"for graph, register, pulse in tqdm(compiled):\n",
285-
" if len(register.qubits) > MAX_QUBITS:\n",
285+
" if len(register) > MAX_QUBITS:\n",
286286
" continue\n",
287287
" states = await executor.run(register=register, pulse=pulse)\n",
288-
" processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=pl.AnalogDevice, state_dict=states, target=graph.target))"
288+
" processed_dataset.append(ProcessedData.custom(register=register, pulse=pulse, device=pl.AnalogDevice, state_dict=states, target=graph.target))"
289289
]
290290
},
291291
{
@@ -313,7 +313,7 @@
313313
"HAVE_PASQAL_ACCOUNT = False # If you have a PASQAL Cloud account, fill in the details and set this to `True`.\n",
314314
"\n",
315315
"if HAVE_PASQAL_ACCOUNT: \n",
316-
" from qek.backends import RemoteQPUBackend\n",
316+
" from qek.target.backends import RemoteQPUBackend\n",
317317
" processed_dataset = []\n",
318318
"\n",
319319
" # Initialize connection\n",
@@ -353,7 +353,7 @@
353353
"\n",
354354
" # Send the work to the QPU and await the result\n",
355355
" states = await executor.run(register=register, pulse=pulse)\n",
356-
" processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=device, state_dict=states, target=graph.target))"
356+
" processed_dataset.append(ProcessedData.custom(register=register, pulse=pulse, device=device, state_dict=states, target=graph.target))"
357357
]
358358
},
359359
{
@@ -565,7 +565,7 @@
565565
"name": "python",
566566
"nbconvert_exporter": "python",
567567
"pygments_lexer": "ipython3",
568-
"version": "3.10.15"
568+
"version": "3.12.3"
569569
}
570570
},
571571
"nbformat": 4,

qek/data/extractors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,9 @@ def compile(
310310
try:
311311
register = graph.compile_register()
312312
pulse = graph.compile_pulse()
313-
sequence = pl.Sequence(register=register, device=graph.device)
313+
sequence = pl.Sequence(register=register.register, device=graph.device)
314314
sequence.declare_channel("ising", "rydberg_global")
315-
sequence.add(pulse, "ising")
315+
sequence.add(pulse.pulse, "ising")
316316
except CompilationError as e:
317317
# In some cases, we produce graphs that pass `is_embeddable` but cannot be compiled.
318318
# It _looks_ like this is due to rounding errors. We're investigating this in issue #29,

qek/data/graphs.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import torch_geometric.utils as pyg_utils
2020
from rdkit.Chem import AllChem
2121

22+
from qek.target import targets
2223
from qek.shared.error import CompilationError
2324
from qek.shared._utils import graph_to_mol
2425

@@ -199,7 +200,7 @@ def is_embeddable(self) -> bool:
199200
SEQUENCE_DEFAULT_AMPLITUDE_RAD_PER_US = 1.0 * 2 * np.pi
200201
SEQUENCE_DEFAULT_DURATION_NS = 660
201202

202-
def compile_register(self) -> pl.Register:
203+
def compile_register(self) -> targets.Register:
203204
"""Create a Quantum Register based on a graph.
204205
205206
Returns:
@@ -225,13 +226,13 @@ def compile_register(self) -> pl.Register:
225226
pl.Sequence(register=reg, device=self.device)
226227
except ValueError as e:
227228
raise CompilationError(f"The graph is not compatible with {self.device}: {e}")
228-
return reg
229+
return targets.Register(device=self.device, register=reg)
229230

230231
def compile_pulse(
231232
self,
232233
normalized_amplitude: float | None = None,
233234
normalized_duration: float | None = None,
234-
) -> pl.Pulse:
235+
) -> targets.Pulse:
235236
"""Extract a Pulse for this graph.
236237
237238
A Pulse represents the laser applied to the atoms on the device.
@@ -290,7 +291,7 @@ def compile_pulse(
290291
detuning=pl.waveforms.RampWaveform(absolute_duration, 0, 0),
291292
phase=0.0,
292293
)
293-
return pulse
294+
return targets.Pulse(pulse)
294295

295296

296297
class MoleculeGraph(BaseGraph):

qek/data/processed_data.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from qek.data.graphs import EPSILON_RADIUS_UM
1515
from qek.shared._utils import make_sequence
16+
from qek.target import targets
1617

1718
logger = logging.getLogger(__name__)
1819

@@ -68,10 +69,10 @@ def __init__(
6869
self.target = target
6970

7071
@classmethod
71-
def from_register(
72+
def custom(
7273
cls,
73-
register: pl.Register,
74-
pulse: pl.Pulse,
74+
register: targets.Register,
75+
pulse: targets.Pulse,
7576
device: pl.devices.Device,
7677
state_dict: dict[str, int | np.int64],
7778
target: int | None,

qek/shared/_utils.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import numpy as np
99
import numpy.typing as npt
1010
import pulser
11-
from pulser import Pulse, Register
1211
from pulser.devices import Device
1312
from qek.shared.error import CompilationError
1413
import rdkit.Chem as Chem
1514

15+
from qek.target import targets
16+
1617

1718
def graph_to_mol(
1819
graph: nx.Graph,
@@ -32,11 +33,11 @@ def graph_to_mol(
3233
m = Chem.MolFromSmiles("")
3334
mw = Chem.RWMol(m)
3435
atom_index = {}
35-
for n, d in graph.nodes(data="x"):
36+
for n, d in graph.nodes(data="x"): # type: ignore
3637
d = np.asarray(d)
3738
idx_d: int = inverse_one_hot(d, dim=0)[0]
3839
atom_index[n] = mw.AddAtom(Chem.Atom(node_mapping[idx_d]))
39-
for a, b, d in graph.edges(data="edge_attr"):
40+
for a, b, d in graph.edges(data="edge_attr"): # type: ignore
4041
start = atom_index[a]
4142
end = atom_index[b]
4243
d = np.asarray(d)
@@ -64,7 +65,9 @@ def inverse_one_hot(array: npt.ArrayLike, dim: int) -> np.ndarray:
6465
return np.nonzero(tmp_array == 1.0)[dim]
6566

6667

67-
def make_sequence(device: Device, pulse: Pulse, register: Register) -> pulser.Sequence:
68+
def make_sequence(
69+
device: Device, pulse: targets.Pulse, register: targets.Register
70+
) -> pulser.Sequence:
6871
"""
6972
Build a sequence for a device from a pulse and a register.
7073
@@ -82,9 +85,9 @@ def make_sequence(device: Device, pulse: Pulse, register: Register) -> pulser.Se
8285
CompilationError if the pulse + register are not compatible with the device.
8386
"""
8487
try:
85-
sequence = pulser.Sequence(register=register, device=device)
88+
sequence = pulser.Sequence(register=register.register, device=device)
8689
sequence.declare_channel("ising", "rydberg_global")
87-
sequence.add(pulse, "ising")
90+
sequence.add(pulse.pulse, "ising")
8891
return sequence
8992
except ValueError as e:
9093
raise CompilationError(f"This pulse/register cannot be executed on the device: {e}")

qek/target/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Quantum compilation targets
3+
"""

0 commit comments

Comments
 (0)