emu-mps 2.2.1__py3-none-any.whl → 2.4.0__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 +3 -4
- emu_mps/mps.py +2 -3
- emu_mps/mps_backend_impl.py +152 -45
- emu_mps/mps_config.py +9 -0
- emu_mps/solver.py +6 -0
- emu_mps/solver_utils.py +2 -2
- emu_mps/utils.py +3 -41
- {emu_mps-2.2.1.dist-info → emu_mps-2.4.0.dist-info}/METADATA +2 -2
- {emu_mps-2.2.1.dist-info → emu_mps-2.4.0.dist-info}/RECORD +10 -10
- emu_mps/noise.py +0 -9
- {emu_mps-2.2.1.dist-info → emu_mps-2.4.0.dist-info}/WHEEL +0 -0
emu_mps/__init__.py
CHANGED
|
@@ -9,12 +9,11 @@ from pulser.backend import (
|
|
|
9
9
|
StateResult,
|
|
10
10
|
EnergySecondMoment,
|
|
11
11
|
)
|
|
12
|
-
from .mps_config import MPSConfig
|
|
12
|
+
from .mps_config import MPSConfig, Solver
|
|
13
13
|
from .mpo import MPO
|
|
14
14
|
from .mps import MPS, inner
|
|
15
15
|
from .mps_backend import MPSBackend
|
|
16
16
|
from .observables import EntanglementEntropy
|
|
17
|
-
from emu_base import aggregate
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
__all__ = [
|
|
@@ -23,6 +22,7 @@ __all__ = [
|
|
|
23
22
|
"MPS",
|
|
24
23
|
"inner",
|
|
25
24
|
"MPSConfig",
|
|
25
|
+
"Solver",
|
|
26
26
|
"MPSBackend",
|
|
27
27
|
"StateResult",
|
|
28
28
|
"BitStrings",
|
|
@@ -33,8 +33,7 @@ __all__ = [
|
|
|
33
33
|
"Energy",
|
|
34
34
|
"EnergyVariance",
|
|
35
35
|
"EnergySecondMoment",
|
|
36
|
-
"aggregate",
|
|
37
36
|
"EntanglementEntropy",
|
|
38
37
|
]
|
|
39
38
|
|
|
40
|
-
__version__ = "2.
|
|
39
|
+
__version__ = "2.4.0"
|
emu_mps/mps.py
CHANGED
|
@@ -7,11 +7,10 @@ from typing import List, Optional, Sequence, TypeVar, Mapping
|
|
|
7
7
|
import torch
|
|
8
8
|
|
|
9
9
|
from pulser.backend.state import State, Eigenstate
|
|
10
|
-
from emu_base import DEVICE_COUNT
|
|
10
|
+
from emu_base import DEVICE_COUNT, apply_measurement_errors
|
|
11
11
|
from emu_mps import MPSConfig
|
|
12
12
|
from emu_mps.algebra import add_factors, scale_factors
|
|
13
13
|
from emu_mps.utils import (
|
|
14
|
-
apply_measurement_errors,
|
|
15
14
|
assign_devices,
|
|
16
15
|
truncate_impl,
|
|
17
16
|
tensor_trace,
|
|
@@ -154,7 +153,7 @@ class MPS(State[complex, torch.Tensor]):
|
|
|
154
153
|
|
|
155
154
|
for i in range(rl_swipe_start, desired_orthogonality_center, -1):
|
|
156
155
|
q, r = torch.linalg.qr(
|
|
157
|
-
self.factors[i].view(self.factors[i].shape[0], -1).mT,
|
|
156
|
+
self.factors[i].contiguous().view(self.factors[i].shape[0], -1).mT,
|
|
158
157
|
)
|
|
159
158
|
self.factors[i] = q.mT.view(-1, 2, self.factors[i].shape[2])
|
|
160
159
|
self.factors[i - 1] = torch.tensordot(
|
emu_mps/mps_backend_impl.py
CHANGED
|
@@ -10,7 +10,6 @@ import uuid
|
|
|
10
10
|
from copy import deepcopy
|
|
11
11
|
from collections import Counter
|
|
12
12
|
from enum import Enum, auto
|
|
13
|
-
from resource import RUSAGE_SELF, getrusage
|
|
14
13
|
from types import MethodType
|
|
15
14
|
from typing import Any, Optional
|
|
16
15
|
|
|
@@ -18,7 +17,7 @@ import torch
|
|
|
18
17
|
from pulser import Sequence
|
|
19
18
|
from pulser.backend import EmulationConfig, Observable, Results, State
|
|
20
19
|
|
|
21
|
-
from emu_base import DEVICE_COUNT, PulserData
|
|
20
|
+
from emu_base import DEVICE_COUNT, PulserData, get_max_rss
|
|
22
21
|
from emu_base.math.brents_root_finding import BrentsRootFinder
|
|
23
22
|
from emu_base.utils import deallocate_tensor
|
|
24
23
|
|
|
@@ -26,12 +25,13 @@ from emu_mps.hamiltonian import make_H, update_H
|
|
|
26
25
|
from emu_mps.mpo import MPO
|
|
27
26
|
from emu_mps.mps import MPS
|
|
28
27
|
from emu_mps.mps_config import MPSConfig
|
|
29
|
-
from emu_mps.noise import pick_well_prepared_qubits
|
|
30
28
|
from emu_base.jump_lindblad_operators import compute_noise_from_lindbladians
|
|
31
29
|
import emu_mps.optimatrix as optimat
|
|
30
|
+
from emu_mps.solver import Solver
|
|
32
31
|
from emu_mps.solver_utils import (
|
|
33
32
|
evolve_pair,
|
|
34
33
|
evolve_single,
|
|
34
|
+
minimize_energy_pair,
|
|
35
35
|
new_right_bath,
|
|
36
36
|
right_baths,
|
|
37
37
|
)
|
|
@@ -68,14 +68,7 @@ class Statistics(Observable):
|
|
|
68
68
|
"""Calculates the observable to store in the Results."""
|
|
69
69
|
assert isinstance(state, MPS)
|
|
70
70
|
duration = self.data[-1]
|
|
71
|
-
|
|
72
|
-
max_mem_per_device = (
|
|
73
|
-
torch.cuda.max_memory_allocated(device) * 1e-6
|
|
74
|
-
for device in range(torch.cuda.device_count())
|
|
75
|
-
)
|
|
76
|
-
max_mem = max(max_mem_per_device)
|
|
77
|
-
else:
|
|
78
|
-
max_mem = getrusage(RUSAGE_SELF).ru_maxrss * 1e-3
|
|
71
|
+
max_mem = get_max_rss(state.factors[0].is_cuda)
|
|
79
72
|
|
|
80
73
|
config.logger.info(
|
|
81
74
|
f"step = {len(self.data)}/{self.timestep_count}, "
|
|
@@ -102,11 +95,11 @@ class MPSBackendImpl:
|
|
|
102
95
|
current_time: float = (
|
|
103
96
|
0.0 # While dt is an integer, noisy collapse can happen at non-integer times.
|
|
104
97
|
)
|
|
105
|
-
well_prepared_qubits_filter: Optional[
|
|
98
|
+
well_prepared_qubits_filter: Optional[torch.Tensor]
|
|
106
99
|
hamiltonian: MPO
|
|
107
100
|
state: MPS
|
|
108
101
|
right_baths: list[torch.Tensor]
|
|
109
|
-
|
|
102
|
+
sweep_index: int
|
|
110
103
|
swipe_direction: SwipeDirection
|
|
111
104
|
timestep_index: int
|
|
112
105
|
target_time: float
|
|
@@ -142,7 +135,7 @@ class MPSBackendImpl:
|
|
|
142
135
|
self.left_baths: list[torch.Tensor]
|
|
143
136
|
self.time = time.time()
|
|
144
137
|
self.swipe_direction = SwipeDirection.LEFT_TO_RIGHT
|
|
145
|
-
self.
|
|
138
|
+
self.sweep_index = 0
|
|
146
139
|
self.timestep_index = 0
|
|
147
140
|
self.results = Results(
|
|
148
141
|
atom_order=optimat.permute_tuple(
|
|
@@ -192,8 +185,9 @@ class MPSBackendImpl:
|
|
|
192
185
|
def init_dark_qubits(self) -> None:
|
|
193
186
|
# has_state_preparation_error
|
|
194
187
|
if self.config.noise_model.state_prep_error > 0.0:
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
bad_atoms = self.pulser_data.hamiltonian.bad_atoms
|
|
189
|
+
self.well_prepared_qubits_filter = torch.logical_not(
|
|
190
|
+
torch.tensor(list(bool(x) for x in bad_atoms.values()))
|
|
197
191
|
)
|
|
198
192
|
else:
|
|
199
193
|
self.well_prepared_qubits_filter = None
|
|
@@ -241,7 +235,7 @@ class MPSBackendImpl:
|
|
|
241
235
|
|
|
242
236
|
initial_state = MPS(
|
|
243
237
|
# Deep copy of every tensor of the initial state.
|
|
244
|
-
[f.
|
|
238
|
+
[f.detach().clone() for f in initial_state.factors],
|
|
245
239
|
config=self.config,
|
|
246
240
|
num_gpus_to_use=self.config.num_gpus_to_use,
|
|
247
241
|
eigenstates=initial_state.eigenstates,
|
|
@@ -249,6 +243,7 @@ class MPSBackendImpl:
|
|
|
249
243
|
initial_state.truncate()
|
|
250
244
|
initial_state *= 1 / initial_state.norm()
|
|
251
245
|
self.state = initial_state
|
|
246
|
+
self.state.orthogonalize(0)
|
|
252
247
|
|
|
253
248
|
def init_hamiltonian(self) -> None:
|
|
254
249
|
"""
|
|
@@ -347,7 +342,7 @@ class MPSBackendImpl:
|
|
|
347
342
|
"""
|
|
348
343
|
Do one unit of simulation work given the current state.
|
|
349
344
|
Update the state accordingly.
|
|
350
|
-
The state of the simulation is stored in self.
|
|
345
|
+
The state of the simulation is stored in self.sweep_index and self.swipe_direction.
|
|
351
346
|
"""
|
|
352
347
|
if self.is_finished():
|
|
353
348
|
return
|
|
@@ -358,79 +353,79 @@ class MPSBackendImpl:
|
|
|
358
353
|
if 1 <= self.qubit_count <= 2:
|
|
359
354
|
# Corner case: only 1 or 2 qubits
|
|
360
355
|
assert self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT
|
|
361
|
-
assert self.
|
|
356
|
+
assert self.sweep_index == 0
|
|
362
357
|
|
|
363
358
|
if self.qubit_count == 1:
|
|
364
359
|
self._evolve(0, dt=delta_time)
|
|
365
360
|
else:
|
|
366
361
|
self._evolve(0, 1, dt=delta_time, orth_center_right=False)
|
|
367
362
|
|
|
368
|
-
self.
|
|
363
|
+
self.sweep_complete()
|
|
369
364
|
|
|
370
365
|
elif (
|
|
371
|
-
self.
|
|
366
|
+
self.sweep_index < self.qubit_count - 2
|
|
372
367
|
and self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT
|
|
373
368
|
):
|
|
374
369
|
# Left-to-right swipe of TDVP
|
|
375
370
|
self._evolve(
|
|
376
|
-
self.
|
|
377
|
-
self.
|
|
371
|
+
self.sweep_index,
|
|
372
|
+
self.sweep_index + 1,
|
|
378
373
|
dt=delta_time / 2,
|
|
379
374
|
orth_center_right=True,
|
|
380
375
|
)
|
|
381
376
|
self.left_baths.append(
|
|
382
377
|
new_left_bath(
|
|
383
378
|
self.get_current_left_bath(),
|
|
384
|
-
self.state.factors[self.
|
|
385
|
-
self.hamiltonian.factors[self.
|
|
386
|
-
).to(self.state.factors[self.
|
|
379
|
+
self.state.factors[self.sweep_index],
|
|
380
|
+
self.hamiltonian.factors[self.sweep_index],
|
|
381
|
+
).to(self.state.factors[self.sweep_index + 1].device)
|
|
387
382
|
)
|
|
388
|
-
self._evolve(self.
|
|
383
|
+
self._evolve(self.sweep_index + 1, dt=-delta_time / 2)
|
|
389
384
|
self.right_baths.pop()
|
|
390
|
-
self.
|
|
385
|
+
self.sweep_index += 1
|
|
391
386
|
|
|
392
387
|
elif (
|
|
393
|
-
self.
|
|
388
|
+
self.sweep_index == self.qubit_count - 2
|
|
394
389
|
and self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT
|
|
395
390
|
):
|
|
396
391
|
# Time-evolution of the rightmost 2 tensors
|
|
397
392
|
self._evolve(
|
|
398
|
-
self.
|
|
399
|
-
self.
|
|
393
|
+
self.sweep_index,
|
|
394
|
+
self.sweep_index + 1,
|
|
400
395
|
dt=delta_time,
|
|
401
396
|
orth_center_right=False,
|
|
402
397
|
)
|
|
403
398
|
self.swipe_direction = SwipeDirection.RIGHT_TO_LEFT
|
|
404
399
|
|
|
405
400
|
elif (
|
|
406
|
-
1 <= self.
|
|
401
|
+
1 <= self.sweep_index and self.swipe_direction == SwipeDirection.RIGHT_TO_LEFT
|
|
407
402
|
):
|
|
408
403
|
# Right-to-left swipe of TDVP
|
|
409
|
-
assert self.
|
|
404
|
+
assert self.sweep_index <= self.qubit_count - 2
|
|
410
405
|
self.right_baths.append(
|
|
411
406
|
new_right_bath(
|
|
412
407
|
self.get_current_right_bath(),
|
|
413
|
-
self.state.factors[self.
|
|
414
|
-
self.hamiltonian.factors[self.
|
|
415
|
-
).to(self.state.factors[self.
|
|
408
|
+
self.state.factors[self.sweep_index + 1],
|
|
409
|
+
self.hamiltonian.factors[self.sweep_index + 1],
|
|
410
|
+
).to(self.state.factors[self.sweep_index].device)
|
|
416
411
|
)
|
|
417
412
|
if not self.has_lindblad_noise:
|
|
418
413
|
# Free memory because it won't be used anymore
|
|
419
414
|
deallocate_tensor(self.right_baths[-2])
|
|
420
415
|
|
|
421
|
-
self._evolve(self.
|
|
416
|
+
self._evolve(self.sweep_index, dt=-delta_time / 2)
|
|
422
417
|
self.left_baths.pop()
|
|
423
418
|
|
|
424
419
|
self._evolve(
|
|
425
|
-
self.
|
|
426
|
-
self.
|
|
420
|
+
self.sweep_index - 1,
|
|
421
|
+
self.sweep_index,
|
|
427
422
|
dt=delta_time / 2,
|
|
428
423
|
orth_center_right=False,
|
|
429
424
|
)
|
|
430
|
-
self.
|
|
425
|
+
self.sweep_index -= 1
|
|
431
426
|
|
|
432
|
-
if self.
|
|
433
|
-
self.
|
|
427
|
+
if self.sweep_index == 0:
|
|
428
|
+
self.sweep_complete()
|
|
434
429
|
self.swipe_direction = SwipeDirection.LEFT_TO_RIGHT
|
|
435
430
|
|
|
436
431
|
else:
|
|
@@ -438,7 +433,7 @@ class MPSBackendImpl:
|
|
|
438
433
|
|
|
439
434
|
self.save_simulation()
|
|
440
435
|
|
|
441
|
-
def
|
|
436
|
+
def sweep_complete(self) -> None:
|
|
442
437
|
self.current_time = self.target_time
|
|
443
438
|
self.timestep_complete()
|
|
444
439
|
|
|
@@ -623,7 +618,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
|
|
|
623
618
|
super().init()
|
|
624
619
|
self.set_jump_threshold(1.0)
|
|
625
620
|
|
|
626
|
-
def
|
|
621
|
+
def sweep_complete(self) -> None:
|
|
627
622
|
previous_time = self.current_time
|
|
628
623
|
self.current_time = self.target_time
|
|
629
624
|
previous_norm_gap_before_jump = self.norm_gap_before_jump
|
|
@@ -690,10 +685,122 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
|
|
|
690
685
|
super().fill_results()
|
|
691
686
|
|
|
692
687
|
|
|
688
|
+
class DMRGBackendImpl(MPSBackendImpl):
|
|
689
|
+
def __init__(
|
|
690
|
+
self,
|
|
691
|
+
mps_config: MPSConfig,
|
|
692
|
+
pulser_data: PulserData,
|
|
693
|
+
energy_tolerance: float = 1e-5,
|
|
694
|
+
max_sweeps: int = 2000,
|
|
695
|
+
):
|
|
696
|
+
|
|
697
|
+
if mps_config.noise_model.noise_types != ():
|
|
698
|
+
raise NotImplementedError(
|
|
699
|
+
"DMRG solver does not currently support noise types"
|
|
700
|
+
f"you are using: {mps_config.noise_model.noise_types}"
|
|
701
|
+
)
|
|
702
|
+
super().__init__(mps_config, pulser_data)
|
|
703
|
+
self.previous_energy: Optional[float] = None
|
|
704
|
+
self.current_energy: Optional[float] = None
|
|
705
|
+
self.sweep_count: int = 0
|
|
706
|
+
self.energy_tolerance: float = energy_tolerance
|
|
707
|
+
self.max_sweeps: int = max_sweeps
|
|
708
|
+
|
|
709
|
+
def convergence_check(self, energy_tolerance: float) -> bool:
|
|
710
|
+
if self.previous_energy is None or self.current_energy is None:
|
|
711
|
+
return False
|
|
712
|
+
return abs(self.current_energy - self.previous_energy) < energy_tolerance
|
|
713
|
+
|
|
714
|
+
def progress(self) -> None:
|
|
715
|
+
if self.is_finished():
|
|
716
|
+
return
|
|
717
|
+
|
|
718
|
+
# perform one two-site energy minimization and update
|
|
719
|
+
idx = self.sweep_index
|
|
720
|
+
assert self.swipe_direction in (
|
|
721
|
+
SwipeDirection.LEFT_TO_RIGHT,
|
|
722
|
+
SwipeDirection.RIGHT_TO_LEFT,
|
|
723
|
+
), "Unknown Swipe direction"
|
|
724
|
+
|
|
725
|
+
orth_center_right = self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT
|
|
726
|
+
new_L, new_R, energy = minimize_energy_pair(
|
|
727
|
+
state_factors=self.state.factors[idx : idx + 2],
|
|
728
|
+
ham_factors=self.hamiltonian.factors[idx : idx + 2],
|
|
729
|
+
baths=(self.left_baths[-1], self.right_baths[-1]),
|
|
730
|
+
orth_center_right=orth_center_right,
|
|
731
|
+
config=self.config,
|
|
732
|
+
residual_tolerance=self.config.precision,
|
|
733
|
+
)
|
|
734
|
+
self.state.factors[idx], self.state.factors[idx + 1] = new_L, new_R
|
|
735
|
+
self.state.orthogonality_center = idx + 1 if orth_center_right else idx
|
|
736
|
+
self.current_energy = energy
|
|
737
|
+
|
|
738
|
+
# updating baths and orthogonality center
|
|
739
|
+
if self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT:
|
|
740
|
+
self._left_to_right_update(idx)
|
|
741
|
+
elif self.swipe_direction == SwipeDirection.RIGHT_TO_LEFT:
|
|
742
|
+
self._right_to_left_update(idx)
|
|
743
|
+
else:
|
|
744
|
+
raise Exception("Did not expect this")
|
|
745
|
+
|
|
746
|
+
self.save_simulation()
|
|
747
|
+
|
|
748
|
+
def _left_to_right_update(self, idx: int) -> None:
|
|
749
|
+
if idx < self.qubit_count - 2:
|
|
750
|
+
self.left_baths.append(
|
|
751
|
+
new_left_bath(
|
|
752
|
+
self.get_current_left_bath(),
|
|
753
|
+
self.state.factors[idx],
|
|
754
|
+
self.hamiltonian.factors[idx],
|
|
755
|
+
).to(self.state.factors[idx + 1].device)
|
|
756
|
+
)
|
|
757
|
+
self.right_baths.pop()
|
|
758
|
+
self.sweep_index += 1
|
|
759
|
+
|
|
760
|
+
if self.sweep_index == self.qubit_count - 2:
|
|
761
|
+
self.swipe_direction = SwipeDirection.RIGHT_TO_LEFT
|
|
762
|
+
|
|
763
|
+
def _right_to_left_update(self, idx: int) -> None:
|
|
764
|
+
if idx > 0:
|
|
765
|
+
self.right_baths.append(
|
|
766
|
+
new_right_bath(
|
|
767
|
+
self.get_current_right_bath(),
|
|
768
|
+
self.state.factors[idx + 1],
|
|
769
|
+
self.hamiltonian.factors[idx + 1],
|
|
770
|
+
).to(self.state.factors[idx].device)
|
|
771
|
+
)
|
|
772
|
+
self.left_baths.pop()
|
|
773
|
+
self.sweep_index -= 1
|
|
774
|
+
|
|
775
|
+
if self.sweep_index == 0:
|
|
776
|
+
self.state.orthogonalize(0)
|
|
777
|
+
self.swipe_direction = SwipeDirection.LEFT_TO_RIGHT
|
|
778
|
+
self.sweep_count += 1
|
|
779
|
+
self.sweep_complete()
|
|
780
|
+
|
|
781
|
+
def sweep_complete(self) -> None:
|
|
782
|
+
# This marks the end of one full sweep: checking convergence
|
|
783
|
+
if self.convergence_check(self.energy_tolerance):
|
|
784
|
+
self.current_time = self.target_time
|
|
785
|
+
self.timestep_complete()
|
|
786
|
+
elif self.sweep_count + 1 > self.max_sweeps:
|
|
787
|
+
# not converged
|
|
788
|
+
raise RuntimeError(f"DMRG did not converge after {self.max_sweeps} sweeps")
|
|
789
|
+
else:
|
|
790
|
+
# not converged for the current sweep. restart
|
|
791
|
+
self.previous_energy = self.current_energy
|
|
792
|
+
|
|
793
|
+
assert self.sweep_index == 0
|
|
794
|
+
assert self.state.orthogonality_center == 0
|
|
795
|
+
assert self.swipe_direction == SwipeDirection.LEFT_TO_RIGHT
|
|
796
|
+
self.current_energy = None
|
|
797
|
+
|
|
798
|
+
|
|
693
799
|
def create_impl(sequence: Sequence, config: MPSConfig) -> MPSBackendImpl:
|
|
694
800
|
pulser_data = PulserData(sequence=sequence, config=config, dt=config.dt)
|
|
695
801
|
|
|
696
802
|
if pulser_data.has_lindblad_noise:
|
|
697
803
|
return NoisyMPSBackendImpl(config, pulser_data)
|
|
698
|
-
|
|
804
|
+
if config.solver == Solver.DMRG:
|
|
805
|
+
return DMRGBackendImpl(config, pulser_data)
|
|
699
806
|
return MPSBackendImpl(config, pulser_data)
|
emu_mps/mps_config.py
CHANGED
|
@@ -4,6 +4,7 @@ from types import MethodType
|
|
|
4
4
|
import copy
|
|
5
5
|
|
|
6
6
|
from emu_base import DEVICE_COUNT
|
|
7
|
+
from emu_mps.solver import Solver
|
|
7
8
|
from emu_mps.custom_callback_implementations import (
|
|
8
9
|
energy_mps_impl,
|
|
9
10
|
energy_second_moment_mps_impl,
|
|
@@ -53,6 +54,11 @@ class MPSConfig(EmulationConfig):
|
|
|
53
54
|
autosave_dt: minimum time interval in seconds between two autosaves.
|
|
54
55
|
Saving the simulation state is only possible at specific times,
|
|
55
56
|
therefore this interval is only a lower bound.
|
|
57
|
+
solver: chooses the solver algorithm to run a sequence.
|
|
58
|
+
Two options are currently available:
|
|
59
|
+
``TDVP``, which performs ordinary time evolution,
|
|
60
|
+
and ``DMRG``, which adiabatically follows the ground state
|
|
61
|
+
of a given adiabatic pulse.
|
|
56
62
|
kwargs: arguments that are passed to the base class
|
|
57
63
|
|
|
58
64
|
Examples:
|
|
@@ -81,6 +87,7 @@ class MPSConfig(EmulationConfig):
|
|
|
81
87
|
log_file: pathlib.Path | None = None,
|
|
82
88
|
autosave_prefix: str = "emu_mps_save_",
|
|
83
89
|
autosave_dt: int = 600, # 10 minutes
|
|
90
|
+
solver: Solver = Solver.TDVP,
|
|
84
91
|
**kwargs: Any,
|
|
85
92
|
):
|
|
86
93
|
kwargs.setdefault("observables", [BitStrings(evaluation_times=[1.0])])
|
|
@@ -97,6 +104,7 @@ class MPSConfig(EmulationConfig):
|
|
|
97
104
|
log_file=log_file,
|
|
98
105
|
autosave_prefix=autosave_prefix,
|
|
99
106
|
autosave_dt=autosave_dt,
|
|
107
|
+
solver=solver,
|
|
100
108
|
**kwargs,
|
|
101
109
|
)
|
|
102
110
|
if self.optimize_qubit_ordering:
|
|
@@ -144,6 +152,7 @@ class MPSConfig(EmulationConfig):
|
|
|
144
152
|
"log_file",
|
|
145
153
|
"autosave_prefix",
|
|
146
154
|
"autosave_dt",
|
|
155
|
+
"solver",
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
def monkeypatch_observables(self) -> None:
|
emu_mps/solver.py
ADDED
emu_mps/solver_utils.py
CHANGED
|
@@ -229,9 +229,9 @@ def evolve_single(
|
|
|
229
229
|
|
|
230
230
|
def minimize_energy_pair(
|
|
231
231
|
*,
|
|
232
|
-
state_factors:
|
|
232
|
+
state_factors: Sequence[torch.Tensor],
|
|
233
233
|
baths: tuple[torch.Tensor, torch.Tensor],
|
|
234
|
-
ham_factors:
|
|
234
|
+
ham_factors: Sequence[torch.Tensor],
|
|
235
235
|
orth_center_right: bool,
|
|
236
236
|
config: MPSConfig,
|
|
237
237
|
residual_tolerance: float,
|
emu_mps/utils.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
import torch
|
|
3
|
-
import random
|
|
4
|
-
from collections import Counter
|
|
5
3
|
|
|
6
4
|
from emu_mps import MPSConfig
|
|
7
5
|
|
|
@@ -111,7 +109,7 @@ def assign_devices(tensors: List[torch.Tensor], num_gpus_to_use: int) -> None:
|
|
|
111
109
|
|
|
112
110
|
|
|
113
111
|
def extended_mps_factors(
|
|
114
|
-
mps_factors: list[torch.Tensor], where:
|
|
112
|
+
mps_factors: list[torch.Tensor], where: torch.Tensor
|
|
115
113
|
) -> list[torch.Tensor]:
|
|
116
114
|
"""
|
|
117
115
|
Given a valid list of MPS factors, accounting for qubits marked as `True` in `where`,
|
|
@@ -149,7 +147,7 @@ def extended_mps_factors(
|
|
|
149
147
|
|
|
150
148
|
|
|
151
149
|
def extended_mpo_factors(
|
|
152
|
-
mpo_factors: list[torch.Tensor], where:
|
|
150
|
+
mpo_factors: list[torch.Tensor], where: torch.Tensor
|
|
153
151
|
) -> list[torch.Tensor]:
|
|
154
152
|
"""
|
|
155
153
|
Given a valid list of MPO factors, accounting for qubits marked as `True` in `where`,
|
|
@@ -184,7 +182,7 @@ def extended_mpo_factors(
|
|
|
184
182
|
|
|
185
183
|
|
|
186
184
|
def get_extended_site_index(
|
|
187
|
-
where:
|
|
185
|
+
where: torch.Tensor, desired_index: Optional[int]
|
|
188
186
|
) -> Optional[int]:
|
|
189
187
|
"""
|
|
190
188
|
Returns the index in `where` that has `desired_index` preceding True elements.
|
|
@@ -210,42 +208,6 @@ def get_extended_site_index(
|
|
|
210
208
|
raise ValueError(f"Index {desired_index} does not exist")
|
|
211
209
|
|
|
212
210
|
|
|
213
|
-
def readout_with_error(c: str, *, p_false_pos: float, p_false_neg: float) -> str:
|
|
214
|
-
# p_false_pos = false positive, p_false_neg = false negative
|
|
215
|
-
r = random.random()
|
|
216
|
-
if c == "0" and r < p_false_pos:
|
|
217
|
-
return "1"
|
|
218
|
-
|
|
219
|
-
if c == "1" and r < p_false_neg:
|
|
220
|
-
return "0"
|
|
221
|
-
|
|
222
|
-
return c
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
def apply_measurement_errors(
|
|
226
|
-
bitstrings: Counter[str], *, p_false_pos: float, p_false_neg: float
|
|
227
|
-
) -> Counter[str]:
|
|
228
|
-
"""
|
|
229
|
-
Given a bag of sampled bitstrings, returns another bag of bitstrings
|
|
230
|
-
sampled with readout/measurement errors.
|
|
231
|
-
|
|
232
|
-
p_false_pos: probability of false positive
|
|
233
|
-
p_false_neg: probability of false negative
|
|
234
|
-
"""
|
|
235
|
-
|
|
236
|
-
result: Counter[str] = Counter()
|
|
237
|
-
for bitstring, count in bitstrings.items():
|
|
238
|
-
for _ in range(count):
|
|
239
|
-
bitstring_with_error = "".join(
|
|
240
|
-
readout_with_error(c, p_false_pos=p_false_pos, p_false_neg=p_false_neg)
|
|
241
|
-
for c in bitstring
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
result[bitstring_with_error] += 1
|
|
245
|
-
|
|
246
|
-
return result
|
|
247
|
-
|
|
248
|
-
|
|
249
211
|
n_operator: torch.Tensor = torch.tensor(
|
|
250
212
|
[
|
|
251
213
|
[0, 0],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emu-mps
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
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.
|
|
28
|
+
Requires-Dist: emu-base==2.4.0
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
31
|
<div align="center">
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
emu_mps/__init__.py,sha256=
|
|
1
|
+
emu_mps/__init__.py,sha256=ySFn8SLcpsp1ndtvv38qg_gTlO9jt_jmwZVZd21hsg0,708
|
|
2
2
|
emu_mps/algebra.py,sha256=78XP9HEbV3wGNUzIulcLU5HizW4XAYmcFdkCe1T1x-k,5489
|
|
3
3
|
emu_mps/custom_callback_implementations.py,sha256=SZGKVyS8U5hy07L-3SqpWlCAqGGKFTlSlWexZwSmjrM,2408
|
|
4
4
|
emu_mps/hamiltonian.py,sha256=gOPxNOBmk6jRPPjevERuCP_scGv0EKYeAJ0uxooihes,15622
|
|
5
5
|
emu_mps/mpo.py,sha256=aWSVuEzZM-_7ZD5Rz3-tSJWX22ARP0tMIl3gUu-_4V4,7834
|
|
6
|
-
emu_mps/mps.py,sha256=
|
|
6
|
+
emu_mps/mps.py,sha256=I7LAxoOPKr4me5ZFdL5AZMerg7go9I1fu-MAI0ZBMgU,19973
|
|
7
7
|
emu_mps/mps_backend.py,sha256=bS83qFxvdoK-c12_1WaPw6O7xUc7vdWifZNHUzNP5sM,2091
|
|
8
|
-
emu_mps/mps_backend_impl.py,sha256=
|
|
9
|
-
emu_mps/mps_config.py,sha256=
|
|
10
|
-
emu_mps/noise.py,sha256=5BXthepWLKnuSTJfIFuPl2AcYPxUeTJdRc2b28ekkhg,208
|
|
8
|
+
emu_mps/mps_backend_impl.py,sha256=JG4MlEsImc9izF-iwpgyw1t8q4M_20DxbbD4HDObSz0,30268
|
|
9
|
+
emu_mps/mps_config.py,sha256=58Y9HEExeHm8p7Zm5CVwku7Y1pQmkaBV7M_hKv4PQcA,8629
|
|
11
10
|
emu_mps/observables.py,sha256=7GQDH5kyaVNrwckk2f8ZJRV9Ca4jKhWWDsOCqYWsoEk,1349
|
|
12
|
-
emu_mps/
|
|
13
|
-
emu_mps/
|
|
11
|
+
emu_mps/solver.py,sha256=M9xkHhlEouTBvoPw2UYVu6kij7CO4Z1FXw_SiGFtdgo,85
|
|
12
|
+
emu_mps/solver_utils.py,sha256=VQ02_RxvPcjyXippuIY4Swpx4EdqtoJTt8Ie70GgdqU,8550
|
|
13
|
+
emu_mps/utils.py,sha256=hgtaRUtBAzk76ab-S_wTVkvqfVOmaUks38zWame9GRQ,7132
|
|
14
14
|
emu_mps/optimatrix/__init__.py,sha256=fBXQ7-rgDro4hcaBijCGhx3J69W96qcw5_3mWc7tND4,364
|
|
15
15
|
emu_mps/optimatrix/optimiser.py,sha256=k9suYmKLKlaZ7ozFuIqvXHyCBoCtGgkX1mpen9GOdOo,6977
|
|
16
16
|
emu_mps/optimatrix/permutations.py,sha256=9DDMZtrGGZ01b9F3GkzHR3paX4qNtZiPoI7Z_Kia3Lc,3727
|
|
17
|
-
emu_mps-2.
|
|
18
|
-
emu_mps-2.
|
|
19
|
-
emu_mps-2.
|
|
17
|
+
emu_mps-2.4.0.dist-info/METADATA,sha256=6eUzR_h7HcAVtC9Sy3umvCIw-w_bICX98VD-uXjRnxM,3587
|
|
18
|
+
emu_mps-2.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
19
|
+
emu_mps-2.4.0.dist-info/RECORD,,
|
emu_mps/noise.py
DELETED
|
File without changes
|