emu-mps 1.2.2__py3-none-any.whl → 1.2.4__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_mps/__init__.py +2 -2
- emu_mps/algebra.py +19 -14
- emu_mps/constants.py +4 -0
- emu_mps/hamiltonian.py +3 -2
- emu_mps/mpo.py +26 -23
- emu_mps/mps.py +16 -29
- emu_mps/mps_backend.py +40 -3
- emu_mps/mps_backend_impl.py +54 -23
- emu_mps/mps_config.py +15 -1
- emu_mps/tdvp.py +15 -32
- emu_mps/utils.py +10 -10
- emu_mps-1.2.4.dist-info/METADATA +99 -0
- emu_mps-1.2.4.dist-info/RECORD +15 -0
- emu_mps-1.2.2.dist-info/METADATA +0 -133
- emu_mps-1.2.2.dist-info/RECORD +0 -14
- {emu_mps-1.2.2.dist-info → emu_mps-1.2.4.dist-info}/WHEEL +0 -0
emu_mps/__init__.py
CHANGED
|
@@ -10,10 +10,10 @@ from emu_base import (
|
|
|
10
10
|
StateResult,
|
|
11
11
|
SecondMomentOfEnergy,
|
|
12
12
|
)
|
|
13
|
+
from .mps_config import MPSConfig
|
|
13
14
|
from .mpo import MPO
|
|
14
15
|
from .mps import MPS, inner
|
|
15
16
|
from .mps_backend import MPSBackend
|
|
16
|
-
from .mps_config import MPSConfig
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
@@ -35,4 +35,4 @@ __all__ = [
|
|
|
35
35
|
"SecondMomentOfEnergy",
|
|
36
36
|
]
|
|
37
37
|
|
|
38
|
-
__version__ = "1.2.
|
|
38
|
+
__version__ = "1.2.4"
|
emu_mps/algebra.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
import torch
|
|
4
6
|
import math
|
|
7
|
+
|
|
8
|
+
from emu_mps import MPSConfig
|
|
5
9
|
from emu_mps.utils import truncate_impl
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def add_factors(
|
|
9
|
-
left: list[torch.
|
|
10
|
-
) -> list[torch.
|
|
13
|
+
left: list[torch.Tensor], right: list[torch.Tensor]
|
|
14
|
+
) -> list[torch.Tensor]:
|
|
11
15
|
"""
|
|
12
16
|
Direct sum algorithm implementation to sum two tensor trains (MPS/MPO).
|
|
13
17
|
It assumes the left and right bond are along the dimension 0 and -1 of each tensor.
|
|
@@ -48,8 +52,8 @@ def add_factors(
|
|
|
48
52
|
|
|
49
53
|
|
|
50
54
|
def scale_factors(
|
|
51
|
-
factors: list[torch.
|
|
52
|
-
) -> list[torch.
|
|
55
|
+
factors: list[torch.Tensor], scalar: complex, *, which: int
|
|
56
|
+
) -> list[torch.Tensor]:
|
|
53
57
|
"""
|
|
54
58
|
Returns a new list of factors where the tensor at the given index is scaled by `scalar`.
|
|
55
59
|
"""
|
|
@@ -57,10 +61,10 @@ def scale_factors(
|
|
|
57
61
|
|
|
58
62
|
|
|
59
63
|
def zip_right_step(
|
|
60
|
-
slider: torch.
|
|
61
|
-
top: torch.
|
|
62
|
-
bottom: torch.
|
|
63
|
-
) -> torch.
|
|
64
|
+
slider: torch.Tensor,
|
|
65
|
+
top: torch.Tensor,
|
|
66
|
+
bottom: torch.Tensor,
|
|
67
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
64
68
|
"""
|
|
65
69
|
Returns a new `MPS/O` factor of the result of the multiplication MPO @ MPS/O,
|
|
66
70
|
and the updated slider, performing a single step of the
|
|
@@ -113,11 +117,10 @@ def zip_right_step(
|
|
|
113
117
|
|
|
114
118
|
|
|
115
119
|
def zip_right(
|
|
116
|
-
top_factors: list[torch.
|
|
117
|
-
bottom_factors: list[torch.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
) -> list[torch.tensor]:
|
|
120
|
+
top_factors: list[torch.Tensor],
|
|
121
|
+
bottom_factors: list[torch.Tensor],
|
|
122
|
+
config: Optional[MPSConfig] = None,
|
|
123
|
+
) -> list[torch.Tensor]:
|
|
121
124
|
"""
|
|
122
125
|
Returns a new matrix product, resulting from applying `top` to `bottom`.
|
|
123
126
|
The resulting factors are:
|
|
@@ -136,6 +139,8 @@ def zip_right(
|
|
|
136
139
|
A final truncation sweep, from right to left,
|
|
137
140
|
moves back the orthogonal center to the first element.
|
|
138
141
|
"""
|
|
142
|
+
config = config if config is not None else MPSConfig()
|
|
143
|
+
|
|
139
144
|
if len(top_factors) != len(bottom_factors):
|
|
140
145
|
raise ValueError("Cannot multiply two matrix products of different lengths.")
|
|
141
146
|
|
|
@@ -146,6 +151,6 @@ def zip_right(
|
|
|
146
151
|
new_factors.append(res)
|
|
147
152
|
new_factors[-1] @= slider[:, :, 0]
|
|
148
153
|
|
|
149
|
-
truncate_impl(new_factors,
|
|
154
|
+
truncate_impl(new_factors, config=config)
|
|
150
155
|
|
|
151
156
|
return new_factors
|
emu_mps/constants.py
ADDED
emu_mps/hamiltonian.py
CHANGED
|
@@ -285,6 +285,7 @@ def _get_interactions_to_keep(interaction_matrix: torch.Tensor) -> list[torch.Te
|
|
|
285
285
|
returns a list of bool valued tensors,
|
|
286
286
|
indicating which interaction terms to keep for each bond in the MPO
|
|
287
287
|
"""
|
|
288
|
+
interaction_matrix = interaction_matrix.clone()
|
|
288
289
|
nqubits = interaction_matrix.size(dim=1)
|
|
289
290
|
middle = nqubits // 2
|
|
290
291
|
interaction_matrix += torch.eye(
|
|
@@ -362,7 +363,7 @@ def make_H(
|
|
|
362
363
|
|
|
363
364
|
interactions_to_keep = _get_interactions_to_keep(interaction_matrix)
|
|
364
365
|
|
|
365
|
-
cores = [_first_factor(interactions_to_keep[0].item())]
|
|
366
|
+
cores = [_first_factor(interactions_to_keep[0].item() != 0.0)]
|
|
366
367
|
|
|
367
368
|
if nqubits > 2:
|
|
368
369
|
for i in range(1, middle):
|
|
@@ -394,7 +395,7 @@ def make_H(
|
|
|
394
395
|
)
|
|
395
396
|
)
|
|
396
397
|
if nqubits == 2:
|
|
397
|
-
scale = interaction_matrix[0, 1]
|
|
398
|
+
scale = interaction_matrix[0, 1].item()
|
|
398
399
|
elif interactions_to_keep[-1][0]:
|
|
399
400
|
scale = 1.0
|
|
400
401
|
else:
|
emu_mps/mpo.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import itertools
|
|
3
|
-
from typing import Any, List,
|
|
3
|
+
from typing import Any, List, Iterable, Optional
|
|
4
4
|
|
|
5
5
|
import torch
|
|
6
6
|
|
|
@@ -8,7 +8,8 @@ from emu_mps.algebra import add_factors, scale_factors, zip_right
|
|
|
8
8
|
from emu_base.base_classes.operator import FullOp, QuditOp
|
|
9
9
|
from emu_base import Operator, State
|
|
10
10
|
from emu_mps.mps import MPS
|
|
11
|
-
from emu_mps.utils import new_left_bath, assign_devices
|
|
11
|
+
from emu_mps.utils import new_left_bath, assign_devices
|
|
12
|
+
from emu_mps.constants import DEVICE_COUNT
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def _validate_operator_targets(operations: FullOp, nqubits: int) -> None:
|
|
@@ -80,8 +81,7 @@ class MPO(Operator):
|
|
|
80
81
|
factors = zip_right(
|
|
81
82
|
self.factors,
|
|
82
83
|
other.factors,
|
|
83
|
-
|
|
84
|
-
max_rank=other.max_bond_dim,
|
|
84
|
+
config=other.config,
|
|
85
85
|
)
|
|
86
86
|
return MPS(factors, orthogonality_center=0)
|
|
87
87
|
|
|
@@ -175,14 +175,15 @@ class MPO(Operator):
|
|
|
175
175
|
Returns:
|
|
176
176
|
the operator in MPO form.
|
|
177
177
|
"""
|
|
178
|
+
operators_with_tensors: dict[str, torch.Tensor | QuditOp] = dict(operators)
|
|
178
179
|
|
|
179
180
|
_validate_operator_targets(operations, nqubits)
|
|
180
181
|
|
|
181
182
|
basis = set(basis)
|
|
182
183
|
if basis == {"r", "g"}:
|
|
183
|
-
#
|
|
184
|
-
# user defined strings in terms of these
|
|
185
|
-
|
|
184
|
+
# operators_with_tensors will now contain the basis for single qubit ops,
|
|
185
|
+
# and potentially user defined strings in terms of these
|
|
186
|
+
operators_with_tensors |= {
|
|
186
187
|
"gg": torch.tensor(
|
|
187
188
|
[[1.0, 0.0], [0.0, 0.0]], dtype=torch.complex128
|
|
188
189
|
).reshape(1, 2, 2, 1),
|
|
@@ -197,9 +198,9 @@ class MPO(Operator):
|
|
|
197
198
|
).reshape(1, 2, 2, 1),
|
|
198
199
|
}
|
|
199
200
|
elif basis == {"0", "1"}:
|
|
200
|
-
#
|
|
201
|
-
# user defined strings in terms of these
|
|
202
|
-
|
|
201
|
+
# operators_with_tensors will now contain the basis for single qubit ops,
|
|
202
|
+
# and potentially user defined strings in terms of these
|
|
203
|
+
operators_with_tensors |= {
|
|
203
204
|
"00": torch.tensor(
|
|
204
205
|
[[1.0, 0.0], [0.0, 0.0]], dtype=torch.complex128
|
|
205
206
|
).reshape(1, 2, 2, 1),
|
|
@@ -218,26 +219,28 @@ class MPO(Operator):
|
|
|
218
219
|
|
|
219
220
|
mpos = []
|
|
220
221
|
for coeff, tensorop in operations:
|
|
221
|
-
# this function will recurse through the
|
|
222
|
+
# this function will recurse through the operators_with_tensors,
|
|
223
|
+
# and replace any definitions
|
|
222
224
|
# in terms of strings by the computed tensor
|
|
223
225
|
def replace_operator_string(op: QuditOp | torch.Tensor) -> torch.Tensor:
|
|
224
|
-
if isinstance(op,
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
if isinstance(op, torch.Tensor):
|
|
227
|
+
return op
|
|
228
|
+
|
|
229
|
+
result = torch.zeros(1, 2, 2, 1, dtype=torch.complex128)
|
|
230
|
+
for opstr, coeff in op.items():
|
|
231
|
+
tensor = replace_operator_string(operators_with_tensors[opstr])
|
|
232
|
+
operators_with_tensors[opstr] = tensor
|
|
233
|
+
result += tensor * coeff
|
|
234
|
+
return result
|
|
231
235
|
|
|
232
236
|
factors = [
|
|
233
237
|
torch.eye(2, 2, dtype=torch.complex128).reshape(1, 2, 2, 1)
|
|
234
238
|
] * nqubits
|
|
235
239
|
|
|
236
|
-
for i, op in enumerate(tensorop):
|
|
237
|
-
tensorop[i] = (replace_operator_string(op[0]), op[1])
|
|
238
|
-
|
|
239
240
|
for op in tensorop:
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
factor = replace_operator_string(op[0])
|
|
242
|
+
for target_qubit in op[1]:
|
|
243
|
+
factors[target_qubit] = factor
|
|
244
|
+
|
|
242
245
|
mpos.append(coeff * MPO(factors, **kwargs))
|
|
243
246
|
return sum(mpos[1:], start=mpos[0])
|
emu_mps/mps.py
CHANGED
|
@@ -7,15 +7,16 @@ from typing import Any, List, Optional, Iterable
|
|
|
7
7
|
import torch
|
|
8
8
|
|
|
9
9
|
from emu_base import State
|
|
10
|
+
from emu_mps import MPSConfig
|
|
10
11
|
from emu_mps.algebra import add_factors, scale_factors
|
|
11
12
|
from emu_mps.utils import (
|
|
12
|
-
DEVICE_COUNT,
|
|
13
13
|
apply_measurement_errors,
|
|
14
14
|
assign_devices,
|
|
15
15
|
truncate_impl,
|
|
16
16
|
tensor_trace,
|
|
17
17
|
n_operator,
|
|
18
18
|
)
|
|
19
|
+
from emu_mps.constants import DEVICE_COUNT
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class MPS(State):
|
|
@@ -27,17 +28,13 @@ class MPS(State):
|
|
|
27
28
|
Only qubits are supported.
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
DEFAULT_MAX_BOND_DIM: int = 1024
|
|
31
|
-
DEFAULT_PRECISION: float = 1e-5
|
|
32
|
-
|
|
33
31
|
def __init__(
|
|
34
32
|
self,
|
|
35
33
|
factors: List[torch.Tensor],
|
|
36
34
|
/,
|
|
37
35
|
*,
|
|
38
36
|
orthogonality_center: Optional[int] = None,
|
|
39
|
-
|
|
40
|
-
max_bond_dim: int = DEFAULT_MAX_BOND_DIM,
|
|
37
|
+
config: Optional[MPSConfig] = None,
|
|
41
38
|
num_gpus_to_use: Optional[int] = DEVICE_COUNT,
|
|
42
39
|
):
|
|
43
40
|
"""
|
|
@@ -53,14 +50,11 @@ class MPS(State):
|
|
|
53
50
|
of the data to this constructor, or some shared objects.
|
|
54
51
|
orthogonality_center: the orthogonality center of the MPS, or None (in which case
|
|
55
52
|
it will be orthogonalized when needed)
|
|
56
|
-
|
|
57
|
-
max_bond_dim: the maximum bond dimension to allow
|
|
53
|
+
config: the emu-mps config object passed to the run method
|
|
58
54
|
num_gpus_to_use: distribute the factors over this many GPUs
|
|
59
55
|
0=all factors to cpu, None=keep the existing device assignment.
|
|
60
56
|
"""
|
|
61
|
-
self.
|
|
62
|
-
self.max_bond_dim = max_bond_dim
|
|
63
|
-
|
|
57
|
+
self.config = config if config is not None else MPSConfig()
|
|
64
58
|
assert all(
|
|
65
59
|
factors[i - 1].shape[2] == factors[i].shape[0] for i in range(1, len(factors))
|
|
66
60
|
), "The dimensions of consecutive tensors should match"
|
|
@@ -84,8 +78,7 @@ class MPS(State):
|
|
|
84
78
|
def make(
|
|
85
79
|
cls,
|
|
86
80
|
num_sites: int,
|
|
87
|
-
|
|
88
|
-
max_bond_dim: int = DEFAULT_MAX_BOND_DIM,
|
|
81
|
+
config: Optional[MPSConfig] = None,
|
|
89
82
|
num_gpus_to_use: int = DEVICE_COUNT,
|
|
90
83
|
) -> MPS:
|
|
91
84
|
"""
|
|
@@ -93,11 +86,12 @@ class MPS(State):
|
|
|
93
86
|
|
|
94
87
|
Args:
|
|
95
88
|
num_sites: the number of qubits
|
|
96
|
-
|
|
97
|
-
max_bond_dim: the maximum bond dimension to allow
|
|
89
|
+
config: the MPSConfig
|
|
98
90
|
num_gpus_to_use: distribute the factors over this many GPUs
|
|
99
91
|
0=all factors to cpu
|
|
100
92
|
"""
|
|
93
|
+
config = config if config is not None else MPSConfig()
|
|
94
|
+
|
|
101
95
|
if num_sites <= 1:
|
|
102
96
|
raise ValueError("For 1 qubit states, do state vector")
|
|
103
97
|
|
|
@@ -106,8 +100,7 @@ class MPS(State):
|
|
|
106
100
|
torch.tensor([[[1.0], [0.0]]], dtype=torch.complex128)
|
|
107
101
|
for _ in range(num_sites)
|
|
108
102
|
],
|
|
109
|
-
|
|
110
|
-
max_bond_dim=max_bond_dim,
|
|
103
|
+
config=config,
|
|
111
104
|
num_gpus_to_use=num_gpus_to_use,
|
|
112
105
|
orthogonality_center=0, # Arbitrary: every qubit is an orthogonality center.
|
|
113
106
|
)
|
|
@@ -165,15 +158,11 @@ class MPS(State):
|
|
|
165
158
|
"""
|
|
166
159
|
SVD based truncation of the state. Puts the orthogonality center at the first qubit.
|
|
167
160
|
Calls orthogonalize on the last qubit, and then sweeps a series of SVDs right-left.
|
|
168
|
-
Uses self.
|
|
161
|
+
Uses self.config for determining accuracy.
|
|
169
162
|
An in-place operation.
|
|
170
163
|
"""
|
|
171
164
|
self.orthogonalize(self.num_sites - 1)
|
|
172
|
-
truncate_impl(
|
|
173
|
-
self.factors,
|
|
174
|
-
max_error=self.precision,
|
|
175
|
-
max_rank=self.max_bond_dim,
|
|
176
|
-
)
|
|
165
|
+
truncate_impl(self.factors, config=self.config)
|
|
177
166
|
self.orthogonality_center = 0
|
|
178
167
|
|
|
179
168
|
def get_max_bond_dim(self) -> int:
|
|
@@ -236,7 +225,7 @@ class MPS(State):
|
|
|
236
225
|
num_qubits = len(self.factors)
|
|
237
226
|
|
|
238
227
|
bitstring = ""
|
|
239
|
-
acc_mps_j: torch.
|
|
228
|
+
acc_mps_j: torch.Tensor = self.factors[0]
|
|
240
229
|
|
|
241
230
|
for qubit in range(num_qubits):
|
|
242
231
|
# comp_basis is a projector: 0 is for ket |0> and 1 for ket |1>
|
|
@@ -303,7 +292,7 @@ class MPS(State):
|
|
|
303
292
|
"""
|
|
304
293
|
Returns the sum of two MPSs, computed with a direct algorithm.
|
|
305
294
|
The resulting MPS is orthogonalized on the first site and truncated
|
|
306
|
-
up to `self.precision`.
|
|
295
|
+
up to `self.config.precision`.
|
|
307
296
|
|
|
308
297
|
Args:
|
|
309
298
|
other: the other state
|
|
@@ -315,8 +304,7 @@ class MPS(State):
|
|
|
315
304
|
new_tt = add_factors(self.factors, other.factors)
|
|
316
305
|
result = MPS(
|
|
317
306
|
new_tt,
|
|
318
|
-
|
|
319
|
-
max_bond_dim=self.max_bond_dim,
|
|
307
|
+
config=self.config,
|
|
320
308
|
num_gpus_to_use=None,
|
|
321
309
|
orthogonality_center=None, # Orthogonality is lost.
|
|
322
310
|
)
|
|
@@ -341,8 +329,7 @@ class MPS(State):
|
|
|
341
329
|
factors = scale_factors(self.factors, scalar, which=which)
|
|
342
330
|
return MPS(
|
|
343
331
|
factors,
|
|
344
|
-
|
|
345
|
-
max_bond_dim=self.max_bond_dim,
|
|
332
|
+
config=self.config,
|
|
346
333
|
num_gpus_to_use=None,
|
|
347
334
|
orthogonality_center=self.orthogonality_center,
|
|
348
335
|
)
|
emu_mps/mps_backend.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
from pulser import Sequence
|
|
2
|
-
|
|
3
1
|
from emu_base import Backend, BackendConfig, Results
|
|
4
2
|
from emu_mps.mps_config import MPSConfig
|
|
5
|
-
from emu_mps.mps_backend_impl import create_impl
|
|
3
|
+
from emu_mps.mps_backend_impl import create_impl, MPSBackendImpl
|
|
4
|
+
from pulser import Sequence
|
|
5
|
+
import pickle
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
import logging
|
|
9
|
+
import pathlib
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class MPSBackend(Backend):
|
|
@@ -11,6 +15,32 @@ class MPSBackend(Backend):
|
|
|
11
15
|
aka tensor trains.
|
|
12
16
|
"""
|
|
13
17
|
|
|
18
|
+
def resume(self, autosave_file: str | pathlib.Path) -> Results:
|
|
19
|
+
"""
|
|
20
|
+
Resume simulation from autosave file.
|
|
21
|
+
Only resume simulations from data you trust!
|
|
22
|
+
Unpickling of untrusted data is not safe.
|
|
23
|
+
"""
|
|
24
|
+
if isinstance(autosave_file, str):
|
|
25
|
+
autosave_file = pathlib.Path(autosave_file)
|
|
26
|
+
|
|
27
|
+
if not autosave_file.is_file():
|
|
28
|
+
raise ValueError(f"Not a file: {autosave_file}")
|
|
29
|
+
|
|
30
|
+
with open(autosave_file, "rb") as f:
|
|
31
|
+
impl: MPSBackendImpl = pickle.load(f)
|
|
32
|
+
|
|
33
|
+
impl.autosave_file = autosave_file
|
|
34
|
+
impl.last_save_time = time.time()
|
|
35
|
+
impl.config.init_logging() # FIXME: might be best to take logger object out of config.
|
|
36
|
+
|
|
37
|
+
logging.getLogger("global_logger").warning(
|
|
38
|
+
f"Resuming simulation from file {autosave_file}\n"
|
|
39
|
+
f"Saving simulation state every {impl.config.autosave_dt} seconds"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return self._run(impl)
|
|
43
|
+
|
|
14
44
|
def run(self, sequence: Sequence, mps_config: BackendConfig) -> Results:
|
|
15
45
|
"""
|
|
16
46
|
Emulates the given sequence.
|
|
@@ -29,7 +59,14 @@ class MPSBackend(Backend):
|
|
|
29
59
|
impl = create_impl(sequence, mps_config)
|
|
30
60
|
impl.init() # This is separate from the constructor for testing purposes.
|
|
31
61
|
|
|
62
|
+
return self._run(impl)
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _run(impl: MPSBackendImpl) -> Results:
|
|
32
66
|
while not impl.is_finished():
|
|
33
67
|
impl.progress()
|
|
34
68
|
|
|
69
|
+
if impl.autosave_file.is_file():
|
|
70
|
+
os.remove(impl.autosave_file)
|
|
71
|
+
|
|
35
72
|
return impl.results
|
emu_mps/mps_backend_impl.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import math
|
|
2
|
+
import pathlib
|
|
2
3
|
import random
|
|
4
|
+
import uuid
|
|
3
5
|
from resource import RUSAGE_SELF, getrusage
|
|
4
6
|
from typing import Optional
|
|
7
|
+
import pickle
|
|
8
|
+
import os
|
|
5
9
|
|
|
6
10
|
import torch
|
|
7
11
|
import time
|
|
@@ -17,7 +21,6 @@ from emu_mps.noise import compute_noise_from_lindbladians, pick_well_prepared_qu
|
|
|
17
21
|
from emu_mps.tdvp import (
|
|
18
22
|
evolve_single,
|
|
19
23
|
evolve_pair,
|
|
20
|
-
EvolveConfig,
|
|
21
24
|
new_right_bath,
|
|
22
25
|
right_baths,
|
|
23
26
|
)
|
|
@@ -52,6 +55,7 @@ class MPSBackendImpl:
|
|
|
52
55
|
def __init__(self, mps_config: MPSConfig, pulser_data: PulserData):
|
|
53
56
|
self.config = mps_config
|
|
54
57
|
self.target_time = float(self.config.dt)
|
|
58
|
+
self.pulser_data = pulser_data
|
|
55
59
|
self.qubit_count = pulser_data.qubit_count
|
|
56
60
|
assert self.qubit_count >= 2
|
|
57
61
|
self.omega = pulser_data.omega
|
|
@@ -71,15 +75,17 @@ class MPSBackendImpl:
|
|
|
71
75
|
self.tdvp_index = 0
|
|
72
76
|
self.timestep_index = 0
|
|
73
77
|
self.results = Results()
|
|
74
|
-
|
|
75
|
-
self.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
is_hermitian=not self.has_lindblad_noise,
|
|
80
|
-
max_error=self.config.precision,
|
|
81
|
-
max_rank=self.config.max_bond_dim,
|
|
78
|
+
self.autosave_file = self._get_autosave_filepath(self.config.autosave_prefix)
|
|
79
|
+
self.config.logger.warning(
|
|
80
|
+
f"""Will save simulation state to file "{self.autosave_file.name}"
|
|
81
|
+
every {self.config.autosave_dt} seconds.\n"""
|
|
82
|
+
f"""To resume: `MPSBackend().resume("{self.autosave_file}")`"""
|
|
82
83
|
)
|
|
84
|
+
self.last_save_time = time.time()
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _get_autosave_filepath(autosave_prefix: str) -> pathlib.Path:
|
|
88
|
+
return pathlib.Path(os.getcwd()) / (autosave_prefix + str(uuid.uuid1()) + ".dat")
|
|
83
89
|
|
|
84
90
|
def init_dark_qubits(self) -> None:
|
|
85
91
|
has_state_preparation_error: bool = (
|
|
@@ -112,8 +118,7 @@ class MPSBackendImpl:
|
|
|
112
118
|
if initial_state is None:
|
|
113
119
|
self.state = MPS.make(
|
|
114
120
|
self.qubit_count,
|
|
115
|
-
|
|
116
|
-
max_bond_dim=self.config.max_bond_dim,
|
|
121
|
+
config=self.config,
|
|
117
122
|
num_gpus_to_use=self.config.num_gpus_to_use,
|
|
118
123
|
)
|
|
119
124
|
return
|
|
@@ -128,8 +133,7 @@ class MPSBackendImpl:
|
|
|
128
133
|
initial_state = MPS(
|
|
129
134
|
# Deep copy of every tensor of the initial state.
|
|
130
135
|
[f.clone().detach() for f in initial_state.factors],
|
|
131
|
-
|
|
132
|
-
max_bond_dim=self.config.max_bond_dim,
|
|
136
|
+
config=self.config,
|
|
133
137
|
num_gpus_to_use=self.config.num_gpus_to_use,
|
|
134
138
|
)
|
|
135
139
|
initial_state.truncate()
|
|
@@ -198,7 +202,8 @@ class MPSBackendImpl:
|
|
|
198
202
|
ham_factor=self.hamiltonian.factors[index],
|
|
199
203
|
baths=baths,
|
|
200
204
|
dt=dt,
|
|
201
|
-
config=self.
|
|
205
|
+
config=self.config,
|
|
206
|
+
is_hermitian=not self.has_lindblad_noise,
|
|
202
207
|
)
|
|
203
208
|
else:
|
|
204
209
|
assert orth_center_right is not None
|
|
@@ -213,8 +218,9 @@ class MPSBackendImpl:
|
|
|
213
218
|
ham_factors=self.hamiltonian.factors[l : r + 1],
|
|
214
219
|
baths=baths,
|
|
215
220
|
dt=dt,
|
|
216
|
-
config=self.
|
|
221
|
+
config=self.config,
|
|
217
222
|
orth_center_right=orth_center_right,
|
|
223
|
+
is_hermitian=not self.has_lindblad_noise,
|
|
218
224
|
)
|
|
219
225
|
|
|
220
226
|
self.state.orthogonality_center = r if orth_center_right else l
|
|
@@ -292,7 +298,7 @@ class MPSBackendImpl:
|
|
|
292
298
|
)
|
|
293
299
|
if not self.has_lindblad_noise:
|
|
294
300
|
# Free memory because it won't be used anymore
|
|
295
|
-
self.right_baths[-2] =
|
|
301
|
+
self.right_baths[-2] = torch.zeros(0)
|
|
296
302
|
|
|
297
303
|
self._evolve(self.tdvp_index, dt=-delta_time / 2)
|
|
298
304
|
self.left_baths.pop()
|
|
@@ -312,7 +318,7 @@ class MPSBackendImpl:
|
|
|
312
318
|
else:
|
|
313
319
|
raise Exception("Didn't expect this")
|
|
314
320
|
|
|
315
|
-
|
|
321
|
+
self.save_simulation()
|
|
316
322
|
|
|
317
323
|
def tdvp_complete(self) -> None:
|
|
318
324
|
self.current_time = self.target_time
|
|
@@ -343,14 +349,40 @@ class MPSBackendImpl:
|
|
|
343
349
|
self.log_step_statistics(duration=time.time() - self.time)
|
|
344
350
|
self.time = time.time()
|
|
345
351
|
|
|
352
|
+
def save_simulation(self) -> None:
|
|
353
|
+
if self.last_save_time > time.time() - self.config.autosave_dt:
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
basename = self.autosave_file
|
|
357
|
+
with open(basename.with_suffix(".new"), "wb") as file_handle:
|
|
358
|
+
pickle.dump(self, file_handle)
|
|
359
|
+
|
|
360
|
+
if basename.is_file():
|
|
361
|
+
os.rename(basename, basename.with_suffix(".bak"))
|
|
362
|
+
|
|
363
|
+
os.rename(basename.with_suffix(".new"), basename)
|
|
364
|
+
autosave_filesize = os.path.getsize(self.autosave_file) / 1e6
|
|
365
|
+
|
|
366
|
+
if basename.with_suffix(".bak").is_file():
|
|
367
|
+
os.remove(basename.with_suffix(".bak"))
|
|
368
|
+
|
|
369
|
+
self.last_save_time = time.time()
|
|
370
|
+
|
|
371
|
+
self.config.logger.debug(
|
|
372
|
+
f"Saved simulation state in file {self.autosave_file} ({autosave_filesize}MB)"
|
|
373
|
+
)
|
|
374
|
+
|
|
346
375
|
def fill_results(self) -> None:
|
|
347
376
|
normalized_state = 1 / self.state.norm() * self.state
|
|
348
377
|
|
|
378
|
+
current_time_int: int = round(self.current_time)
|
|
379
|
+
assert abs(self.current_time - current_time_int) < 1e-10
|
|
380
|
+
|
|
349
381
|
if self.well_prepared_qubits_filter is None:
|
|
350
382
|
for callback in self.config.callbacks:
|
|
351
383
|
callback(
|
|
352
384
|
self.config,
|
|
353
|
-
|
|
385
|
+
current_time_int,
|
|
354
386
|
normalized_state,
|
|
355
387
|
self.hamiltonian,
|
|
356
388
|
self.results,
|
|
@@ -359,7 +391,7 @@ class MPSBackendImpl:
|
|
|
359
391
|
|
|
360
392
|
full_mpo, full_state = None, None
|
|
361
393
|
for callback in self.config.callbacks:
|
|
362
|
-
if
|
|
394
|
+
if current_time_int not in callback.evaluation_times:
|
|
363
395
|
continue
|
|
364
396
|
|
|
365
397
|
if full_mpo is None or full_state is None:
|
|
@@ -380,7 +412,7 @@ class MPSBackendImpl:
|
|
|
380
412
|
),
|
|
381
413
|
)
|
|
382
414
|
|
|
383
|
-
callback(self.config,
|
|
415
|
+
callback(self.config, current_time_int, full_state, full_mpo, self.results)
|
|
384
416
|
|
|
385
417
|
def log_step_statistics(self, *, duration: float) -> None:
|
|
386
418
|
if self.state.factors[0].is_cuda:
|
|
@@ -424,13 +456,12 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
|
|
|
424
456
|
"""
|
|
425
457
|
|
|
426
458
|
jump_threshold: float
|
|
427
|
-
aggregated_lindblad_ops:
|
|
459
|
+
aggregated_lindblad_ops: torch.Tensor
|
|
428
460
|
norm_gap_before_jump: float
|
|
429
461
|
root_finder: Optional[BrentsRootFinder]
|
|
430
462
|
|
|
431
463
|
def __init__(self, config: MPSConfig, pulser_data: PulserData):
|
|
432
464
|
super().__init__(config, pulser_data)
|
|
433
|
-
self.aggregated_lindblad_ops = None
|
|
434
465
|
self.lindblad_ops = pulser_data.lindblad_ops
|
|
435
466
|
self.root_finder = None
|
|
436
467
|
|
|
@@ -491,7 +522,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
|
|
|
491
522
|
for qubit in range(self.state.num_sites)
|
|
492
523
|
for op in self.lindblad_ops
|
|
493
524
|
],
|
|
494
|
-
weights=jump_operator_weights.reshape(-1),
|
|
525
|
+
weights=jump_operator_weights.reshape(-1).tolist(),
|
|
495
526
|
)[0]
|
|
496
527
|
|
|
497
528
|
self.state.apply(jumped_qubit_index, jump_operator)
|
emu_mps/mps_config.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from emu_base import BackendConfig, State
|
|
4
|
-
from emu_mps.
|
|
4
|
+
from emu_mps.constants import DEVICE_COUNT
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class MPSConfig(BackendConfig):
|
|
@@ -23,6 +23,10 @@ class MPSConfig(BackendConfig):
|
|
|
23
23
|
num_gpus_to_use: during the simulation, distribute the state over this many GPUs
|
|
24
24
|
0=all factors to cpu. As shown in the benchmarks, using multiple GPUs might
|
|
25
25
|
alleviate memory pressure per GPU, but the runtime should be similar.
|
|
26
|
+
autosave_prefix: filename prefix for autosaving simulation state to file
|
|
27
|
+
autosave_dt: minimum time interval in seconds between two autosaves
|
|
28
|
+
Saving the simulation state is only possible at specific times,
|
|
29
|
+
therefore this interval is only a lower bound.
|
|
26
30
|
kwargs: arguments that are passed to the base class
|
|
27
31
|
|
|
28
32
|
Examples:
|
|
@@ -43,6 +47,8 @@ class MPSConfig(BackendConfig):
|
|
|
43
47
|
max_krylov_dim: int = 100,
|
|
44
48
|
extra_krylov_tolerance: float = 1e-3,
|
|
45
49
|
num_gpus_to_use: int = DEVICE_COUNT,
|
|
50
|
+
autosave_prefix: str = "emu_mps_save_",
|
|
51
|
+
autosave_dt: int = 600, # 10 minutes
|
|
46
52
|
**kwargs: Any,
|
|
47
53
|
):
|
|
48
54
|
super().__init__(**kwargs)
|
|
@@ -62,3 +68,11 @@ class MPSConfig(BackendConfig):
|
|
|
62
68
|
and self.noise_model.amp_sigma != 0.0
|
|
63
69
|
):
|
|
64
70
|
raise NotImplementedError("Unsupported noise type: amp_sigma")
|
|
71
|
+
|
|
72
|
+
self.autosave_prefix = autosave_prefix
|
|
73
|
+
self.autosave_dt = autosave_dt
|
|
74
|
+
|
|
75
|
+
MIN_AUTOSAVE_DT = 10
|
|
76
|
+
assert (
|
|
77
|
+
self.autosave_dt > MIN_AUTOSAVE_DT
|
|
78
|
+
), f"autosave_dt must be larger than {MIN_AUTOSAVE_DT} seconds"
|
emu_mps/tdvp.py
CHANGED
|
@@ -3,6 +3,7 @@ import torch
|
|
|
3
3
|
from emu_base import krylov_exp
|
|
4
4
|
from emu_mps import MPS, MPO
|
|
5
5
|
from emu_mps.utils import split_tensor
|
|
6
|
+
from emu_mps.mps_config import MPSConfig
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def new_right_bath(
|
|
@@ -10,7 +11,8 @@ def new_right_bath(
|
|
|
10
11
|
) -> torch.Tensor:
|
|
11
12
|
bath = torch.tensordot(state, bath, ([2], [2]))
|
|
12
13
|
bath = torch.tensordot(op.to(bath.device), bath, ([2, 3], [1, 3]))
|
|
13
|
-
|
|
14
|
+
bath = torch.tensordot(state.conj(), bath, ([1, 2], [1, 3]))
|
|
15
|
+
return bath
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
"""
|
|
@@ -79,27 +81,6 @@ def apply_effective_Hamiltonian(
|
|
|
79
81
|
_TIME_CONVERSION_COEFF = 0.001 # Omega and delta are given in rad/ms, dt in ns
|
|
80
82
|
|
|
81
83
|
|
|
82
|
-
class EvolveConfig:
|
|
83
|
-
def __init__(
|
|
84
|
-
self,
|
|
85
|
-
*,
|
|
86
|
-
exp_tolerance: float,
|
|
87
|
-
norm_tolerance: float,
|
|
88
|
-
max_krylov_dim: int,
|
|
89
|
-
is_hermitian: bool,
|
|
90
|
-
max_error: float,
|
|
91
|
-
max_rank: int
|
|
92
|
-
) -> None:
|
|
93
|
-
self.exp_tolerance = exp_tolerance
|
|
94
|
-
self.norm_tolerance = norm_tolerance
|
|
95
|
-
self.max_krylov_dim = max_krylov_dim
|
|
96
|
-
self.is_hermitian = is_hermitian
|
|
97
|
-
self.max_error = (
|
|
98
|
-
max_error # FIXME: max_error and max_rank are irrelevant for evolve_single
|
|
99
|
-
)
|
|
100
|
-
self.max_rank = max_rank
|
|
101
|
-
|
|
102
|
-
|
|
103
84
|
def evolve_pair(
|
|
104
85
|
*,
|
|
105
86
|
state_factors: list[torch.Tensor],
|
|
@@ -107,7 +88,8 @@ def evolve_pair(
|
|
|
107
88
|
ham_factors: list[torch.Tensor],
|
|
108
89
|
dt: float,
|
|
109
90
|
orth_center_right: bool,
|
|
110
|
-
|
|
91
|
+
is_hermitian: bool,
|
|
92
|
+
config: MPSConfig,
|
|
111
93
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
112
94
|
"""
|
|
113
95
|
Time evolution of a pair of tensors of a tensor train using baths and truncated SVD.
|
|
@@ -154,16 +136,16 @@ def evolve_pair(
|
|
|
154
136
|
evol = krylov_exp(
|
|
155
137
|
op,
|
|
156
138
|
combined_state_factors,
|
|
157
|
-
exp_tolerance=config.
|
|
158
|
-
norm_tolerance=config.
|
|
139
|
+
exp_tolerance=config.precision * config.extra_krylov_tolerance,
|
|
140
|
+
norm_tolerance=config.precision * config.extra_krylov_tolerance,
|
|
159
141
|
max_krylov_dim=config.max_krylov_dim,
|
|
160
|
-
is_hermitian=
|
|
142
|
+
is_hermitian=is_hermitian,
|
|
161
143
|
).reshape(left_bond_dim * 2, 2 * right_bond_dim)
|
|
162
144
|
|
|
163
145
|
l, r = split_tensor(
|
|
164
146
|
evol,
|
|
165
|
-
max_error=config.
|
|
166
|
-
max_rank=config.
|
|
147
|
+
max_error=config.precision,
|
|
148
|
+
max_rank=config.max_bond_dim,
|
|
167
149
|
orth_center_right=orth_center_right,
|
|
168
150
|
)
|
|
169
151
|
|
|
@@ -178,7 +160,8 @@ def evolve_single(
|
|
|
178
160
|
baths: tuple[torch.Tensor, torch.Tensor],
|
|
179
161
|
ham_factor: torch.Tensor,
|
|
180
162
|
dt: float,
|
|
181
|
-
|
|
163
|
+
is_hermitian: bool,
|
|
164
|
+
config: MPSConfig,
|
|
182
165
|
) -> torch.Tensor:
|
|
183
166
|
"""
|
|
184
167
|
Time evolution of a single tensor of a tensor train using baths.
|
|
@@ -202,8 +185,8 @@ def evolve_single(
|
|
|
202
185
|
return krylov_exp(
|
|
203
186
|
op,
|
|
204
187
|
state_factor,
|
|
205
|
-
exp_tolerance=config.
|
|
206
|
-
norm_tolerance=config.
|
|
188
|
+
exp_tolerance=config.precision * config.extra_krylov_tolerance,
|
|
189
|
+
norm_tolerance=config.precision * config.extra_krylov_tolerance,
|
|
207
190
|
max_krylov_dim=config.max_krylov_dim,
|
|
208
|
-
is_hermitian=
|
|
191
|
+
is_hermitian=is_hermitian,
|
|
209
192
|
)
|
emu_mps/utils.py
CHANGED
|
@@ -3,7 +3,7 @@ import torch
|
|
|
3
3
|
import random
|
|
4
4
|
from collections import Counter
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
from emu_mps import MPSConfig
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def new_left_bath(
|
|
@@ -12,15 +12,16 @@ def new_left_bath(
|
|
|
12
12
|
# this order is more efficient than contracting the op first in general
|
|
13
13
|
bath = torch.tensordot(bath, state.conj(), ([0], [0]))
|
|
14
14
|
bath = torch.tensordot(bath, op.to(bath.device), ([0, 2], [0, 1]))
|
|
15
|
-
|
|
15
|
+
bath = torch.tensordot(bath, state, ([0, 2], [0, 1]))
|
|
16
|
+
return bath
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def _determine_cutoff_index(d: torch.Tensor, max_error: float) -> int:
|
|
19
20
|
assert max_error > 0
|
|
20
21
|
squared_max_error = max_error * max_error
|
|
21
|
-
acc = 0
|
|
22
|
+
acc = 0.0
|
|
22
23
|
for i in range(d.shape[0]):
|
|
23
|
-
acc += d[i]
|
|
24
|
+
acc += d[i].item()
|
|
24
25
|
if acc > squared_max_error:
|
|
25
26
|
return i
|
|
26
27
|
return 0 # type: ignore[no-any-return]
|
|
@@ -60,9 +61,8 @@ def split_tensor(
|
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
def truncate_impl(
|
|
63
|
-
factors: list[torch.
|
|
64
|
-
|
|
65
|
-
max_rank: int = 1024,
|
|
64
|
+
factors: list[torch.Tensor],
|
|
65
|
+
config: MPSConfig,
|
|
66
66
|
) -> None:
|
|
67
67
|
"""
|
|
68
68
|
Eigenvalues-based truncation of a matrix product.
|
|
@@ -78,8 +78,8 @@ def truncate_impl(
|
|
|
78
78
|
|
|
79
79
|
l, r = split_tensor(
|
|
80
80
|
factors[i].reshape(factor_shape[0], -1),
|
|
81
|
-
max_error=
|
|
82
|
-
max_rank=
|
|
81
|
+
max_error=config.precision,
|
|
82
|
+
max_rank=config.max_bond_dim,
|
|
83
83
|
orth_center_right=False,
|
|
84
84
|
)
|
|
85
85
|
|
|
@@ -241,7 +241,7 @@ def apply_measurement_errors(
|
|
|
241
241
|
return result
|
|
242
242
|
|
|
243
243
|
|
|
244
|
-
n_operator = torch.tensor(
|
|
244
|
+
n_operator: torch.Tensor = torch.tensor(
|
|
245
245
|
[
|
|
246
246
|
[0, 0],
|
|
247
247
|
[0, 1],
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: emu-mps
|
|
3
|
+
Version: 1.2.4
|
|
4
|
+
Summary: Pasqal MPS based pulse emulator built on PyTorch
|
|
5
|
+
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
|
+
License: PASQAL OPEN-SOURCE SOFTWARE LICENSE AGREEMENT (MIT-derived)
|
|
7
|
+
|
|
8
|
+
The author of the License is:
|
|
9
|
+
Pasqal, a Société par Actions Simplifiée (Simplified Joint Stock Company) registered under number 849 441 522 at the Registre du commerce et des sociétés (Trade and Companies Register) of Evry – France, headquartered at 7 rue Leonard de Vinci – 91300 – Massy – France, duly represented by its Président, M. Georges-Olivier REYMOND,
|
|
10
|
+
|
|
11
|
+
Hereafter referred to as « the Licensor »
|
|
12
|
+
|
|
13
|
+
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software (the “Licensee”) and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The Software is “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
|
15
|
+
|
|
16
|
+
- If use of the Software leads to the necessary use of any patent of the Licensor and/or any of its Affiliates (defined as a company owned or controlled by the Licensor), the Licensee is granted a royalty-free license, in any country where such patent is in force, to use the object of such patent; or use the process covered by such patent,
|
|
17
|
+
|
|
18
|
+
- Such a patent license is granted for internal research or academic use of the Licensee's, which includes use by employees and students of the Licensee, acting on behalf of the Licensee, for research purposes only.
|
|
19
|
+
|
|
20
|
+
- The License is governed by the laws of France. Any dispute relating to the License, notably its execution, performance and/or termination shall be brought to, heard and tried by the Tribunal Judiciaire de Paris, regardless of the rules of jurisdiction in the matter.
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: emu-base==1.2.4
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
<div align="center">
|
|
29
|
+
<img src="docs/logos/LogoTaglineSoftGreen.svg">
|
|
30
|
+
|
|
31
|
+
# Emu-MPS
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
**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.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
**Warning:** installing emu-mps will update pulser-core
|
|
39
|
+
|
|
40
|
+
### Using `hatch`, `uv` or any pyproject-compatible Python manager
|
|
41
|
+
|
|
42
|
+
To add `emu-mps` to your project, edit your `pyproject.toml` to add the line
|
|
43
|
+
|
|
44
|
+
```toml
|
|
45
|
+
"emu-mps"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
to the list of `dependencies`.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Using `pip` or `pipx`
|
|
52
|
+
To install the `pipy` package using `pip` or `pipx`
|
|
53
|
+
|
|
54
|
+
1. Create a `venv` if that's not done yet
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
$ python -m venv venv
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. Enter the venv
|
|
62
|
+
|
|
63
|
+
If you're running Unix:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
$ . venv/bin/activate
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If you're running Windows:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
C:\> /path/to/new/virtual/environment/Scripts/activate
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
3. Install the package
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
$ pip install emu-mps
|
|
79
|
+
# or
|
|
80
|
+
$ pipx install emu-mps
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
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.
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
For the time being, the easiest way to learn how to use this package is to look
|
|
89
|
+
at the [examples](examples/emu_mps_examples) and [notebooks](https://pasqal-io.github.io/emulators/latest/).
|
|
90
|
+
|
|
91
|
+
See also the [full documentation](https://github.com/pasqal-io/emulators/blob/main/docs/index.md) for
|
|
92
|
+
the API, information about contributing, benchmarks, etc.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Getting in touch
|
|
96
|
+
|
|
97
|
+
- [Pasqal Community Portal](https://community.pasqal.com/) (forums, chat, tutorials, examples, code library).
|
|
98
|
+
- [GitHub Repository](https://github.com/pasqal-io/quantum-evolution-kernel) (source code, issue tracker).
|
|
99
|
+
- [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, ...)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
emu_mps/__init__.py,sha256=ou1n7ieIHlszqg9hBVYhvA8IBOpeduAWno3GC7X2dWE,646
|
|
2
|
+
emu_mps/algebra.py,sha256=ngPtTH-j2ZCBWoaJZXlkUyIlug7dY7Q92gzfnRlpPMA,5485
|
|
3
|
+
emu_mps/constants.py,sha256=41LYkKLUCz-oxPbd-j7nUDZuhIbUrnez6prT0uR0jcE,56
|
|
4
|
+
emu_mps/hamiltonian.py,sha256=LALCdAQyYQgqFLVvtB9cHoUilT_7eU-6bAHRhu39lqM,15865
|
|
5
|
+
emu_mps/mpo.py,sha256=7y6q0UIfyX9igQknqtgt6nymuVcgjHlH3-Qv7N7uOZE,8769
|
|
6
|
+
emu_mps/mps.py,sha256=OjG_caqPOioCdOt-bFUkOf2xuNGnKzj0LaMc3EJCHi4,17855
|
|
7
|
+
emu_mps/mps_backend.py,sha256=6fVaq-D4xyicYRjGjhqMEieC7---90LpfpbV7ZD7zkQ,2192
|
|
8
|
+
emu_mps/mps_backend_impl.py,sha256=XCrmIjV-ZzI0nTOoaIaM6RGtfo4WHoJakVtoqoTZ7zI,20556
|
|
9
|
+
emu_mps/mps_config.py,sha256=MxahrPDaOpfdB6SLG1610iDUOuLR04IaCjKQRk99ICY,3346
|
|
10
|
+
emu_mps/noise.py,sha256=h4X2EFjoC_Ok0gZ8I9wN77RANXaVehTBbjkcbY_GAmY,784
|
|
11
|
+
emu_mps/tdvp.py,sha256=TH4CcBNczRURXYGPXndWKDs0jWXz_x9ozM961uGiSOw,5711
|
|
12
|
+
emu_mps/utils.py,sha256=n9BcpuIz4Kl6EYlATaK8TKsyF-T7FTwbBo6KSAQYzl8,8066
|
|
13
|
+
emu_mps-1.2.4.dist-info/METADATA,sha256=nAxhFZeRlvVOqTdfJ-szluFRpa0q8EwbmogczPni3kM,5465
|
|
14
|
+
emu_mps-1.2.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
emu_mps-1.2.4.dist-info/RECORD,,
|
emu_mps-1.2.2.dist-info/METADATA
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: emu-mps
|
|
3
|
-
Version: 1.2.2
|
|
4
|
-
Summary: Pasqal MPS based pulse emulator built on PyTorch
|
|
5
|
-
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
|
-
License: Proprietary
|
|
7
|
-
Classifier: License :: Other/Proprietary License
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
10
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
11
|
-
Requires-Python: >=3.10
|
|
12
|
-
Requires-Dist: emu-base==1.2.2
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
<div align="center">
|
|
16
|
-
<img src="docs/logos/LogoTaglineSoftGreen.svg">
|
|
17
|
-
|
|
18
|
-
# Emu-MPS
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
**EMU-MPS** is a Pulser backend, designed to **EMU**late the dynamics of programmable arrays of neutral atoms, with matrix product states (**MPS**). It allows users to increase the number of qubits and reduce computation time.
|
|
22
|
-
|
|
23
|
-
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.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## Getting started
|
|
27
|
-
|
|
28
|
-
You can install from source, or download the package from the private pypi registry that pasqal maintains in gitlab.
|
|
29
|
-
For developers, we recommend installing from source, for users we recommend installing from the registry.
|
|
30
|
-
|
|
31
|
-
**Warning:** installing emu-mps will update pulser-core
|
|
32
|
-
|
|
33
|
-
We always recommend using a virtual environment.
|
|
34
|
-
|
|
35
|
-
<details>
|
|
36
|
-
<summary>Click me to see how it is done</summary>
|
|
37
|
-
|
|
38
|
-
#### Create a virtual environment using python
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
python -m venv .venv
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Or
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
python -m venv /path/to/new/virtual/environment
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Replace `/path/to/new/virtual/environment` with your desired directory path.
|
|
51
|
-
|
|
52
|
-
Then activate the environment On linux or MacOS
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
source /path/to/new/virtual/environment/bin/activate
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
While on Windows it's
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
C:\> /path/to/new/virtual/environment/Scripts/activate
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Remember to replace `/path/to/new/virtual/environment` with the actual path to your virtual environment. Once the environment is activated, you can clone emu_mps and install it using
|
|
65
|
-
|
|
66
|
-
</details>
|
|
67
|
-
|
|
68
|
-
### installing from the registry
|
|
69
|
-
|
|
70
|
-
When pip is configured to know about the pasqal registry, Emu-MPS installs as
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
pip install emu-mps
|
|
74
|
-
```
|
|
75
|
-
When pip is not already configured, the easiest way to do so, is to add a file `~/.config/pip/pip.conf` containing:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
[global]
|
|
79
|
-
extra-index-url=https://gitlab.pasqal.com/api/v4/projects/597/packages/pypi/simple
|
|
80
|
-
possible.other.urls
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
As this shows, it is also possible to have multiple extra repositories configured. Note that the order is not important.
|
|
84
|
-
|
|
85
|
-
It is also possible to add the `extra-index-url` to the `pip install` command directly, if you somehow don't want to create a `pip.conf` file.
|
|
86
|
-
|
|
87
|
-
### installing from source
|
|
88
|
-
git clone this [repository ](https://gitlab.pasqal.com/emulation/rydberg-atoms/emu-ct) or download
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Then, `cd` into the root folder of the repo and type
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
pip install -e .
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
<details>
|
|
98
|
-
<summary>Guidelines for developers </summary>
|
|
99
|
-
We recommend using an environment, git clone the repository, then inside the `emu_mps` folder
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
pip install -e .
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Also, the installation of pytest, nbmake, pre-commit.
|
|
106
|
-
|
|
107
|
-
Do not forget to run the unit test suite by simply running `pytest` command.
|
|
108
|
-
|
|
109
|
-
Another way can be using hatch.
|
|
110
|
-
|
|
111
|
-
#### virtual environment with `hatch`
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
python -m pip install hatch
|
|
115
|
-
python -m hatch -v shell
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
When inside the shell with development dependencies, install first the pre-commit hook:
|
|
119
|
-
```
|
|
120
|
-
pre-commit install
|
|
121
|
-
```
|
|
122
|
-
</details>
|
|
123
|
-
|
|
124
|
-
## Check the tutorial notebooks and example scripts
|
|
125
|
-
|
|
126
|
-
For more information, you can check the tutorials and examples located in the [examples folder](https://gitlab.pasqal.com/emulation/rydberg-atoms/emu-ct/-/tree/main/examples?ref_type=heads)
|
|
127
|
-
|
|
128
|
-
## Documentation
|
|
129
|
-
|
|
130
|
-
Please check the [documentation](./docs/index.md) page for more info about contributing, the API, benchmarks, etc.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-

|
emu_mps-1.2.2.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
emu_mps/__init__.py,sha256=O7sRnIvjoRsX-2lVy3NwydAIos69aCbYyFNpUGwAoTw,646
|
|
2
|
-
emu_mps/algebra.py,sha256=fMkhie3iy-zEivE6G3EpJoo2o6Gde0rqrOwBUKOBkME,5384
|
|
3
|
-
emu_mps/hamiltonian.py,sha256=Ojb-T2bpb-Ym99IbFsTC2WqQ4C06IbcYK4VusA2pt_0,15799
|
|
4
|
-
emu_mps/mpo.py,sha256=RA9n50li1NJsIWCid-uOFgYV-usX4S7ZgmRdPLht8hs,8611
|
|
5
|
-
emu_mps/mps.py,sha256=3Oi_gT5KdY46Q3l9uGifWyrb77DUQOHxnu_O9ky7MYo,18338
|
|
6
|
-
emu_mps/mps_backend.py,sha256=ZCZMCsJz4-Oi0Ox2CqCOWqQWdE9ZJj6n_Jssd0g_1T0,972
|
|
7
|
-
emu_mps/mps_backend_impl.py,sha256=dEhBVciS7bdZ0AymbgDVyNUfprxuv-GgUoRcmKBkLHY,19512
|
|
8
|
-
emu_mps/mps_config.py,sha256=-ScHCNiiAwg_kMBSGOsXydjnbo_y3AqrvqpG3RzTYmU,2703
|
|
9
|
-
emu_mps/noise.py,sha256=h4X2EFjoC_Ok0gZ8I9wN77RANXaVehTBbjkcbY_GAmY,784
|
|
10
|
-
emu_mps/tdvp.py,sha256=mDStFKgbG3OXsEG-Ja8NJKsTGyLxsjNS6sQXK7B33EY,6088
|
|
11
|
-
emu_mps/utils.py,sha256=05s5TBE2zhsJwugbQFDCFQi7L0pVX0ukI-WIW2zN5TA,8052
|
|
12
|
-
emu_mps-1.2.2.dist-info/METADATA,sha256=fYI3Qzl0Kdm0FxPR0zQtJOegTg05-zOYuiv6Ee74fQM,4203
|
|
13
|
-
emu_mps-1.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
-
emu_mps-1.2.2.dist-info/RECORD,,
|
|
File without changes
|