emu-base 1.2.5__py3-none-any.whl → 1.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
emu_base/__init__.py CHANGED
@@ -15,6 +15,7 @@ from .base_classes.default_callbacks import (
15
15
  StateResult,
16
16
  SecondMomentOfEnergy,
17
17
  )
18
+ from .constants import DEVICE_COUNT
18
19
  from .pulser_adapter import PulserData, HamiltonianType
19
20
  from .math.brents_root_finding import find_root_brents
20
21
  from .math.krylov_exp import krylov_exp, DEFAULT_MAX_KRYLOV_DIM
@@ -42,6 +43,7 @@ __all__ = [
42
43
  "krylov_exp",
43
44
  "HamiltonianType",
44
45
  "DEFAULT_MAX_KRYLOV_DIM",
46
+ "DEVICE_COUNT",
45
47
  ]
46
48
 
47
- __version__ = "1.2.5"
49
+ __version__ = "1.2.7"
@@ -9,9 +9,14 @@ _NUMERIC_TYPES = {int, float, complex}
9
9
 
10
10
  def mean_aggregator(
11
11
  values: list[Any],
12
- ) -> complex | float | list[complex] | list[float] | list[list[complex]] | list[
13
- list[float]
14
- ]: # FIXME: support tuples?
12
+ ) -> (
13
+ complex
14
+ | float
15
+ | list[complex]
16
+ | list[float]
17
+ | list[list[complex]]
18
+ | list[list[float]]
19
+ ): # FIXME: support tuples?
15
20
  if values == []:
16
21
  raise ValueError("Cannot average 0 samples")
17
22
 
@@ -37,7 +37,7 @@ class BackendConfig:
37
37
  *,
38
38
  observables: list[Callback] | None = None,
39
39
  with_modulation: bool = False,
40
- noise_model: NoiseModel = None,
40
+ noise_model: NoiseModel | None = None,
41
41
  interaction_matrix: list[list[float]] | None = None,
42
42
  interaction_cutoff: float = 0.0,
43
43
  log_level: int = logging.INFO,
@@ -3,11 +3,19 @@ from typing import Any, Callable, Optional
3
3
  from pathlib import Path
4
4
  import json
5
5
  import logging
6
+ import torch
6
7
 
7
8
  from emu_base.base_classes.callback import Callback, AggregationType
8
9
  from emu_base.base_classes.aggregators import aggregation_types_definitions
9
10
 
10
11
 
12
+ class ResultsEncoder(json.JSONEncoder):
13
+ def default(self, obj: Any) -> Any:
14
+ if isinstance(obj, torch.Tensor):
15
+ return obj.tolist()
16
+ return super().default(obj)
17
+
18
+
11
19
  @dataclass
12
20
  class Results:
13
21
  """
@@ -171,4 +179,5 @@ class Results:
171
179
  "statistics": self.statistics,
172
180
  },
173
181
  file_handle,
182
+ cls=ResultsEncoder,
174
183
  )
emu_base/constants.py ADDED
@@ -0,0 +1,4 @@
1
+ import torch
2
+
3
+
4
+ DEVICE_COUNT = torch.cuda.device_count()
@@ -32,7 +32,10 @@ def get_lindblad_operators(
32
32
  ]
33
33
 
34
34
  if noise_type == "eff_noise":
35
- if not all(op.shape == (2, 2) for op in noise_model.eff_noise_opers):
35
+ if not all(
36
+ isinstance(op, torch.Tensor) and op.shape == (2, 2)
37
+ for op in noise_model.eff_noise_opers
38
+ ):
36
39
  raise ValueError("Only 2 * 2 effective noise operator matrices are supported")
37
40
 
38
41
  return [
@@ -5,6 +5,8 @@ import math
5
5
  from pulser.noise_model import NoiseModel
6
6
  from enum import Enum
7
7
 
8
+ from pulser.register.base_register import BaseRegister
9
+
8
10
  from emu_base.base_classes.config import BackendConfig
9
11
  from emu_base.lindblad_operators import get_lindblad_operators
10
12
  from emu_base.utils import dist2, dist3
@@ -16,7 +18,7 @@ class HamiltonianType(Enum):
16
18
 
17
19
 
18
20
  def _get_qubit_positions(
19
- register: pulser.Register,
21
+ register: BaseRegister,
20
22
  ) -> list[torch.Tensor]:
21
23
  """Conversion from pulser Register to emu-mps register (torch type).
