|
8 | 8 | from uuid import UUID |
9 | 9 | import time |
10 | 10 | from typing import Any, Callable, Generator, Generic, Sequence, TypeVar, cast |
11 | | -import emu_mps |
12 | 11 | from numpy.typing import NDArray |
13 | 12 | from pasqal_cloud import SDK |
14 | 13 | from pasqal_cloud.batch import Batch |
|
17 | 16 | from pasqal_cloud.utils.filters import BatchFilters |
18 | 17 | from pathlib import Path |
19 | 18 | import numpy as np |
| 19 | +import os |
20 | 20 | import pulser as pl |
21 | 21 | from pulser.devices import Device |
22 | 22 | from pulser.json.abstract_repr.deserializer import deserialize_device |
@@ -438,97 +438,102 @@ def run(self, max_qubits: int = 8) -> SyncExtracted: |
438 | 438 | return result |
439 | 439 |
|
440 | 440 |
|
441 | | -class EmuMPSExtractor(BaseExtractor[GraphType]): |
442 | | - """ |
443 | | - A Extractor that uses the emu-mps Emulator to run sequences compiled |
444 | | - from graphs. |
445 | | -
|
446 | | - Performance note: emulating a quantum device on a classical |
447 | | - computer requires considerable amount of resources, so this |
448 | | - Extractor may be slow or require too much memory. If should, |
449 | | - however, be faster than QutipExtractor in most cases. |
| 441 | +if os.name == "posix": |
| 442 | + # Any Unix including Linux and macOS |
450 | 443 |
|
451 | | - See also: |
452 | | - - QPUExtractor (run on a physical QPU) |
453 | | -
|
454 | | - Args: |
455 | | - path: Path to store the result of the run, for future uses. |
456 | | - To reload the result of a previous run, use `LoadExtractor`. |
457 | | - compiler: A graph compiler, in charge of converting graphs to Pulser Sequences, |
458 | | - the format that can be executed on a quantum device. |
459 | | - device: A device to use. For general experiments, the default |
460 | | - device `AnalogDevice` is a perfectly reasonable choice. |
461 | | - """ |
462 | | - |
463 | | - def __init__( |
464 | | - self, |
465 | | - compiler: BaseGraphCompiler[GraphType], |
466 | | - device: Device = pl.devices.AnalogDevice, |
467 | | - path: Path | None = None, |
468 | | - ): |
469 | | - super().__init__(device=device, compiler=compiler, path=path) |
470 | | - self.graphs: list[BaseGraph] |
471 | | - self.device = device |
| 444 | + import emu_mps |
472 | 445 |
|
473 | | - def run(self, max_qubits: int = 10, dt: int = 10) -> BaseExtracted: |
| 446 | + class EmuMPSExtractor(BaseExtractor[GraphType]): |
474 | 447 | """ |
475 | | - Run the compiled graphs. |
| 448 | + A Extractor that uses the emu-mps Emulator to run sequences compiled |
| 449 | + from graphs. |
476 | 450 |
|
477 | | - As emulating a quantum device is slow consumes resources and time exponential in the |
478 | | - number of qubits, for the sake of performance, we limit the number of qubits in the execution |
479 | | - of this extractor. |
480 | | -
|
481 | | - Args: |
482 | | - max_qubits: Skip any sequence that require strictly more than `max_qubits`. Defaults to 8. |
483 | | - dt: The duration of the simulation step, in us. Defaults to 10. |
484 | | -
|
485 | | - Returns: |
486 | | - Processed data for all the sequences that were executed. |
487 | | - """ |
488 | | - if len(self.sequences) == 0: |
489 | | - logger.warning("No sequences to run, did you forget to call compile()?") |
490 | | - return SyncExtracted(raw_data=[], targets=[], sequences=[], states=[]) |
| 451 | + Performance note: emulating a quantum device on a classical |
| 452 | + computer requires considerable amount of resources, so this |
| 453 | + Extractor may be slow or require too much memory. If should, |
| 454 | + however, be faster than QutipExtractor in most cases. |
491 | 455 |
|
492 | | - backend = emu_mps.MPSBackend() |
493 | | - raw_data = [] |
494 | | - targets: list[int] = [] |
495 | | - sequences = [] |
496 | | - states = [] |
497 | | - for compiled in self.sequences: |
498 | | - qubits_used = len(compiled.sequence.qubit_info) |
499 | | - if qubits_used > max_qubits: |
500 | | - logger.info( |
501 | | - "Graph %s exceeds the qubit limit specified in EmuMPSExtractor (%s > %s), skipping", |
502 | | - id, |
503 | | - qubits_used, |
504 | | - max_qubits, |
505 | | - ) |
506 | | - continue |
507 | | - logger.debug("Executing compiled graph # %s", id) |
| 456 | + See also: |
| 457 | + - QPUExtractor (run on a physical QPU) |
508 | 458 |
|
509 | | - # Configure observable. |
510 | | - cutoff_duration = int(ceil(compiled.sequence.get_duration() / dt) * dt) |
511 | | - observable = emu_mps.BitStrings(evaluation_times={cutoff_duration}) |
512 | | - config = emu_mps.MPSConfig(observables=[observable], dt=dt) |
513 | | - counter: dict[str, Any] = backend.run(compiled.sequence, config)[observable.name][ |
514 | | - cutoff_duration |
515 | | - ] |
516 | | - logger.debug("Execution of compiled graph # %s complete", id) |
517 | | - raw_data.append(compiled.graph) |
518 | | - if compiled.graph.target is not None: |
519 | | - targets.append(compiled.graph.target) |
520 | | - sequences.append(compiled.sequence) |
521 | | - states.append(counter) |
| 459 | + Args: |
| 460 | + path: Path to store the result of the run, for future uses. |
| 461 | + To reload the result of a previous run, use `LoadExtractor`. |
| 462 | + compiler: A graph compiler, in charge of converting graphs to Pulser Sequences, |
| 463 | + the format that can be executed on a quantum device. |
| 464 | + device: A device to use. For general experiments, the default |
| 465 | + device `AnalogDevice` is a perfectly reasonable choice. |
| 466 | + """ |
| 467 | + |
| 468 | + def __init__( |
| 469 | + self, |
| 470 | + compiler: BaseGraphCompiler[GraphType], |
| 471 | + device: Device = pl.devices.AnalogDevice, |
| 472 | + path: Path | None = None, |
| 473 | + ): |
| 474 | + super().__init__(device=device, compiler=compiler, path=path) |
| 475 | + self.graphs: list[BaseGraph] |
| 476 | + self.device = device |
| 477 | + |
| 478 | + def run(self, max_qubits: int = 10, dt: int = 10) -> BaseExtracted: |
| 479 | + """ |
| 480 | + Run the compiled graphs. |
| 481 | +
|
| 482 | + As emulating a quantum device is slow consumes resources and time exponential in the |
| 483 | + number of qubits, for the sake of performance, we limit the number of qubits in the execution |
| 484 | + of this extractor. |
| 485 | +
|
| 486 | + Args: |
| 487 | + max_qubits: Skip any sequence that require strictly more than `max_qubits`. Defaults to 8. |
| 488 | + dt: The duration of the simulation step, in us. Defaults to 10. |
| 489 | +
|
| 490 | + Returns: |
| 491 | + Processed data for all the sequences that were executed. |
| 492 | + """ |
| 493 | + if len(self.sequences) == 0: |
| 494 | + logger.warning("No sequences to run, did you forget to call compile()?") |
| 495 | + return SyncExtracted(raw_data=[], targets=[], sequences=[], states=[]) |
| 496 | + |
| 497 | + backend = emu_mps.MPSBackend() |
| 498 | + raw_data = [] |
| 499 | + targets: list[int] = [] |
| 500 | + sequences = [] |
| 501 | + states = [] |
| 502 | + for compiled in self.sequences: |
| 503 | + qubits_used = len(compiled.sequence.qubit_info) |
| 504 | + if qubits_used > max_qubits: |
| 505 | + logger.info( |
| 506 | + "Graph %s exceeds the qubit limit specified in EmuMPSExtractor (%s > %s), skipping", |
| 507 | + id, |
| 508 | + qubits_used, |
| 509 | + max_qubits, |
| 510 | + ) |
| 511 | + continue |
| 512 | + logger.debug("Executing compiled graph # %s", id) |
| 513 | + |
| 514 | + # Configure observable. |
| 515 | + cutoff_duration = int(ceil(compiled.sequence.get_duration() / dt) * dt) |
| 516 | + observable = emu_mps.BitStrings(evaluation_times={cutoff_duration}) |
| 517 | + config = emu_mps.MPSConfig(observables=[observable], dt=dt) |
| 518 | + counter: dict[str, Any] = backend.run(compiled.sequence, config)[observable.name][ |
| 519 | + cutoff_duration |
| 520 | + ] |
| 521 | + logger.debug("Execution of compiled graph # %s complete", id) |
| 522 | + raw_data.append(compiled.graph) |
| 523 | + if compiled.graph.target is not None: |
| 524 | + targets.append(compiled.graph.target) |
| 525 | + sequences.append(compiled.sequence) |
| 526 | + states.append(counter) |
522 | 527 |
|
523 | | - logger.debug("Emulation step complete, %s compiled graphs executed", len(raw_data)) |
| 528 | + logger.debug("Emulation step complete, %s compiled graphs executed", len(raw_data)) |
524 | 529 |
|
525 | | - result = SyncExtracted( |
526 | | - raw_data=raw_data, targets=targets, sequences=sequences, states=states |
527 | | - ) |
528 | | - logger.debug("Emulation step complete, %s compiled graphs executed", len(raw_data)) |
529 | | - if self.path is not None: |
530 | | - result.save_dataset(self.path) |
531 | | - return result |
| 530 | + result = SyncExtracted( |
| 531 | + raw_data=raw_data, targets=targets, sequences=sequences, states=states |
| 532 | + ) |
| 533 | + logger.debug("Emulation step complete, %s compiled graphs executed", len(raw_data)) |
| 534 | + if self.path is not None: |
| 535 | + result.save_dataset(self.path) |
| 536 | + return result |
532 | 537 |
|
533 | 538 |
|
534 | 539 | # How many seconds to sleep while waiting for the results from the cloud. |
@@ -781,7 +786,7 @@ def run( |
781 | 786 | """ |
782 | 787 | Launch the extraction. |
783 | 788 | """ |
784 | | - raise Exception("Not implemented") |
| 789 | + raise NotImplementedError() |
785 | 790 |
|
786 | 791 | def _run( |
787 | 792 | self, |
|
0 commit comments