emu-mps 2.4.2__py3-none-any.whl → 2.4.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 +1 -1
- emu_mps/hamiltonian.py +116 -92
- emu_mps/mpo.py +56 -22
- emu_mps/mps.py +4 -2
- emu_mps/mps_backend.py +3 -3
- emu_mps/mps_backend_impl.py +8 -4
- emu_mps/mps_config.py +10 -35
- emu_mps/solver_utils.py +4 -3
- emu_mps/utils.py +13 -6
- {emu_mps-2.4.2.dist-info → emu_mps-2.4.4.dist-info}/METADATA +2 -2
- emu_mps-2.4.4.dist-info/RECORD +19 -0
- emu_mps-2.4.2.dist-info/RECORD +0 -19
- {emu_mps-2.4.2.dist-info → emu_mps-2.4.4.dist-info}/WHEEL +0 -0
emu_mps/__init__.py
CHANGED
emu_mps/hamiltonian.py
CHANGED
|
@@ -4,35 +4,41 @@ to the Hamiltonian of a neutral atoms quantum processor.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from abc import abstractmethod, ABC
|
|
7
|
-
from typing import Iterator
|
|
7
|
+
from typing import Iterator, Literal, Union
|
|
8
8
|
|
|
9
|
+
from pulser.channels.base_channel import States
|
|
9
10
|
from emu_base import HamiltonianType
|
|
10
11
|
import torch
|
|
11
12
|
from emu_mps.mpo import MPO
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
dtype = torch.complex128
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
Eigenstate = Union[States, Literal["0", "1"]]
|
|
19
|
+
|
|
20
|
+
|
|
16
21
|
class Operators:
|
|
17
22
|
id = torch.eye(2, dtype=dtype)
|
|
23
|
+
id_3x3 = torch.eye(3, dtype=dtype)
|
|
18
24
|
n = torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=dtype)
|
|
19
25
|
creation = torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=dtype)
|
|
20
26
|
sx = torch.tensor([[0.0, 0.5], [0.5, 0.0]], dtype=dtype)
|
|
21
27
|
sy = torch.tensor([[0.0, -0.5j], [0.5j, 0.0]], dtype=dtype)
|
|
22
|
-
pu = torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=dtype)
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
class HamiltonianMPOFactors(ABC):
|
|
26
|
-
def __init__(self, interaction_matrix: torch.Tensor):
|
|
31
|
+
def __init__(self, interaction_matrix: torch.Tensor, dim: int = 2):
|
|
27
32
|
assert interaction_matrix.ndim == 2, "interaction matrix is not a matrix"
|
|
28
33
|
assert (
|
|
29
34
|
interaction_matrix.shape[0] == interaction_matrix.shape[1]
|
|
30
35
|
), "interaction matrix is not square"
|
|
31
|
-
|
|
36
|
+
self.dim = dim
|
|
32
37
|
self.interaction_matrix = interaction_matrix.clone()
|
|
33
38
|
self.interaction_matrix.fill_diagonal_(0.0) # or assert
|
|
34
39
|
self.qubit_count = self.interaction_matrix.shape[0]
|
|
35
40
|
self.middle = self.qubit_count // 2
|
|
41
|
+
self.identity = Operators.id if self.dim == 2 else Operators.id_3x3
|
|
36
42
|
|
|
37
43
|
def __iter__(self) -> Iterator[torch.Tensor]:
|
|
38
44
|
yield self.first_factor()
|
|
@@ -72,10 +78,12 @@ class HamiltonianMPOFactors(ABC):
|
|
|
72
78
|
class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
73
79
|
def first_factor(self) -> torch.Tensor:
|
|
74
80
|
has_right_interaction = self.interaction_matrix[0, 1:].any()
|
|
75
|
-
fac = torch.zeros(
|
|
76
|
-
|
|
81
|
+
fac = torch.zeros(
|
|
82
|
+
1, self.dim, self.dim, 3 if has_right_interaction else 2, dtype=dtype
|
|
83
|
+
)
|
|
84
|
+
fac[0, :, :, 1] = self.identity
|
|
77
85
|
if has_right_interaction:
|
|
78
|
-
fac[0,
|
|
86
|
+
fac[0, :2, :2, 2] = Operators.n
|
|
79
87
|
|
|
80
88
|
return fac
|
|
81
89
|
|
|
@@ -86,18 +94,18 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
86
94
|
|
|
87
95
|
fac = torch.zeros(
|
|
88
96
|
int(current_left_interactions.sum().item() + 2),
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
self.dim,
|
|
98
|
+
self.dim,
|
|
91
99
|
int(left_interactions_to_keep.sum().item() + int(has_right_interaction) + 2),
|
|
92
100
|
dtype=dtype,
|
|
93
101
|
)
|
|
94
102
|
|
|
95
|
-
fac[0, :, :, 0] =
|
|
96
|
-
fac[1, :, :, 1] =
|
|
103
|
+
fac[0, :, :, 0] = self.identity
|
|
104
|
+
fac[1, :, :, 1] = self.identity
|
|
97
105
|
if has_right_interaction:
|
|
98
|
-
fac[1,
|
|
106
|
+
fac[1, :2, :2, -1] = Operators.n
|
|
99
107
|
|
|
100
|
-
fac[2:,
|
|
108
|
+
fac[2:, :2, :2, 0] = (
|
|
101
109
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
102
110
|
* Operators.n
|
|
103
111
|
)
|
|
@@ -106,7 +114,7 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
106
114
|
j = 2
|
|
107
115
|
for current_left_interaction in current_left_interactions.nonzero().flatten():
|
|
108
116
|
if left_interactions_to_keep[current_left_interaction]:
|
|
109
|
-
fac[i, :, :, j] =
|
|
117
|
+
fac[i, :, :, j] = self.identity
|
|
110
118
|
j += 1
|
|
111
119
|
i += 1
|
|
112
120
|
return fac
|
|
@@ -118,21 +126,21 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
118
126
|
|
|
119
127
|
fac = torch.zeros(
|
|
120
128
|
int(current_left_interactions.sum().item() + 2),
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
self.dim,
|
|
130
|
+
self.dim,
|
|
123
131
|
int(current_right_interactions.sum().item() + 2),
|
|
124
132
|
dtype=dtype,
|
|
125
133
|
)
|
|
126
134
|
|
|
127
|
-
fac[0, :, :, 0] =
|
|
128
|
-
fac[1, :, :, 1] =
|
|
135
|
+
fac[0, :, :, 0] = self.identity
|
|
136
|
+
fac[1, :, :, 1] = self.identity
|
|
129
137
|
|
|
130
|
-
fac[2:,
|
|
138
|
+
fac[2:, :2, :2, 0] = (
|
|
131
139
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
132
140
|
* Operators.n
|
|
133
141
|
)
|
|
134
142
|
|
|
135
|
-
fac[1,
|
|
143
|
+
fac[1, :2, :2, 2:] = self.interaction_matrix[n + 1 :][
|
|
136
144
|
None, None, current_right_interactions, n
|
|
137
145
|
] * Operators.n.unsqueeze(-1)
|
|
138
146
|
|
|
@@ -140,7 +148,7 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
140
148
|
self.interaction_matrix[:n, n + 1 :][current_left_interactions, :][
|
|
141
149
|
:, None, None, current_right_interactions
|
|
142
150
|
]
|
|
143
|
-
*
|
|
151
|
+
* self.identity[None, ..., None]
|
|
144
152
|
)
|
|
145
153
|
|
|
146
154
|
return fac
|
|
@@ -152,18 +160,18 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
152
160
|
|
|
153
161
|
fac = torch.zeros(
|
|
154
162
|
int(right_interactions_to_keep.sum().item() + int(has_left_interaction) + 2),
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
self.dim,
|
|
164
|
+
self.dim,
|
|
157
165
|
int(current_right_interactions.sum().item() + 2),
|
|
158
166
|
dtype=dtype,
|
|
159
167
|
)
|
|
160
168
|
|
|
161
|
-
fac[0, :, :, 0] =
|
|
162
|
-
fac[1, :, :, 1] =
|
|
169
|
+
fac[0, :, :, 0] = self.identity
|
|
170
|
+
fac[1, :, :, 1] = self.identity
|
|
163
171
|
if has_left_interaction:
|
|
164
|
-
fac[2,
|
|
172
|
+
fac[2, :2, :2, 0] = Operators.n
|
|
165
173
|
|
|
166
|
-
fac[1,
|
|
174
|
+
fac[1, :2, :2, 2:] = self.interaction_matrix[n + 1 :][
|
|
167
175
|
None, None, current_right_interactions, n
|
|
168
176
|
] * Operators.n.unsqueeze(-1)
|
|
169
177
|
|
|
@@ -171,20 +179,22 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
171
179
|
j = 2
|
|
172
180
|
for current_right_interaction in current_right_interactions.nonzero().flatten():
|
|
173
181
|
if right_interactions_to_keep[current_right_interaction]:
|
|
174
|
-
fac[i, :, :, j] =
|
|
182
|
+
fac[i, :, :, j] = self.identity
|
|
175
183
|
i += 1
|
|
176
184
|
j += 1
|
|
177
185
|
return fac
|
|
178
186
|
|
|
179
187
|
def last_factor(self) -> torch.Tensor:
|
|
180
188
|
has_left_interaction = self.interaction_matrix[-1, :-1].any()
|
|
181
|
-
fac = torch.zeros(
|
|
182
|
-
|
|
189
|
+
fac = torch.zeros(
|
|
190
|
+
3 if has_left_interaction else 2, self.dim, self.dim, 1, dtype=dtype
|
|
191
|
+
)
|
|
192
|
+
fac[0, :, :, 0] = self.identity
|
|
183
193
|
if has_left_interaction:
|
|
184
194
|
if self.qubit_count >= 3:
|
|
185
|
-
fac[2,
|
|
195
|
+
fac[2, :2, :2, 0] = Operators.n
|
|
186
196
|
else:
|
|
187
|
-
fac[2,
|
|
197
|
+
fac[2, :2, :2, 0] = self.interaction_matrix[0, 1] * Operators.n
|
|
188
198
|
|
|
189
199
|
return fac
|
|
190
200
|
|
|
@@ -192,11 +202,13 @@ class RydbergHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
192
202
|
class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
193
203
|
def first_factor(self) -> torch.Tensor:
|
|
194
204
|
has_right_interaction = self.interaction_matrix[0, 1:].any()
|
|
195
|
-
fac = torch.zeros(
|
|
196
|
-
|
|
205
|
+
fac = torch.zeros(
|
|
206
|
+
1, self.dim, self.dim, 4 if has_right_interaction else 2, dtype=dtype
|
|
207
|
+
)
|
|
208
|
+
fac[0, :, :, 1] = self.identity
|
|
197
209
|
if has_right_interaction:
|
|
198
|
-
fac[0,
|
|
199
|
-
fac[0,
|
|
210
|
+
fac[0, :2, :2, 2] = Operators.creation
|
|
211
|
+
fac[0, :2, :2, 3] = Operators.creation.T
|
|
200
212
|
|
|
201
213
|
return fac
|
|
202
214
|
|
|
@@ -207,8 +219,8 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
207
219
|
|
|
208
220
|
fac = torch.zeros(
|
|
209
221
|
int(2 * current_left_interactions.sum().item() + 2),
|
|
210
|
-
|
|
211
|
-
|
|
222
|
+
self.dim,
|
|
223
|
+
self.dim,
|
|
212
224
|
int(
|
|
213
225
|
2 * left_interactions_to_keep.sum().item()
|
|
214
226
|
+ 2 * int(has_right_interaction)
|
|
@@ -217,17 +229,17 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
217
229
|
dtype=dtype,
|
|
218
230
|
)
|
|
219
231
|
|
|
220
|
-
fac[0, :, :, 0] =
|
|
221
|
-
fac[1, :, :, 1] =
|
|
232
|
+
fac[0, :, :, 0] = self.identity
|
|
233
|
+
fac[1, :, :, 1] = self.identity
|
|
222
234
|
if has_right_interaction:
|
|
223
|
-
fac[1,
|
|
224
|
-
fac[1,
|
|
235
|
+
fac[1, :2, :2, -2] = Operators.creation
|
|
236
|
+
fac[1, :2, :2, -1] = Operators.creation.T
|
|
225
237
|
|
|
226
|
-
fac[2::2,
|
|
238
|
+
fac[2::2, :2, :2, 0] = (
|
|
227
239
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
228
240
|
* Operators.creation.T
|
|
229
241
|
)
|
|
230
|
-
fac[3::2,
|
|
242
|
+
fac[3::2, :2, :2, 0] = (
|
|
231
243
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
232
244
|
* Operators.creation
|
|
233
245
|
)
|
|
@@ -236,8 +248,8 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
236
248
|
j = 2
|
|
237
249
|
for current_left_interaction in current_left_interactions.nonzero().flatten():
|
|
238
250
|
if left_interactions_to_keep[current_left_interaction]:
|
|
239
|
-
fac[i, :, :, j] =
|
|
240
|
-
fac[i + 1, :, :, j + 1] =
|
|
251
|
+
fac[i, :, :, j] = self.identity
|
|
252
|
+
fac[i + 1, :, :, j + 1] = self.identity
|
|
241
253
|
j += 2
|
|
242
254
|
i += 2
|
|
243
255
|
return fac
|
|
@@ -249,28 +261,28 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
249
261
|
|
|
250
262
|
fac = torch.zeros(
|
|
251
263
|
int(2 * current_left_interactions.sum().item() + 2),
|
|
252
|
-
|
|
253
|
-
|
|
264
|
+
self.dim,
|
|
265
|
+
self.dim,
|
|
254
266
|
int(2 * current_right_interactions.sum().item() + 2),
|
|
255
267
|
dtype=dtype,
|
|
256
268
|
)
|
|
257
269
|
|
|
258
|
-
fac[0, :, :, 0] =
|
|
259
|
-
fac[1, :, :, 1] =
|
|
270
|
+
fac[0, :, :, 0] = self.identity
|
|
271
|
+
fac[1, :, :, 1] = self.identity
|
|
260
272
|
|
|
261
|
-
fac[2::2,
|
|
273
|
+
fac[2::2, :2, :2, 0] = (
|
|
262
274
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
263
275
|
* Operators.creation.T
|
|
264
276
|
)
|
|
265
|
-
fac[3::2,
|
|
277
|
+
fac[3::2, :2, :2, 0] = (
|
|
266
278
|
self.interaction_matrix[:n][current_left_interactions, n, None, None]
|
|
267
279
|
* Operators.creation
|
|
268
280
|
)
|
|
269
281
|
|
|
270
|
-
fac[1,
|
|
282
|
+
fac[1, :2, :2, 2::2] = self.interaction_matrix[n + 1 :][
|
|
271
283
|
None, None, current_right_interactions, n
|
|
272
284
|
] * Operators.creation.unsqueeze(-1)
|
|
273
|
-
fac[1,
|
|
285
|
+
fac[1, :2, :2, 3::2] = self.interaction_matrix[n + 1 :][
|
|
274
286
|
None, None, current_right_interactions, n
|
|
275
287
|
] * Operators.creation.T.unsqueeze(-1)
|
|
276
288
|
|
|
@@ -278,13 +290,13 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
278
290
|
self.interaction_matrix[:n, n + 1 :][current_left_interactions, :][
|
|
279
291
|
:, None, None, current_right_interactions
|
|
280
292
|
]
|
|
281
|
-
*
|
|
293
|
+
* self.identity[None, ..., None]
|
|
282
294
|
)
|
|
283
295
|
fac[3::2, :, :, 3::2] = (
|
|
284
296
|
self.interaction_matrix[:n, n + 1 :][current_left_interactions, :][
|
|
285
297
|
:, None, None, current_right_interactions
|
|
286
298
|
]
|
|
287
|
-
*
|
|
299
|
+
* self.identity[None, ..., None]
|
|
288
300
|
)
|
|
289
301
|
|
|
290
302
|
return fac
|
|
@@ -300,22 +312,22 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
300
312
|
+ 2 * int(has_left_interaction)
|
|
301
313
|
+ 2
|
|
302
314
|
),
|
|
303
|
-
|
|
304
|
-
|
|
315
|
+
self.dim,
|
|
316
|
+
self.dim,
|
|
305
317
|
int(2 * current_right_interactions.sum().item() + 2),
|
|
306
318
|
dtype=dtype,
|
|
307
319
|
)
|
|
308
320
|
|
|
309
|
-
fac[0, :, :, 0] =
|
|
310
|
-
fac[1, :, :, 1] =
|
|
321
|
+
fac[0, :, :, 0] = self.identity
|
|
322
|
+
fac[1, :, :, 1] = self.identity
|
|
311
323
|
if has_left_interaction:
|
|
312
|
-
fac[2,
|
|
313
|
-
fac[3,
|
|
324
|
+
fac[2, :2, :2, 0] = Operators.creation.T
|
|
325
|
+
fac[3, :2, :2, 0] = Operators.creation
|
|
314
326
|
|
|
315
|
-
fac[1,
|
|
327
|
+
fac[1, :2, :2, 2::2] = self.interaction_matrix[n + 1 :][
|
|
316
328
|
None, None, current_right_interactions, n
|
|
317
329
|
] * Operators.creation.unsqueeze(-1)
|
|
318
|
-
fac[1,
|
|
330
|
+
fac[1, :2, :2, 3::2] = self.interaction_matrix[n + 1 :][
|
|
319
331
|
None, None, current_right_interactions, n
|
|
320
332
|
] * Operators.creation.T.unsqueeze(-1)
|
|
321
333
|
|
|
@@ -323,23 +335,25 @@ class XYHamiltonianMPOFactors(HamiltonianMPOFactors):
|
|
|
323
335
|
j = 2
|
|
324
336
|
for current_right_interaction in current_right_interactions.nonzero().flatten():
|
|
325
337
|
if right_interactions_to_keep[current_right_interaction]:
|
|
326
|
-
fac[i, :, :, j] =
|
|
327
|
-
fac[i + 1, :, :, j + 1] =
|
|
338
|
+
fac[i, :, :, j] = self.identity
|
|
339
|
+
fac[i + 1, :, :, j + 1] = self.identity
|
|
328
340
|
i += 2
|
|
329
341
|
j += 2
|
|
330
342
|
return fac
|
|
331
343
|
|
|
332
344
|
def last_factor(self) -> torch.Tensor:
|
|
333
345
|
has_left_interaction = self.interaction_matrix[-1, :-1].any()
|
|
334
|
-
fac = torch.zeros(
|
|
335
|
-
|
|
346
|
+
fac = torch.zeros(
|
|
347
|
+
4 if has_left_interaction else 2, self.dim, self.dim, 1, dtype=dtype
|
|
348
|
+
)
|
|
349
|
+
fac[0, :, :, 0] = self.identity
|
|
336
350
|
if has_left_interaction:
|
|
337
351
|
if self.qubit_count >= 3:
|
|
338
|
-
fac[2,
|
|
339
|
-
fac[3,
|
|
352
|
+
fac[2, :2, :2, 0] = Operators.creation.T
|
|
353
|
+
fac[3, :2, :2, 0] = Operators.creation
|
|
340
354
|
else:
|
|
341
|
-
fac[2,
|
|
342
|
-
fac[3,
|
|
355
|
+
fac[2, :2, :2, 0] = self.interaction_matrix[0, 1] * Operators.creation.T
|
|
356
|
+
fac[3, :2, :2, 0] = self.interaction_matrix[0, 1] * Operators.creation
|
|
343
357
|
|
|
344
358
|
return fac
|
|
345
359
|
|
|
@@ -348,6 +362,7 @@ def make_H(
|
|
|
348
362
|
*,
|
|
349
363
|
interaction_matrix: torch.Tensor, # depends on Hamiltonian Type
|
|
350
364
|
hamiltonian_type: HamiltonianType,
|
|
365
|
+
dim: int = 2,
|
|
351
366
|
num_gpus_to_use: int | None,
|
|
352
367
|
) -> MPO:
|
|
353
368
|
r"""
|
|
@@ -357,53 +372,58 @@ def make_H(
|
|
|
357
372
|
The Hamiltonian H is given by:
|
|
358
373
|
H = ∑ⱼΩⱼ[cos(ϕⱼ)σˣⱼ + sin(ϕⱼ)σʸⱼ] - ∑ⱼΔⱼnⱼ + ∑ᵢ﹥ⱼC⁶/rᵢⱼ⁶ nᵢnⱼ
|
|
359
374
|
|
|
360
|
-
If noise is considered, the Hamiltonian includes an additional term to
|
|
361
|
-
the Monte Carlo WaveFunction algorithm:
|
|
375
|
+
If noise is considered, the Hamiltonian includes an additional term to
|
|
376
|
+
support the Monte Carlo WaveFunction algorithm:
|
|
362
377
|
H = ∑ⱼΩⱼ[cos(ϕⱼ)σˣⱼ + sin(ϕⱼ)σʸⱼ] - ∑ⱼΔⱼnⱼ + ∑ᵢ﹥ⱼC⁶/rᵢⱼ⁶ nᵢnⱼ - 0.5i∑ₘ ∑ᵤ Lₘᵘ⁺ Lₘᵘ
|
|
363
|
-
where Lₘᵘ are the Lindblad operators representing the noise,
|
|
364
|
-
and u for the number of atoms
|
|
378
|
+
where Lₘᵘ are the Lindblad operators representing the noise,
|
|
379
|
+
m for noise channel and u for the number of atoms
|
|
365
380
|
|
|
366
|
-
make_H constructs an MPO of the appropriate size, but the single qubit
|
|
381
|
+
make_H constructs an MPO of the appropriate size, but the single qubit
|
|
382
|
+
terms are left at zero.
|
|
367
383
|
To fill in the appropriate values, call update_H
|
|
368
384
|
|
|
369
385
|
Args:
|
|
370
|
-
interaction_matrix (torch.Tensor): The interaction matrix describing
|
|
371
|
-
between qubits.
|
|
372
|
-
|
|
386
|
+
interaction_matrix (torch.Tensor): The interaction matrix describing
|
|
387
|
+
the interactions between qubits.
|
|
388
|
+
hamiltonian_type: whether to use XY or Rydberg interation
|
|
389
|
+
dim: dimension of the basis (2 or 3)
|
|
390
|
+
num_gpus_to_use (int): how many gpus to put the Hamiltonian on.
|
|
391
|
+
See utils.assign_devices
|
|
373
392
|
Returns:
|
|
374
|
-
MPO: A Matrix Product Operator (MPO) representing the specified
|
|
393
|
+
MPO: A Matrix Product Operator (MPO) representing the specified
|
|
394
|
+
Hamiltonian.
|
|
375
395
|
|
|
376
396
|
Note:
|
|
377
397
|
For more information about the Hamiltonian and its usage, refer to the
|
|
378
398
|
[Pulser documentation](https://pulser.readthedocs.io/en/stable/conventions.html#hamiltonians).
|
|
379
399
|
|
|
380
400
|
"""
|
|
401
|
+
|
|
381
402
|
if hamiltonian_type == HamiltonianType.Rydberg:
|
|
382
403
|
return MPO(
|
|
383
|
-
list(RydbergHamiltonianMPOFactors(interaction_matrix)),
|
|
404
|
+
list(RydbergHamiltonianMPOFactors(interaction_matrix, dim=dim)),
|
|
384
405
|
num_gpus_to_use=num_gpus_to_use,
|
|
385
406
|
)
|
|
386
407
|
|
|
387
408
|
if hamiltonian_type == HamiltonianType.XY:
|
|
388
409
|
return MPO(
|
|
389
|
-
list(XYHamiltonianMPOFactors(interaction_matrix)),
|
|
410
|
+
list(XYHamiltonianMPOFactors(interaction_matrix, dim=dim)),
|
|
390
411
|
num_gpus_to_use=num_gpus_to_use,
|
|
391
412
|
)
|
|
392
413
|
|
|
393
|
-
raise ValueError(f"Unsupported hamiltonian type {hamiltonian_type}")
|
|
394
|
-
|
|
395
414
|
|
|
396
415
|
def update_H(
|
|
397
416
|
hamiltonian: MPO,
|
|
398
417
|
omega: torch.Tensor,
|
|
399
418
|
delta: torch.Tensor,
|
|
400
419
|
phi: torch.Tensor,
|
|
401
|
-
noise: torch.Tensor
|
|
420
|
+
noise: torch.Tensor,
|
|
402
421
|
) -> None:
|
|
403
422
|
"""
|
|
404
423
|
The single qubit operators in the Hamiltonian,
|
|
405
|
-
corresponding to the omega, delta, phi parameters and the aggregated
|
|
406
|
-
have a well-determined position in the factors of
|
|
424
|
+
corresponding to the omega, delta, phi parameters and the aggregated
|
|
425
|
+
Lindblad operators have a well-determined position in the factors of
|
|
426
|
+
the Hamiltonian.
|
|
407
427
|
This function updates this part of the factors to update the
|
|
408
428
|
Hamiltonian with new parameters without rebuilding the entire thing.
|
|
409
429
|
See make_H for details about the Hamiltonian.
|
|
@@ -416,20 +436,24 @@ def update_H(
|
|
|
416
436
|
phi (torch.Tensor): The phase ϕⱼ corresponding to each qubit.
|
|
417
437
|
noise (torch.Tensor, optional): The single-qubit noise
|
|
418
438
|
term -0.5i∑ⱼLⱼ†Lⱼ applied to all qubits.
|
|
419
|
-
This can be computed using the `compute_noise_from_lindbladians`
|
|
439
|
+
This can be computed using the `compute_noise_from_lindbladians`
|
|
440
|
+
function.
|
|
420
441
|
Defaults to a zero tensor.
|
|
421
442
|
"""
|
|
422
443
|
|
|
423
|
-
assert noise.shape == (2, 2)
|
|
444
|
+
assert noise.shape == (2, 2) or (3, 3)
|
|
424
445
|
nqubits = omega.size(dim=0)
|
|
425
446
|
|
|
426
447
|
a = torch.tensordot(omega * torch.cos(phi), Operators.sx, dims=0)
|
|
427
|
-
c = torch.tensordot(delta, Operators.
|
|
448
|
+
c = torch.tensordot(delta, Operators.n, dims=0)
|
|
428
449
|
b = torch.tensordot(omega * torch.sin(phi), Operators.sy, dims=0)
|
|
429
450
|
|
|
430
|
-
single_qubit_terms = a + b - c + noise
|
|
431
451
|
factors = hamiltonian.factors
|
|
432
452
|
|
|
453
|
+
single_qubit_terms = torch.stack(nqubits * [noise])
|
|
454
|
+
|
|
455
|
+
single_qubit_terms[:, :2, :2] += a + b - c
|
|
456
|
+
|
|
433
457
|
factors[0][0, :, :, 0] = single_qubit_terms[0]
|
|
434
458
|
for i in range(1, nqubits):
|
|
435
459
|
factors[i][1, :, :, 0] = single_qubit_terms[i]
|
emu_mps/mpo.py
CHANGED
|
@@ -10,6 +10,8 @@ from pulser.backend.operator import FullOp, QuditOp
|
|
|
10
10
|
from emu_mps.mps import MPS, DEFAULT_MAX_BOND_DIM, DEFAULT_PRECISION
|
|
11
11
|
from emu_mps.utils import new_left_bath, assign_devices
|
|
12
12
|
|
|
13
|
+
dtype = torch.complex128
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
class MPO(Operator[complex, torch.Tensor, MPS]):
|
|
15
17
|
"""
|
|
@@ -30,7 +32,8 @@ class MPO(Operator[complex, torch.Tensor, MPS]):
|
|
|
30
32
|
raise ValueError("For 1 qubit states, do state vector")
|
|
31
33
|
if factors[0].shape[0] != 1 or factors[-1].shape[-1] != 1:
|
|
32
34
|
raise ValueError(
|
|
33
|
-
"The dimension of the left (right) link of the first (last)
|
|
35
|
+
"The dimension of the left (right) link of the first (last) "
|
|
36
|
+
"tensor should be 1"
|
|
34
37
|
)
|
|
35
38
|
assert all(
|
|
36
39
|
factors[i - 1].shape[-1] == factors[i].shape[0]
|
|
@@ -164,42 +167,75 @@ class MPO(Operator[complex, torch.Tensor, MPS]):
|
|
|
164
167
|
"""
|
|
165
168
|
|
|
166
169
|
basis = set(eigenstates)
|
|
167
|
-
|
|
170
|
+
dim = len(basis)
|
|
168
171
|
operators_with_tensors: dict[str, torch.Tensor | QuditOp]
|
|
169
172
|
if basis == {"r", "g"}:
|
|
170
|
-
# operators_with_tensors will now contain the basis for single
|
|
171
|
-
# and potentially user defined strings in terms of these
|
|
173
|
+
# operators_with_tensors will now contain the basis for single
|
|
174
|
+
# qubit ops, and potentially user defined strings in terms of these
|
|
172
175
|
operators_with_tensors = {
|
|
173
|
-
"gg": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=
|
|
174
|
-
1,
|
|
176
|
+
"gg": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=dtype).view(
|
|
177
|
+
1, dim, dim, 1
|
|
175
178
|
),
|
|
176
|
-
"rg": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=
|
|
179
|
+
"rg": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=dtype).view(
|
|
177
180
|
1, 2, 2, 1
|
|
178
181
|
),
|
|
179
|
-
"gr": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=
|
|
182
|
+
"gr": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=dtype).view(
|
|
180
183
|
1, 2, 2, 1
|
|
181
184
|
),
|
|
182
|
-
"rr": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=
|
|
183
|
-
1,
|
|
185
|
+
"rr": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=dtype).view(
|
|
186
|
+
1, dim, dim, 1
|
|
184
187
|
),
|
|
185
188
|
}
|
|
186
189
|
elif basis == {"0", "1"}:
|
|
187
|
-
# operators_with_tensors will now contain the basis for single
|
|
188
|
-
# and potentially user defined strings in terms of these
|
|
190
|
+
# operators_with_tensors will now contain the basis for single
|
|
191
|
+
# qubit ops, and potentially user defined strings in terms of these
|
|
189
192
|
operators_with_tensors = {
|
|
190
|
-
"00": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=
|
|
191
|
-
1,
|
|
193
|
+
"00": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=dtype).view(
|
|
194
|
+
1, dim, dim, 1
|
|
192
195
|
),
|
|
193
|
-
"10": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=
|
|
196
|
+
"10": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=dtype).view(
|
|
194
197
|
1, 2, 2, 1
|
|
195
198
|
),
|
|
196
|
-
"01": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=
|
|
199
|
+
"01": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=dtype).view(
|
|
197
200
|
1, 2, 2, 1
|
|
198
201
|
),
|
|
199
|
-
"11": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=
|
|
200
|
-
1,
|
|
202
|
+
"11": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=dtype).view(
|
|
203
|
+
1, dim, dim, 1
|
|
201
204
|
),
|
|
202
205
|
}
|
|
206
|
+
elif basis == {"r", "g", "x"}:
|
|
207
|
+
# operators_with_tensors will now contain the basis for single
|
|
208
|
+
# qubit ops, and potentially user defined strings in terms of these
|
|
209
|
+
operators_with_tensors = {
|
|
210
|
+
"gg": torch.tensor(
|
|
211
|
+
[[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
212
|
+
).view(1, dim, dim, 1),
|
|
213
|
+
"gr": torch.tensor(
|
|
214
|
+
[[0.0, 1.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
215
|
+
).view(1, dim, dim, 1),
|
|
216
|
+
"rg": torch.tensor(
|
|
217
|
+
[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
218
|
+
).view(1, dim, dim, 1),
|
|
219
|
+
"rr": torch.tensor(
|
|
220
|
+
[[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
221
|
+
).view(1, dim, dim, 1),
|
|
222
|
+
"xx": torch.tensor(
|
|
223
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 1.0]], dtype=dtype
|
|
224
|
+
).view(1, dim, dim, 1),
|
|
225
|
+
"xg": torch.tensor(
|
|
226
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0]], dtype=dtype
|
|
227
|
+
).view(1, dim, dim, 1),
|
|
228
|
+
"xr": torch.tensor(
|
|
229
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]], dtype=dtype
|
|
230
|
+
).view(1, dim, dim, 1),
|
|
231
|
+
"gx": torch.tensor(
|
|
232
|
+
[[0.0, 0.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
233
|
+
).view(1, dim, dim, 1),
|
|
234
|
+
"rx": torch.tensor(
|
|
235
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0, 0.0]], dtype=dtype
|
|
236
|
+
).view(1, dim, dim, 1),
|
|
237
|
+
}
|
|
238
|
+
|
|
203
239
|
else:
|
|
204
240
|
raise ValueError("Unsupported basis provided")
|
|
205
241
|
|
|
@@ -212,16 +248,14 @@ class MPO(Operator[complex, torch.Tensor, MPS]):
|
|
|
212
248
|
if isinstance(op, torch.Tensor):
|
|
213
249
|
return op
|
|
214
250
|
|
|
215
|
-
result = torch.zeros(1,
|
|
251
|
+
result = torch.zeros(1, dim, dim, 1, dtype=dtype)
|
|
216
252
|
for opstr, coeff in op.items():
|
|
217
253
|
tensor = replace_operator_string(operators_with_tensors[opstr])
|
|
218
254
|
operators_with_tensors[opstr] = tensor
|
|
219
255
|
result += tensor * coeff
|
|
220
256
|
return result
|
|
221
257
|
|
|
222
|
-
factors = [
|
|
223
|
-
torch.eye(2, 2, dtype=torch.complex128).view(1, 2, 2, 1)
|
|
224
|
-
] * n_qudits
|
|
258
|
+
factors = [torch.eye(dim, dim, dtype=dtype).view(1, dim, dim, 1)] * n_qudits
|
|
225
259
|
|
|
226
260
|
for op in tensorop:
|
|
227
261
|
factor = replace_operator_string(op[0])
|
emu_mps/mps.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
import math
|
|
3
3
|
from collections import Counter
|
|
4
4
|
from typing import List, Optional, Sequence, TypeVar, Mapping
|
|
5
|
-
|
|
5
|
+
import logging
|
|
6
6
|
import torch
|
|
7
7
|
|
|
8
8
|
from pulser.backend.state import State, Eigenstate
|
|
@@ -481,7 +481,9 @@ class MPS(State[complex, torch.Tensor]):
|
|
|
481
481
|
accum_mps += amplitude * MPS(factors, eigenstates=eigenstates)
|
|
482
482
|
norm = accum_mps.norm()
|
|
483
483
|
if not math.isclose(1.0, norm, rel_tol=1e-5, abs_tol=0.0):
|
|
484
|
-
|
|
484
|
+
logging.getLogger("emulators").warning(
|
|
485
|
+
"\nThe state is not normalized, normalizing it for you."
|
|
486
|
+
)
|
|
485
487
|
accum_mps *= 1 / norm
|
|
486
488
|
|
|
487
489
|
return accum_mps, amplitudes
|
emu_mps/mps_backend.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from pulser.backend import EmulatorBackend, Results
|
|
2
2
|
from emu_mps.mps_config import MPSConfig
|
|
3
|
+
from emu_base import init_logging
|
|
3
4
|
from emu_mps.mps_backend_impl import create_impl, MPSBackendImpl
|
|
4
5
|
import pickle
|
|
5
6
|
import os
|
|
6
7
|
import time
|
|
7
|
-
import logging
|
|
8
8
|
import pathlib
|
|
9
9
|
|
|
10
10
|
|
|
@@ -34,9 +34,9 @@ class MPSBackend(EmulatorBackend):
|
|
|
34
34
|
|
|
35
35
|
impl.autosave_file = autosave_file
|
|
36
36
|
impl.last_save_time = time.time()
|
|
37
|
-
impl.config.
|
|
37
|
+
init_logging(impl.config.log_level, impl.config.log_file)
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
impl.config.logger.warning(
|
|
40
40
|
f"Resuming simulation from file {autosave_file}\n"
|
|
41
41
|
f"Saving simulation state every {impl.config.autosave_dt} seconds"
|
|
42
42
|
)
|
emu_mps/mps_backend_impl.py
CHANGED
|
@@ -42,6 +42,8 @@ from emu_mps.utils import (
|
|
|
42
42
|
new_left_bath,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
+
dtype = torch.complex128
|
|
46
|
+
|
|
45
47
|
|
|
46
48
|
class Statistics(Observable):
|
|
47
49
|
def __init__(
|
|
@@ -117,7 +119,8 @@ class MPSBackendImpl:
|
|
|
117
119
|
self.phi = pulser_data.phi
|
|
118
120
|
self.timestep_count: int = self.omega.shape[0]
|
|
119
121
|
self.has_lindblad_noise = pulser_data.has_lindblad_noise
|
|
120
|
-
self.
|
|
122
|
+
self.dim = pulser_data.dim
|
|
123
|
+
self.lindblad_noise = torch.zeros(self.dim, self.dim, dtype=dtype)
|
|
121
124
|
self.qubit_permutation = (
|
|
122
125
|
optimat.minimize_bandwidth(pulser_data.full_interaction_matrix)
|
|
123
126
|
if self.config.optimize_qubit_ordering
|
|
@@ -126,6 +129,7 @@ class MPSBackendImpl:
|
|
|
126
129
|
self.full_interaction_matrix = optimat.permute_tensor(
|
|
127
130
|
pulser_data.full_interaction_matrix, self.qubit_permutation
|
|
128
131
|
)
|
|
132
|
+
|
|
129
133
|
self.masked_interaction_matrix = optimat.permute_tensor(
|
|
130
134
|
pulser_data.masked_interaction_matrix, self.qubit_permutation
|
|
131
135
|
)
|
|
@@ -276,9 +280,7 @@ class MPSBackendImpl:
|
|
|
276
280
|
|
|
277
281
|
def init_baths(self) -> None:
|
|
278
282
|
self.left_baths = [
|
|
279
|
-
torch.ones(
|
|
280
|
-
1, 1, 1, dtype=torch.complex128, device=self.state.factors[0].device
|
|
281
|
-
)
|
|
283
|
+
torch.ones(1, 1, 1, dtype=dtype, device=self.state.factors[0].device)
|
|
282
284
|
]
|
|
283
285
|
self.right_baths = right_baths(self.state, self.hamiltonian, final_qubit=2)
|
|
284
286
|
assert len(self.right_baths) == self.qubit_count - 1
|
|
@@ -451,6 +453,7 @@ class MPSBackendImpl:
|
|
|
451
453
|
self.hamiltonian = make_H(
|
|
452
454
|
interaction_matrix=self.full_interaction_matrix,
|
|
453
455
|
hamiltonian_type=self.hamiltonian_type,
|
|
456
|
+
dim=self.dim,
|
|
454
457
|
num_gpus_to_use=self.resolved_num_gpus,
|
|
455
458
|
)
|
|
456
459
|
|
|
@@ -686,6 +689,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
|
|
|
686
689
|
omega=self.omega[self.timestep_index - 1, :], # Meh
|
|
687
690
|
delta=self.delta[self.timestep_index - 1, :],
|
|
688
691
|
phi=self.phi[self.timestep_index - 1, :],
|
|
692
|
+
noise=torch.zeros(self.dim, self.dim, dtype=dtype), # no noise
|
|
689
693
|
)
|
|
690
694
|
|
|
691
695
|
super().fill_results()
|
emu_mps/mps_config.py
CHANGED
|
@@ -3,6 +3,7 @@ from types import MethodType
|
|
|
3
3
|
|
|
4
4
|
import copy
|
|
5
5
|
|
|
6
|
+
from emu_base import init_logging
|
|
6
7
|
from emu_mps.mps import MPS, DEFAULT_MAX_BOND_DIM, DEFAULT_PRECISION
|
|
7
8
|
from emu_mps.mpo import MPO
|
|
8
9
|
from emu_mps.solver import Solver
|
|
@@ -24,7 +25,6 @@ from pulser.backend import (
|
|
|
24
25
|
)
|
|
25
26
|
import logging
|
|
26
27
|
import pathlib
|
|
27
|
-
import sys
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class MPSConfig(EmulationConfig):
|
|
@@ -90,7 +90,7 @@ class MPSConfig(EmulationConfig):
|
|
|
90
90
|
max_krylov_dim: int = 100,
|
|
91
91
|
extra_krylov_tolerance: float = 1e-3,
|
|
92
92
|
num_gpus_to_use: int | None = None,
|
|
93
|
-
optimize_qubit_ordering: bool =
|
|
93
|
+
optimize_qubit_ordering: bool = True,
|
|
94
94
|
interaction_cutoff: float = 0.0,
|
|
95
95
|
log_level: int = logging.INFO,
|
|
96
96
|
log_file: pathlib.Path | None = None,
|
|
@@ -116,8 +116,6 @@ class MPSConfig(EmulationConfig):
|
|
|
116
116
|
solver=solver,
|
|
117
117
|
**kwargs,
|
|
118
118
|
)
|
|
119
|
-
if self.optimize_qubit_ordering:
|
|
120
|
-
self.check_permutable_observables()
|
|
121
119
|
|
|
122
120
|
MIN_AUTOSAVE_DT = 10
|
|
123
121
|
assert (
|
|
@@ -125,20 +123,8 @@ class MPSConfig(EmulationConfig):
|
|
|
125
123
|
), f"autosave_dt must be larger than {MIN_AUTOSAVE_DT} seconds"
|
|
126
124
|
|
|
127
125
|
self.monkeypatch_observables()
|
|
126
|
+
self.logger = init_logging(log_level, log_file)
|
|
128
127
|
|
|
129
|
-
self.logger = logging.getLogger("global_logger")
|
|
130
|
-
if log_file is None:
|
|
131
|
-
logging.basicConfig(
|
|
132
|
-
level=log_level, format="%(message)s", stream=sys.stdout, force=True
|
|
133
|
-
) # default to stream = sys.stderr
|
|
134
|
-
else:
|
|
135
|
-
logging.basicConfig(
|
|
136
|
-
level=log_level,
|
|
137
|
-
format="%(message)s",
|
|
138
|
-
filename=str(log_file),
|
|
139
|
-
filemode="w",
|
|
140
|
-
force=True,
|
|
141
|
-
)
|
|
142
128
|
if (self.noise_model.runs != 1 and self.noise_model.runs is not None) or (
|
|
143
129
|
self.noise_model.samples_per_run != 1
|
|
144
130
|
and self.noise_model.samples_per_run is not None
|
|
@@ -146,6 +132,9 @@ class MPSConfig(EmulationConfig):
|
|
|
146
132
|
self.logger.warning(
|
|
147
133
|
"Warning: The runs and samples_per_run values of the NoiseModel are ignored!"
|
|
148
134
|
)
|
|
135
|
+
self._backend_options[
|
|
136
|
+
"optimize_qubit_ordering"
|
|
137
|
+
] &= self.check_permutable_observables()
|
|
149
138
|
|
|
150
139
|
def _expected_kwargs(self) -> set[str]:
|
|
151
140
|
return super()._expected_kwargs() | {
|
|
@@ -191,21 +180,7 @@ class MPSConfig(EmulationConfig):
|
|
|
191
180
|
obs_list.append(obs_copy)
|
|
192
181
|
self.observables = tuple(obs_list)
|
|
193
182
|
|
|
194
|
-
def
|
|
195
|
-
if self.log_file is None:
|
|
196
|
-
logging.basicConfig(
|
|
197
|
-
level=self.log_level, format="%(message)s", stream=sys.stdout, force=True
|
|
198
|
-
) # default to stream = sys.stderr
|
|
199
|
-
else:
|
|
200
|
-
logging.basicConfig(
|
|
201
|
-
level=self.log_level,
|
|
202
|
-
format="%(message)s",
|
|
203
|
-
filename=str(self.log_file),
|
|
204
|
-
filemode="w",
|
|
205
|
-
force=True,
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
def check_permutable_observables(self) -> None:
|
|
183
|
+
def check_permutable_observables(self) -> bool:
|
|
209
184
|
allowed_permutable_obs = set(
|
|
210
185
|
[
|
|
211
186
|
"bitstrings",
|
|
@@ -221,10 +196,10 @@ class MPSConfig(EmulationConfig):
|
|
|
221
196
|
actual_obs = set([obs._base_tag for obs in self.observables])
|
|
222
197
|
not_allowed = actual_obs.difference(allowed_permutable_obs)
|
|
223
198
|
if not_allowed:
|
|
224
|
-
|
|
199
|
+
self.logger.warning(
|
|
225
200
|
f"emu-mps allows only {allowed_permutable_obs} observables with"
|
|
226
201
|
" `optimize_qubit_ordering = True`."
|
|
227
202
|
f" you provided unsupported {not_allowed}"
|
|
228
|
-
"
|
|
229
|
-
" `optimize_qubit_ordering = False` in `MPSConfig()`."
|
|
203
|
+
" using `optimize_qubit_ordering = False` instead."
|
|
230
204
|
)
|
|
205
|
+
return not_allowed == set()
|
emu_mps/solver_utils.py
CHANGED
|
@@ -5,7 +5,7 @@ from emu_base import krylov_exp
|
|
|
5
5
|
from emu_base.math.krylov_energy_min import krylov_energy_minimization
|
|
6
6
|
from emu_base.utils import deallocate_tensor
|
|
7
7
|
from emu_mps import MPS, MPO
|
|
8
|
-
from emu_mps.utils import
|
|
8
|
+
from emu_mps.utils import split_matrix
|
|
9
9
|
from emu_mps.mps_config import MPSConfig
|
|
10
10
|
|
|
11
11
|
|
|
@@ -178,11 +178,12 @@ def evolve_pair(
|
|
|
178
178
|
is_hermitian=is_hermitian,
|
|
179
179
|
).view(left_bond_dim * 2, 2 * right_bond_dim)
|
|
180
180
|
|
|
181
|
-
l, r =
|
|
181
|
+
l, r = split_matrix(
|
|
182
182
|
evol,
|
|
183
183
|
max_error=config.precision,
|
|
184
184
|
max_rank=config.max_bond_dim,
|
|
185
185
|
orth_center_right=orth_center_right,
|
|
186
|
+
preserve_norm=not is_hermitian, # only relevant for computing jump times
|
|
186
187
|
)
|
|
187
188
|
|
|
188
189
|
return l.view(left_bond_dim, 2, -1), r.view(-1, 2, right_bond_dim).to(right_device)
|
|
@@ -262,7 +263,7 @@ def minimize_energy_pair(
|
|
|
262
263
|
)
|
|
263
264
|
updated_state = updated_state.view(left_bond_dim * 2, 2 * right_bond_dim)
|
|
264
265
|
|
|
265
|
-
l, r =
|
|
266
|
+
l, r = split_matrix(
|
|
266
267
|
updated_state,
|
|
267
268
|
max_error=config.precision,
|
|
268
269
|
max_rank=config.max_bond_dim,
|
emu_mps/utils.py
CHANGED
|
@@ -23,11 +23,12 @@ def _determine_cutoff_index(d: torch.Tensor, max_error: float) -> int:
|
|
|
23
23
|
return 0 # type: ignore[no-any-return]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
26
|
+
def split_matrix(
|
|
27
27
|
m: torch.Tensor,
|
|
28
28
|
max_error: float = 1e-5,
|
|
29
29
|
max_rank: int = 1024,
|
|
30
30
|
orth_center_right: bool = True,
|
|
31
|
+
preserve_norm: bool = False,
|
|
31
32
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
32
33
|
"""
|
|
33
34
|
Computes a low-rank approximation split of m using the Eckart-Young-Mirsky theorem.
|
|
@@ -41,8 +42,11 @@ def split_tensor(
|
|
|
41
42
|
d.shape[0] - max_rank,
|
|
42
43
|
)
|
|
43
44
|
left = q[:, max_bond:]
|
|
44
|
-
right =
|
|
45
|
-
|
|
45
|
+
right = left.T.conj() @ m
|
|
46
|
+
if preserve_norm:
|
|
47
|
+
old_norm2 = torch.sum(d)
|
|
48
|
+
new_norm2 = torch.sum(d[max_bond:])
|
|
49
|
+
right *= torch.sqrt(old_norm2 / new_norm2)
|
|
46
50
|
else:
|
|
47
51
|
d, q = torch.linalg.eigh(m.T.conj() @ m)
|
|
48
52
|
max_bond = max(
|
|
@@ -50,8 +54,11 @@ def split_tensor(
|
|
|
50
54
|
d.shape[0] - max_rank,
|
|
51
55
|
)
|
|
52
56
|
right = q[:, max_bond:].T.conj_physical()
|
|
53
|
-
left = m @ q
|
|
54
|
-
|
|
57
|
+
left = m @ q[:, max_bond:]
|
|
58
|
+
if preserve_norm:
|
|
59
|
+
old_norm2 = torch.sum(d)
|
|
60
|
+
new_norm2 = torch.sum(d[max_bond:])
|
|
61
|
+
left *= torch.sqrt(old_norm2 / new_norm2)
|
|
55
62
|
|
|
56
63
|
return left, right
|
|
57
64
|
|
|
@@ -71,7 +78,7 @@ def truncate_impl(
|
|
|
71
78
|
for i in range(len(factors) - 1, 0, -1):
|
|
72
79
|
factor_shape = factors[i].shape
|
|
73
80
|
|
|
74
|
-
l, r =
|
|
81
|
+
l, r = split_matrix(
|
|
75
82
|
factors[i].view(factor_shape[0], -1),
|
|
76
83
|
max_error=precision,
|
|
77
84
|
max_rank=max_bond_dim,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emu-mps
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: Pasqal MPS based pulse emulator built on PyTorch
|
|
5
5
|
Project-URL: Documentation, https://pasqal-io.github.io/emulators/
|
|
6
6
|
Project-URL: Repository, https://github.com/pasqal-io/emulators
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
25
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
26
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
|
-
Requires-Dist: emu-base==2.4.
|
|
28
|
+
Requires-Dist: emu-base==2.4.4
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
31
|
<div align="center">
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
emu_mps/__init__.py,sha256=xZGOemPMjLVSBvpfex6jcvAYl0SQRrlcVyP9n0j1nvo,708
|
|
2
|
+
emu_mps/algebra.py,sha256=VZ5uaX5PYWGqDCpRKKr819BLMMtT0pKDxc2HrlLCdgU,5423
|
|
3
|
+
emu_mps/custom_callback_implementations.py,sha256=WeczmO6qkvBIipvXLqX45i3D7M4ovOrepusIGs6d2Ts,2420
|
|
4
|
+
emu_mps/hamiltonian.py,sha256=OM0bPNZV7J5Egk6aTUwt4GaWqiUOP68ujZBTuqlBY1k,16289
|
|
5
|
+
emu_mps/mpo.py,sha256=WmGDGkCMhlODmydd0b09YcSRlsk6Bg5xYQ4rXSNJvnY,9703
|
|
6
|
+
emu_mps/mps.py,sha256=9LcneNCGMLYQJfiTQrDzBKz-YQmcL76P9l-A-3_bXOI,21707
|
|
7
|
+
emu_mps/mps_backend.py,sha256=dXcW5Fa2AAL_WlHUgr30WS2l3qiDJgBVebiVpyYWpPM,2064
|
|
8
|
+
emu_mps/mps_backend_impl.py,sha256=LMvi7KpfnsNmbiwQHuCtoA2lsK3Pf1vETuipL7BlG5U,30688
|
|
9
|
+
emu_mps/mps_config.py,sha256=btlygf6VDFna9TQus-zOmUqB6pfe_aoQmNoivfvxABk,8251
|
|
10
|
+
emu_mps/observables.py,sha256=4C_ewkd3YkJP0xghTrGUTgXUGvJRCQcetb8cU0SjMl0,1900
|
|
11
|
+
emu_mps/solver.py,sha256=M9xkHhlEouTBvoPw2UYVu6kij7CO4Z1FXw_SiGFtdgo,85
|
|
12
|
+
emu_mps/solver_utils.py,sha256=CklWjVvhngpyElpJyfUTncr4ErBP6VbUE3Z9nf8s3mM,8762
|
|
13
|
+
emu_mps/utils.py,sha256=rL75H55hB5lDMjy8a_O2PpJq51iZKjSx91X4euxB3mY,7293
|
|
14
|
+
emu_mps/optimatrix/__init__.py,sha256=fBXQ7-rgDro4hcaBijCGhx3J69W96qcw5_3mWc7tND4,364
|
|
15
|
+
emu_mps/optimatrix/optimiser.py,sha256=k9suYmKLKlaZ7ozFuIqvXHyCBoCtGgkX1mpen9GOdOo,6977
|
|
16
|
+
emu_mps/optimatrix/permutations.py,sha256=9DDMZtrGGZ01b9F3GkzHR3paX4qNtZiPoI7Z_Kia3Lc,3727
|
|
17
|
+
emu_mps-2.4.4.dist-info/METADATA,sha256=6iwSnm9w0kSDuUXbyy81ZBEwktkc0mjQa8K2GnVP0E8,3587
|
|
18
|
+
emu_mps-2.4.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
19
|
+
emu_mps-2.4.4.dist-info/RECORD,,
|
emu_mps-2.4.2.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
emu_mps/__init__.py,sha256=fEovcc3ow-4zoEodViCEyn2uZWnb-uwVSG-uRBs-g7U,708
|
|
2
|
-
emu_mps/algebra.py,sha256=VZ5uaX5PYWGqDCpRKKr819BLMMtT0pKDxc2HrlLCdgU,5423
|
|
3
|
-
emu_mps/custom_callback_implementations.py,sha256=WeczmO6qkvBIipvXLqX45i3D7M4ovOrepusIGs6d2Ts,2420
|
|
4
|
-
emu_mps/hamiltonian.py,sha256=gOPxNOBmk6jRPPjevERuCP_scGv0EKYeAJ0uxooihes,15622
|
|
5
|
-
emu_mps/mpo.py,sha256=-kbElxfodWh3IQa8QDor4TgLJE-zsaMRI5PZ6dZpYck,8049
|
|
6
|
-
emu_mps/mps.py,sha256=1aMvY4NQSBeAwzTwHRegXncGQqm-1g05W00OimFNzt8,21630
|
|
7
|
-
emu_mps/mps_backend.py,sha256=bS83qFxvdoK-c12_1WaPw6O7xUc7vdWifZNHUzNP5sM,2091
|
|
8
|
-
emu_mps/mps_backend_impl.py,sha256=B8hi21uHFUSVJ3k5J4A4fXqyQo4sNHSb2uddN3lUYPA,30558
|
|
9
|
-
emu_mps/mps_config.py,sha256=j7rho3edFzDxPO_VX7j5jc0Drw9wO2NWRymkqZ9hzmU,9128
|
|
10
|
-
emu_mps/observables.py,sha256=4C_ewkd3YkJP0xghTrGUTgXUGvJRCQcetb8cU0SjMl0,1900
|
|
11
|
-
emu_mps/solver.py,sha256=M9xkHhlEouTBvoPw2UYVu6kij7CO4Z1FXw_SiGFtdgo,85
|
|
12
|
-
emu_mps/solver_utils.py,sha256=EnNzEaUrtTMQbrWoqOy8vyDsQwlsfQCUc2HgOp4z8dk,8680
|
|
13
|
-
emu_mps/utils.py,sha256=pW5N_EbbGiOviQpJCw1a0pVgEDObP_InceNaIqY5bHE,6982
|
|
14
|
-
emu_mps/optimatrix/__init__.py,sha256=fBXQ7-rgDro4hcaBijCGhx3J69W96qcw5_3mWc7tND4,364
|
|
15
|
-
emu_mps/optimatrix/optimiser.py,sha256=k9suYmKLKlaZ7ozFuIqvXHyCBoCtGgkX1mpen9GOdOo,6977
|
|
16
|
-
emu_mps/optimatrix/permutations.py,sha256=9DDMZtrGGZ01b9F3GkzHR3paX4qNtZiPoI7Z_Kia3Lc,3727
|
|
17
|
-
emu_mps-2.4.2.dist-info/METADATA,sha256=SMYIZXDve_o9aZ7K1EfB7V6s8FBqA_wltE5a8ecNUf4,3587
|
|
18
|
-
emu_mps-2.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
19
|
-
emu_mps-2.4.2.dist-info/RECORD,,
|
|
File without changes
|