22
24
  Each element will be given as [Rx,Ry,Rz]"""
@@ -31,7 +33,7 @@ def _get_qubit_positions(
31
33
  def _rydberg_interaction(sequence: pulser.Sequence) -> torch.Tensor:
32
34
  """
33
35
  Computes the Ising interaction matrix from the qubit positions.
34
- Hᵢⱼ=C₆/Rᵢⱼ⁶ (nᵢ⊗ nⱼ)
36
+ Hᵢⱼ=C₆/R⁶ᵢⱼ (nᵢ⊗ nⱼ)
35
37
  """
36
38
 
37
39
  num_qubits = len(sequence.register.qubit_ids)
@@ -88,7 +90,7 @@ def _xy_interaction(sequence: pulser.Sequence) -> torch.Tensor:
88
90
  def _extract_omega_delta_phi(
89
91
  *,
90
92
  sequence: pulser.Sequence,
91
- dt: int,
93
+ target_times: list[int],
92
94
  with_modulation: bool,
93
95
  laser_waist: float | None,
94
96
  ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
@@ -125,7 +127,7 @@ def _extract_omega_delta_phi(
125
127
 
126
128
  max_duration = sequence.get_duration(include_fall_time=with_modulation)
127
129
 
128
- nsamples = math.ceil(max_duration / dt - 1 / 2)
130
+ nsamples = len(target_times) - 1
129
131
  omega = torch.zeros(
130
132
  nsamples,
131
133
  len(sequence.register.qubit_ids),
@@ -157,27 +159,45 @@ def _extract_omega_delta_phi(
157
159
  for slot in ch_samples.slots:
158
160
  global_times |= set(i for i in range(slot.ti, slot.tf))
159
161
 
160
- step = 0
161
- t = (step + 1 / 2) * dt
162
-
163
- while t < max_duration:
164
- for q_pos, q_id in enumerate(sequence.register.qubit_ids):
165
- omega[step, q_pos] = (
166
- locals_a_d_p[q_id]["amp"][math.floor(t)]
167
- + locals_a_d_p[q_id]["amp"][math.ceil(t)]
168
- ) / 2.0
169
- delta[step, q_pos] = (
170
- locals_a_d_p[q_id]["det"][math.floor(t)]
171
- + locals_a_d_p[q_id]["det"][math.ceil(t)]
172
- ) / 2.0
173
- phi[step, q_pos] = (
174
- locals_a_d_p[q_id]["phase"][math.floor(t)]
175
- + locals_a_d_p[q_id]["phase"][math.ceil(t)]
176
- ) / 2.0
177
- if t in global_times:
178
- omega[step] *= waist_factors
179
- step += 1
180
- t = (step + 1 / 2) * dt
162
+ omega_1 = torch.zeros_like(omega[0])
163
+ omega_2 = torch.zeros_like(omega[0])
164
+
165
+ for i in range(nsamples):
166
+ t = (target_times[i] + target_times[i + 1]) / 2
167
+ # The sampled values correspond to the start of each interval
168
+ # To maximize the order of the solver, we need the values in the middle
169
+ if math.ceil(t) < max_duration:
170
+ # If we're not the final step, approximate this using linear interpolation
171
+ # Note that for dt even, t1=t2
172
+ for q_pos, q_id in enumerate(sequence.register.qubit_ids):
173
+ t1 = math.floor(t)
174
+ t2 = math.ceil(t)
175
+ omega_1[q_pos] = locals_a_d_p[q_id]["amp"][t1]
176
+ omega_2[q_pos] = locals_a_d_p[q_id]["amp"][t2]
177
+ delta[i, q_pos] = (
178
+ locals_a_d_p[q_id]["det"][t1] + locals_a_d_p[q_id]["det"][t2]
179
+ ) / 2.0
180
+ phi[i, q_pos] = (
181
+ locals_a_d_p[q_id]["phase"][t1] + locals_a_d_p[q_id]["phase"][t2]
182
+ ) / 2.0
183
+ # omegas at different times need to have the laser waist applied independently
184
+ if t1 in global_times:
185
+ omega_1 *= waist_factors
186
+ if t2 in global_times:
187
+ omega_2 *= waist_factors
188
+ omega[i] = 0.5 * (omega_1 + omega_2)
189
+ else:
190
+ # We're in the final step and dt=1, approximate this using linear extrapolation
191
+ # we can reuse omega_1 and omega_2 from before
192
+ for q_pos, q_id in enumerate(sequence.register.qubit_ids):
193
+ delta[i, q_pos] = (
194
+ 3.0 * locals_a_d_p[q_id]["det"][t2] - locals_a_d_p[q_id]["det"][t1]
195
+ ) / 2.0
196
+ phi[i, q_pos] = (
197
+ 3.0 * locals_a_d_p[q_id]["phase"][t2]
198
+ - locals_a_d_p[q_id]["phase"][t1]
199
+ ) / 2.0
200
+ omega[i] = torch.clamp(0.5 * (3 * omega_2 - omega_1).real, min=0.0)
181
201
 
182
202
  return omega, delta, phi
183
203
 
@@ -200,7 +220,7 @@ def _get_all_lindblad_noise_operators(
200
220
 
201
221
 
202
222
  class PulserData:
203
- slm_end_time: int
223
+ slm_end_time: float
204
224
  full_interaction_matrix: torch.Tensor
205
225
  masked_interaction_matrix: torch.Tensor
206
226
  omega: torch.Tensor
@@ -212,12 +232,20 @@ class PulserData:
212
232
  def __init__(self, *, sequence: pulser.Sequence, config: BackendConfig, dt: int):
213
233
  self.qubit_count = len(sequence.register.qubit_ids)
214
234
 
235
+ # the end value is exclusive, so add +1
236
+ observable_times = set(torch.arange(0, sequence.get_duration() + 1, dt).tolist())
237
+ observable_times.add(sequence.get_duration())
238
+ for obs in config.callbacks:
239
+ observable_times |= set(obs.evaluation_times)
240
+ self.target_times = list(observable_times)
241
+ self.target_times.sort()
242
+
215
243
  laser_waist = (
216
244
  config.noise_model.laser_waist if config.noise_model is not None else None
217
245
  )
218
246
  self.omega, self.delta, self.phi = _extract_omega_delta_phi(
219
247
  sequence=sequence,
220
- dt=dt,
248
+ target_times=self.target_times,
221
249
  with_modulation=config.with_modulation,
222
250
  laser_waist=laser_waist,
223
251
  )
@@ -250,11 +278,12 @@ class PulserData:
250
278
  ] = 0.0
251
279
  self.masked_interaction_matrix = self.full_interaction_matrix.clone()
252
280
 
253
- slm_targets = sequence._slm_mask_targets
254
281
  self.slm_end_time = (
255
282
  sequence._slm_mask_time[1] if len(sequence._slm_mask_time) > 1 else 0.0
256
283
  )
257
284
 
258
- for target in slm_targets:
285
+ # disable interaction for SLM masked qubits
286
+ slm_targets = list(sequence._slm_mask_targets)
287
+ for target in sequence.register.find_indices(slm_targets):
259
288
  self.masked_interaction_matrix[target] = 0.0
260
289
  self.masked_interaction_matrix[:, target] = 0.0
@@ -1,7 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emu-base
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: Pasqal base classes for emulators
5
+ Project-URL: Documentation, https://pasqal-io.github.io/emulators/
6
+ Project-URL: Repository, https://github.com/pasqal-io/emulators
7
+ Project-URL: Issues, https://github.com/pasqal-io/emulators/issues
5
8
  Author-email: Anton Quelle <anton.quelle@pasqal.com>, Mauro Mendizabal <mauro.mendizabal-pico@pasqal.com>, Stefano Grava <stefano.grava@pasqal.com>, Pablo Le Henaff <pablo.le-henaff@pasqal.com>
6
9
  License: PASQAL OPEN-SOURCE SOFTWARE LICENSE AGREEMENT (MIT-derived)
7
10
 
@@ -28,75 +31,8 @@ Description-Content-Type: text/markdown
28
31
 
29
32
  <div align="center">
30
33
  <img src="docs/logos/LogoTaglineSoftGreen.svg">
31
-
32
- # Emu-MPS
33
34
  </div>
34
35
 
35
- **Emu-mps** is a backend for the [Pulser low-level Quantum Programming toolkit](https://pulser.readthedocs.io) that lets you run Quantum Algorithms on a simulated device, using GPU acceleration if available. More in depth, emu-mps is designed to **emu**late the dynamics of programmable arrays of neutral atoms, with matrix product states (**mps**). While benchmarking is incomplete as of this writing, early results suggest that this design makes emu-mps faster and more memory-efficient than previous generations of quantum emulators at running simulations with large numbers of qubits.
36
-
37
- As of this writing, Emu-MPS is provided for Linux and macOS but will not work under Windows.
38
-
39
- ## Installation
40
-
41
- **Warning:** installing emu-mps will update pulser-core
42
-
43
- ### Using `hatch`, `uv` or any pyproject-compatible Python manager
44
-
45
- To add `emu-mps` to your project, edit your `pyproject.toml` to add the line
46
-
47
- ```toml
48
- "emu-mps"
49
- ```
50
-
51
- to the list of `dependencies`.
52
-
53
-
54
- ### Using `pip` or `pipx`
55
- To install the `pipy` package using `pip` or `pipx`
56
-
57
- 1. Create a `venv` if that's not done yet
58
-
59
- ```sh
60
- $ python -m venv venv
61
-
62
- ```
63
-
64
- 2. Enter the venv
65
-
66
- If you're running Unix:
67
-
68
- ```sh
69
- $ . venv/bin/activate
70
- ```
71
-
72
- If you're running Windows:
73
-
74
- ```sh
75
- C:\> /path/to/new/virtual/environment/Scripts/activate
76
- ```
77
-
78
- 3. Install the package
79
-
80
- ```sh
81
- $ pip install emu-mps
82
- # or
83
- $ pipx install emu-mps
84
- ```
85
-
86
-
87
- Join us on [Slack](https://pasqalworkspace.slack.com/archives/C07MUV5K7EU) or by [e-mail](mailto:emulation@pasqal.com) to give us feedback about how you plan to use Emu-MPS or if you require specific feature-upgrades.
88
-
89
- ## Usage
90
-
91
- For the time being, the easiest way to learn how to use this package is to look
92
- at the [examples](examples/emu_mps_examples) and [notebooks](https://pasqal-io.github.io/emulators/latest/).
93
-
94
- See also the [full documentation](https://github.com/pasqal-io/emulators/blob/main/docs/index.md) for
95
- the API, information about contributing, benchmarks, etc.
96
-
97
-
98
- ## Getting in touch
36
+ # Welcome to the Pasqal analog emulators
99
37
 
100
- - [Pasqal Community Portal](https://community.pasqal.com/) (forums, chat, tutorials, examples, code library).
101
- - [GitHub Repository](https://github.com/pasqal-io/quantum-evolution-kernel) (source code, issue tracker).
102
- - [Professional Support](https://www.pasqal.com/contact-us/) (if you need tech support, custom licenses, a variant of this library optimized for your workload, your own QPU, remote access to a QPU, ...)
38
+ Welcome, and please see the [GitHub pages](https://pasqal-io.github.io/emulators/) for a landing page to this repo.
@@ -1,19 +1,20 @@
1
- emu_base/__init__.py,sha256=5X57qf5fo0zv8vmeaDbrW9ThsAEIjy5HPsCpQEIL8iI,1130
2
- emu_base/lindblad_operators.py,sha256=eXkXsLt_fV7jhF31EkxzYFU04rOJV3uWXsl6t1aTAqs,1562
3
- emu_base/pulser_adapter.py,sha256=vnIiOpjJBXEwMmuWqOdRIlHmkkYnvvPmsSfRNPzBi2k,9118
1
+ emu_base/__init__.py,sha256=i2Gv8rIusAD2hXepDFdFTfC5FQOOpNXtB0F1xoSd6QQ,1186
2
+ emu_base/constants.py,sha256=41LYkKLUCz-oxPbd-j7nUDZuhIbUrnez6prT0uR0jcE,56
3
+ emu_base/lindblad_operators.py,sha256=Nsl1YrWb8IDM9Z50ucy2Ed44p_IRETnlbr6qaqAgV50,1629
4
+ emu_base/pulser_adapter.py,sha256=SIkw3Mrob4RPTt4QNYzC8CUJ0tj-sTj-t-fEKEyRTtU,10858
4
5
  emu_base/utils.py,sha256=RM8O0qfPAJfcdqqAojwEEKV7I3ZfVDklnTisTGhUg5k,233
5
6
  emu_base/base_classes/__init__.py,sha256=Su6fHtjCyg0fw-7y7e7nbMfDASppNRQs8iGaAOkO3c4,570
6
- emu_base/base_classes/aggregators.py,sha256=bcvoGfZCkPKv-CI29gTma6HBphGh7bjBq2Ome77eRJM,1840
7
+ emu_base/base_classes/aggregators.py,sha256=BDzFq12q36p12TXp2qv3g7a9cHnXbT1hhMZJLVojkDU,1862
7
8
  emu_base/base_classes/backend.py,sha256=7tnwb9MnRbwRN1_JTqliYftjqExuOE-Rrwz9AU2Pc4c,1645
8
9
  emu_base/base_classes/callback.py,sha256=JXah_ZDNM8iyPWy7IOwW481qRFyqVvlSM-0OkjBzV0A,3055
9
- emu_base/base_classes/config.py,sha256=Dg2CwC9sc5HYwszQAJSVjkSd3wuQrv6aEZGYBRnFl48,4098
10
+ emu_base/base_classes/config.py,sha256=oLS2jwmxqwMbLKIPdqohK-KPIcXdtpG3sRr_Y12poNQ,4105
10
11
  emu_base/base_classes/default_callbacks.py,sha256=F44kkuwWdVcvMGZ9vJ2q7ug-_P8IQyJv-SVxSVWHW_w,9940
11
12
  emu_base/base_classes/operator.py,sha256=MJjuDUTwJLbaSJzSNCKDWGvmGCGAEIEWISLoPSSzNsU,3501
12
- emu_base/base_classes/results.py,sha256=7-Mz3jmFy19hd3PIA5idK610mC3b5jOf3EKBmV14Jv4,5569
13
+ emu_base/base_classes/results.py,sha256=w4js7gThb49sNjEEDfEf0tYRwK004GrafWPeEOvyeCg,5810
13
14
  emu_base/base_classes/state.py,sha256=7iIyZmBqqJ6G4SyYZ3kyylWjAqiYIx0aW5B0T74EPZI,2707
14
15
  emu_base/math/__init__.py,sha256=6BbIytYV5uC-e5jLMtIErkcUl_PvfSNnhmVFY9Il8uQ,97
15
16
  emu_base/math/brents_root_finding.py,sha256=AVx6L1Il6rpPJWrLJ7cn6oNmJyZOPRgEaaZaubC9lsU,3711
16
17
  emu_base/math/krylov_exp.py,sha256=UCFNeq-j2ukgBsOPC9_Jiv1aqpy88SrslDLiCxIGBwk,3840
17
- emu_base-1.2.5.dist-info/METADATA,sha256=wD4pmz4VvisVEZmM3Z44CC_fiwB6SMw-O9fFKPHvb7w,5576
18
- emu_base-1.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- emu_base-1.2.5.dist-info/RECORD,,
18
+ emu_base-1.2.7.dist-info/METADATA,sha256=JpfSGDOQtIu2hwwoHXgBNgf8ZmIkUh0MUDiwJYPZL_Q,3522
19
+ emu_base-1.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ emu_base-1.2.7.dist-info/RECORD,,