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 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.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.tensor], right: list[torch.tensor]
10
- ) -> list[torch.tensor]:
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.tensor], scalar: complex, *, which: int
52
- ) -> list[torch.tensor]:
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.tensor,
61
- top: torch.tensor,
62
- bottom: torch.tensor,
63
- ) -> torch.tensor:
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.tensor],
117
- bottom_factors: list[torch.tensor],
118
- max_error: float = 1e-5,
119
- max_rank: int = 1024,
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, max_error=max_error, max_rank=max_rank)
154
+ truncate_impl(new_factors, config=config)
150
155
 
151
156
  return new_factors
emu_mps/constants.py ADDED
@@ -0,0 +1,4 @@
1
+ import torch
2
+
3
+
4
+ DEVICE_COUNT = torch.cuda.device_count()
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, cast, Iterable, Optional
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, DEVICE_COUNT
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
- max_error=other.precision,
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
- # operators will now contain the basis for single qubit ops, and potentially
184
- # user defined strings in terms of these
185
- operators |= {
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
- # operators will now contain the basis for single qubit ops, and potentially
201
- # user defined strings in terms of these
202
- operators |= {
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 operators, and replace any definitions
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, dict):
225
- for opstr, coeff in op.items():
226
- tensor = replace_operator_string(operators[opstr])
227
- operators[opstr] = tensor
228
- op[opstr] = tensor * coeff
229
- op = sum(cast(list[torch.Tensor], op.values()))
230
- return op
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
- for i in op[1]:
241
- factors[i] = op[0]
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
- precision: float = DEFAULT_PRECISION,
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
- precision: the precision with which to truncate here or in tdvp
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.precision = precision
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
- precision: float = DEFAULT_PRECISION,
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
- precision: the precision with which to truncate here or in tdvp
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
- precision=precision,
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.precision and self.max_bond_dim for determining accuracy.
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.tensor = self.factors[0]
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
- precision=self.precision,
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
- precision=self.precision,
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
@@ -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.evolve_config = EvolveConfig(
76
- exp_tolerance=self.config.precision * self.config.extra_krylov_tolerance,
77
- norm_tolerance=self.config.precision * self.config.extra_krylov_tolerance,
78
- max_krylov_dim=self.config.max_krylov_dim,
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
- precision=self.config.precision,
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
- precision=self.config.precision,
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.evolve_config,
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.evolve_config,
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] = None
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
- # TODO: checkpoint/autosave here
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
- self.current_time,
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 self.current_time not in callback.evaluation_times:
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, self.current_time, full_state, full_mpo, self.results)
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: Optional[torch.Tensor]
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.utils import DEVICE_COUNT
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
- return torch.tensordot(state.conj(), bath, ([1, 2], [1, 3]))
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
- config: EvolveConfig
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.exp_tolerance,
158
- norm_tolerance=config.norm_tolerance,
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=config.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.max_error,
166
- max_rank=config.max_rank,
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
- config: EvolveConfig
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.exp_tolerance,
206
- norm_tolerance=config.norm_tolerance,
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=config.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
- DEVICE_COUNT = torch.cuda.device_count()
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
- return torch.tensordot(bath, state, ([0, 2], [0, 1]))
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.tensor],
64
- max_error: float = 1e-5,
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=max_error,
82
- max_rank=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,,
@@ -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
- ![Code Coverage](https://img.shields.io/badge/Coverage-95%25-brightgreen.svg)
@@ -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,,