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 +3 -1
- emu_base/base_classes/aggregators.py +8 -3
- emu_base/base_classes/config.py +1 -1
- emu_base/base_classes/results.py +9 -0
- emu_base/constants.py +4 -0
- emu_base/lindblad_operators.py +4 -1
- emu_base/pulser_adapter.py +58 -29
- {emu_base-1.2.5.dist-info → emu_base-1.2.7.dist-info}/METADATA +6 -70
- {emu_base-1.2.5.dist-info → emu_base-1.2.7.dist-info}/RECORD +10 -9
- {emu_base-1.2.5.dist-info → emu_base-1.2.7.dist-info}/WHEEL +0 -0
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.
|
|
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
|
-
) ->
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
emu_base/base_classes/config.py
CHANGED
|
@@ -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,
|
emu_base/base_classes/results.py
CHANGED
|
@@ -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
emu_base/lindblad_operators.py
CHANGED
|
@@ -32,7 +32,10 @@ def get_lindblad_operators(
|
|
|
32
32
|
]
|
|
33
33
|
|
|
34
34
|
if noise_type == "eff_noise":
|
|
35
|
-
if not all(
|
|
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 [
|
emu_base/pulser_adapter.py
CHANGED
|
@@ -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:
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
locals_a_d_p[q_id]["
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
2
|
-
emu_base/
|
|
3
|
-
emu_base/
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
18
|
-
emu_base-1.2.
|
|
19
|
-
emu_base-1.2.
|
|
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,,
|
|
File without changes
|