|
84 | 84 | "cell_type": "markdown", |
85 | 85 | "metadata": {}, |
86 | 86 | "source": [ |
87 | | - "## Create a Pulser sequence\n", |
| 87 | + "## Compile a Register and a Pulse\n", |
88 | 88 | "\n", |
89 | | - "Once the embedding is found, we create a Pulser Sequence that can be interpreted by a Quantum Device.\n", |
| 89 | + "Once the embedding is found, we compile a Register (the position of atoms on the Quantum Device) and a Pulse (the lasers applied to these atoms).\n", |
90 | 90 | "\n", |
91 | | - "Not all graphs can be embedded on a given device. In this notebook, for the sake of simplicity, we simply discard graphs that cannot be trivially embedded. Future versions of this library may succeed at embedding more graphs." |
| 91 | + "Note that not all graphs can be embedded on a given device. In this notebook, for the sake of simplicity, we simply discard graphs that cannot be trivially embedded. Future versions of this library may succeed at embedding more graphs." |
92 | 92 | ] |
93 | 93 | }, |
94 | 94 | { |
|
97 | 97 | "metadata": {}, |
98 | 98 | "outputs": [], |
99 | 99 | "source": [ |
| 100 | + "from qek.shared.error import CompilationError\n", |
| 101 | + "\n", |
100 | 102 | "compiled = [] \n", |
101 | 103 | "\n", |
102 | 104 | "for graph in tqdm(graphs_to_compile):\n", |
103 | | - " sequence = None\n", |
104 | 105 | " try:\n", |
105 | | - " sequence = graph.compute_sequence()\n", |
106 | | - " except ValueError:\n", |
| 106 | + " register = graph.compile_register()\n", |
| 107 | + " pulse = graph.compile_pulse()\n", |
| 108 | + " except CompilationError:\n", |
107 | 109 | " # Let's just skip graphs that cannot be computed.\n", |
108 | | - " print(\"Sequence %s cannot be compiled for this device\" % (graph.id, ))\n", |
| 110 | + " print(\"Graph %s cannot be compiled for this device\" % (graph.id, ))\n", |
109 | 111 | " continue\n", |
110 | | - " if sequence is not None:\n", |
111 | | - " compiled.append((graph, sequence))\n", |
112 | | - "print(\"Compiled %s sequences\" % (len(compiled, )))" |
| 112 | + " compiled.append((graph, register, pulse))\n", |
| 113 | + "print(\"Compiled %s graphs into registers/pulses\" % (len(compiled, )))" |
113 | 114 | ] |
114 | 115 | }, |
115 | 116 | { |
116 | 117 | "cell_type": "markdown", |
117 | 118 | "metadata": {}, |
118 | 119 | "source": [ |
119 | | - "Let's take a look at some of these sequences." |
| 120 | + "Let's take a look at some of these registers and pulses." |
120 | 121 | ] |
121 | 122 | }, |
122 | 123 | { |
|
125 | 126 | "metadata": {}, |
126 | 127 | "outputs": [], |
127 | 128 | "source": [ |
128 | | - "example_graph, example_sequence = compiled[64]\n", |
| 129 | + "example_graph, example_register, example_pulse = compiled[64]\n", |
129 | 130 | "\n", |
130 | 131 | "# The molecule, as laid out on the Quantum Device.\n", |
131 | | - "example_sequence.register.draw(blockade_radius=pl.AnalogDevice.min_atom_distance + 0.01)\n", |
| 132 | + "example_register.draw(blockade_radius=pl.AnalogDevice.min_atom_distance + 0.01)\n", |
132 | 133 | "\n", |
133 | 134 | "# The laser pulse used to control its state evolution.\n", |
134 | | - "example_sequence.draw()" |
| 135 | + "example_pulse.draw()" |
135 | 136 | ] |
136 | 137 | }, |
137 | 138 | { |
|
151 | 152 | "source": [ |
152 | 153 | "import pulser\n", |
153 | 154 | "\n", |
154 | | - "alternative_sequence = pulser.Sequence( # A sequence of 0 pulses.\n", |
155 | | - " pulser.Register({\"q0\": (0, 0)}), # A single atom, called q0, at the center of the device.\n", |
156 | | - " pulser.AnalogDevice # A generic analog quantum computer.\n", |
157 | | - ")\n" |
| 155 | + "example_register = pulser.Register({\"q0\": (0, 0)})\n", |
| 156 | + "example_pulse = pulser.Pulse.ArbitraryPhase(\n", |
| 157 | + " amplitude=pulser.waveforms.RampWaveform(duration=150, start=100, stop=300),\n", |
| 158 | + " phase=pulser.waveforms.ConstantWaveform(duration=150, value=15),\n", |
| 159 | + " post_phase_shift=5\n", |
| 160 | + ")\n", |
| 161 | + "\n", |
| 162 | + "example_register.draw()\n", |
| 163 | + "example_pulse.draw()" |
158 | 164 | ] |
159 | 165 | }, |
160 | 166 | { |
|
180 | 186 | "metadata": {}, |
181 | 187 | "outputs": [], |
182 | 188 | "source": [ |
183 | | - "from pulser_simulation import QutipEmulator\n", |
184 | 189 | "from qek.data.dataset import ProcessedData\n", |
| 190 | + "from qek.backends import QutipBackend\n", |
185 | 191 | "\n", |
186 | 192 | "# In this tutorial, to make things faster, we'll only run the sequences that require 5 qubits or less.\n", |
187 | 193 | "# If you wish to run more entries, feel free to increase this value.\n", |
|
193 | 199 | "MAX_QUBITS = 5\n", |
194 | 200 | "\n", |
195 | 201 | "processed_dataset = []\n", |
196 | | - "for graph, sequence in tqdm(compiled):\n", |
197 | | - " if len(sequence.qubit_info) > MAX_QUBITS:\n", |
| 202 | + "executor = QutipBackend(device=pl.AnalogDevice)\n", |
| 203 | + "for graph, register, pulse in tqdm(compiled):\n", |
| 204 | + " if len(register.qubits) > MAX_QUBITS:\n", |
198 | 205 | " continue\n", |
199 | | - " simulator = QutipEmulator.from_sequence(sequence=sequence)\n", |
200 | | - " states = simulator.run().sample_final_state()\n", |
201 | | - " processed_dataset.append(ProcessedData(sequence=sequence, state_dict=states, target=graph.target))" |
| 206 | + " states = await executor.run(register=register, pulse=pulse)\n", |
| 207 | + " processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=pl.AnalogDevice, state_dict=states, target=graph.target))" |
202 | 208 | ] |
203 | 209 | }, |
204 | 210 | { |
|
233 | 239 | "HAVE_PASQAL_ACCOUNT = False # If you have a PASQAL Cloud account, fill in the details and set this to `True`.\n", |
234 | 240 | "\n", |
235 | 241 | "if HAVE_PASQAL_ACCOUNT: \n", |
| 242 | + " from qek.backends import RemoteQPUBackend\n", |
236 | 243 | " processed_dataset = []\n", |
237 | 244 | "\n", |
238 | 245 | " # Initialize connection\n", |
239 | | - " from pulser.json.abstract_repr.deserializer import deserialize_device\n", |
240 | | - " from pasqal_cloud import SDK\n", |
241 | 246 | "\n", |
242 | 247 | " my_project_id = \"your_project_id\"# Replace this value with your project_id on the PASQAL platform.\n", |
243 | 248 | " my_username = \"your_username\" # Replace this value with your username or email on the PASQAL platform.\n", |
|
246 | 251 | " # See the documentation of PASQAL Cloud for other ways to provide your password.\n", |
247 | 252 | "\n", |
248 | 253 | " # Initialize the cloud client\n", |
249 | | - " sdk = SDK(username=my_username, project_id=my_project_id, password=my_password)\n", |
| 254 | + " executor = RemoteQPUBackend(username=my_username, project_id=my_project_id, password=my_password)\n", |
250 | 255 | "\n", |
251 | | - " # Fetch the latest lists of QPUs\n", |
252 | | - " specs = sdk.get_device_specs_dict()\n", |
253 | | - " # We'll use \"Fresnel\", generally the recommended QPU on PASQAL Cloud as of this writing.\n", |
254 | | - " device = deserialize_device(specs[\"FRESNEL\"])\n", |
| 256 | + " # Fetch the specification of our QPU\n", |
| 257 | + " device = await executor.device()\n", |
255 | 258 | "\n", |
256 | 259 | " # As previously, create the list of graphs and embed them.\n", |
257 | 260 | " graphs_to_compile = []\n", |
258 | 261 | " for i, data in enumerate(tqdm(og_ptcfm)):\n", |
259 | | - " graph = qek_graphs.MoleculeGraph(data=data, device=device, id=i)\n", |
| 262 | + " graph = qek_graphs.PTCFMGraph(data=data, device=device, id=i)\n", |
260 | 263 | " graphs_to_compile.append(graph)\n", |
261 | 264 | "\n", |
262 | 265 | " compiled = []\n", |
263 | 266 | " for graph in tqdm(graphs_to_compile):\n", |
264 | 267 | " sequence = None\n", |
265 | 268 | " try:\n", |
266 | | - " sequence = graph.compute_sequence()\n", |
267 | | - " except ValueError:\n", |
| 269 | + " register = graph.compile_register()\n", |
| 270 | + " pulse = graph.compile_pulse()\n", |
| 271 | + " except CompilationError:\n", |
268 | 272 | " # Let's just skip graphs that cannot be computed.\n", |
269 | 273 | " print(\"Sequence %s cannot be compiled for this device\" % (graph.id, ))\n", |
270 | 274 | " continue\n", |
271 | | - " if sequence is not None:\n", |
272 | | - " compiled.append((graph, sequence))\n", |
| 275 | + " compiled.append((graph, register, pulse))\n", |
273 | 276 | "\n", |
274 | 277 | " # Now that the connection is initialized, we just have to send the work\n", |
275 | 278 | " # to the QPU and wait for the results.\n", |
276 | | - " for graph, sequence in tqdm(compiled):\n", |
277 | | - "\n", |
278 | | - " # Send the work to the QPU.\n", |
279 | | - " batch = sdk.create_batch(\n", |
280 | | - " # The sequence.\n", |
281 | | - " sequence.to_abstract_repr(),\n", |
282 | | - "\n", |
283 | | - " # Run each sequence 1000 times to refine results. Recall that quantum computations\n", |
284 | | - " # are probabilistic, so you need to run each sequence many times to progressively\n", |
285 | | - " # refine your probability distribution.\n", |
286 | | - " jobs=[{\"runs\": 1000}],\n", |
287 | | - "\n", |
288 | | - " # And wait for the results.\n", |
289 | | - " #\n", |
290 | | - " # WARNING\n", |
291 | | - " #\n", |
292 | | - " # # Wait lines\n", |
293 | | - " #\n", |
294 | | - " # As of this writing, the waiting line to access a QPU can be very long (typically\n", |
295 | | - " # several hours). Argument `wait=True` will stop your program until the batch has\n", |
296 | | - " # completed. You will have to determine whether that's what you want.\n", |
297 | | - " wait=True,\n", |
298 | | - " )\n", |
299 | | - "\n", |
300 | | - " # The sdk returns a single job.\n", |
301 | | - " job = batch.jobs[0]\n", |
302 | | - " assert job.status == \"DONE\"\n", |
303 | | - " states = job.result\n", |
304 | | - " processed_dataset.append(ProcessedData(sequence=sequence, state_dict=states, target=graph.target))" |
| 279 | + " for graph, register, pulse in tqdm(compiled):\n", |
| 280 | + "\n", |
| 281 | + " # Send the work to the QPU and await the result\n", |
| 282 | + " states = await executor.run(register=register, pulse=pulse)\n", |
| 283 | + " processed_dataset.append(ProcessedData.from_register(register=register, pulse=pulse, device=device, state_dict=states, target=graph.target))" |
305 | 284 | ] |
306 | 285 | }, |
307 | 286 | { |
|
345 | 324 | "cell_type": "markdown", |
346 | 325 | "metadata": {}, |
347 | 326 | "source": [ |
348 | | - "We can check the sequence for one of the samples:" |
| 327 | + "Let's take a look at one of our samples:" |
349 | 328 | ] |
350 | 329 | }, |
351 | 330 | { |
|
354 | 333 | "metadata": {}, |
355 | 334 | "outputs": [], |
356 | 335 | "source": [ |
357 | | - "dataset_example = processed_dataset[64]\n", |
358 | | - "dataset_example.draw_sequence()" |
| 336 | + "from qek.data.dataset import ProcessedData\n", |
| 337 | + "\n", |
| 338 | + "# The geometry we compiled from this graph for execution on the Quantum Device.\n", |
| 339 | + "dataset_example: ProcessedData = processed_dataset[64]\n", |
| 340 | + "dataset_example.draw_register()" |
359 | 341 | ] |
360 | 342 | }, |
361 | 343 | { |
|
364 | 346 | "metadata": {}, |
365 | 347 | "outputs": [], |
366 | 348 | "source": [ |
367 | | - "dataset_example.draw_register()" |
| 349 | + "# The laser pulses we used to drive the execution on the Quantum Device.\n", |
| 350 | + "dataset_example.draw_pulse()" |
368 | 351 | ] |
369 | 352 | }, |
370 | 353 | { |
|
0 commit comments