emu-mps 2.0.2__py3-none-any.whl → 2.0.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
@@ -37,4 +37,4 @@ __all__ = [
37
37
  "EntanglementEntropy",
38
38
  ]
39
39
 
40
- __version__ = "2.0.2"
40
+ __version__ = "2.0.4"
emu_mps/mps_backend.py CHANGED
@@ -55,7 +55,9 @@ class MPSBackend(EmulatorBackend):
55
55
  impl = create_impl(self._sequence, self._config)
56
56
  impl.init() # This is separate from the constructor for testing purposes.
57
57
 
58
- return self._run(impl)
58
+ results = self._run(impl)
59
+
60
+ return impl.permute_results(results, self._config.optimize_qubit_ordering)
59
61
 
60
62
  @staticmethod
61
63
  def _run(impl: MPSBackendImpl) -> Results:
@@ -1,29 +1,35 @@
1
1
  import math
2
+ import os
2
3
  import pathlib
4
+ import pickle
3
5
  import random
6
+ import time
7
+ import typing
4
8
  import uuid
5
9
 
10
+ from collections import Counter
11
+ from enum import Enum, auto
6
12
  from resource import RUSAGE_SELF, getrusage
7
- from typing import Optional, Any
8
- import typing
9
- import pickle
10
- import os
13
+ from types import MethodType
14
+ from typing import Any, Optional
15
+
11
16
  import torch
12
- import time
13
17
  from pulser import Sequence
14
- from types import MethodType
18
+ from pulser.backend import EmulationConfig, Observable, Results, State
15
19
 
16
- from pulser.backend import State, Observable, EmulationConfig, Results
17
- from emu_base import PulserData, DEVICE_COUNT
20
+ from emu_base import DEVICE_COUNT, PulserData
18
21
  from emu_base.math.brents_root_finding import BrentsRootFinder
22
+
19
23
  from emu_mps.hamiltonian import make_H, update_H
20
24
  from emu_mps.mpo import MPO
21
25
  from emu_mps.mps import MPS
22
26
  from emu_mps.mps_config import MPSConfig
23
- from emu_mps.noise import compute_noise_from_lindbladians, pick_well_prepared_qubits
27
+ from emu_mps.noise import pick_well_prepared_qubits
28
+ from emu_base.jump_lindblad_operators import compute_noise_from_lindbladians
29
+ import emu_mps.optimatrix as optimat
24
30
  from emu_mps.tdvp import (
25
- evolve_single,
26
31
  evolve_pair,
32
+ evolve_single,
27
33
  new_right_bath,
28
34
  right_baths,
29
35
  )
@@ -33,7 +39,6 @@ from emu_mps.utils import (
33
39
  get_extended_site_index,
34
40
  new_left_bath,
35
41
  )
36
- from enum import Enum, auto
37
42
 
38
43
 
39
44
  class Statistics(Observable):
@@ -118,8 +123,17 @@ class MPSBackendImpl:
118
123
  self.timestep_count: int = self.omega.shape[0]
119
124
  self.has_lindblad_noise = pulser_data.has_lindblad_noise
120
125
  self.lindblad_noise = torch.zeros(2, 2, dtype=torch.complex128)
121
- self.full_interaction_matrix = pulser_data.full_interaction_matrix
122
- self.masked_interaction_matrix = pulser_data.masked_interaction_matrix
126
+ self.qubit_permutation = (
127
+ optimat.minimize_bandwidth(pulser_data.full_interaction_matrix)
128
+ if self.config.optimize_qubit_ordering
129
+ else optimat.eye_permutation(self.qubit_count)
130
+ )
131
+ self.full_interaction_matrix = optimat.permute_tensor(
132
+ pulser_data.full_interaction_matrix, self.qubit_permutation
133
+ )
134
+ self.masked_interaction_matrix = optimat.permute_tensor(
135
+ pulser_data.masked_interaction_matrix, self.qubit_permutation
136
+ )
123
137
  self.hamiltonian_type = pulser_data.hamiltonian_type
124
138
  self.slm_end_time = pulser_data.slm_end_time
125
139
  self.is_masked = self.slm_end_time > 0.0
@@ -128,7 +142,12 @@ class MPSBackendImpl:
128
142
  self.swipe_direction = SwipeDirection.LEFT_TO_RIGHT
129
143
  self.tdvp_index = 0
130
144
  self.timestep_index = 0
131
- self.results = Results(atom_order=(), total_duration=self.target_times[-1])
145
+ self.results = Results(
146
+ atom_order=optimat.permute_tuple(
147
+ pulser_data.qubit_ids, self.qubit_permutation
148
+ ),
149
+ total_duration=self.target_times[-1],
150
+ )
132
151
  self.statistics = Statistics(
133
152
  evaluation_times=[t / self.target_times[-1] for t in self.target_times],
134
153
  data=[],
@@ -203,6 +222,18 @@ class MPSBackendImpl:
203
222
  )
204
223
 
205
224
  assert isinstance(initial_state, MPS)
225
+ if not torch.equal(
226
+ self.qubit_permutation, optimat.eye_permutation(self.qubit_count)
227
+ ):
228
+ # permute the initial state to match with permuted Hamiltonian
229
+ abstr_repr = initial_state._to_abstract_repr()
230
+ eigs = abstr_repr["eigenstates"]
231
+ ampl = {
232
+ optimat.permute_string(bstr, self.qubit_permutation): amp
233
+ for bstr, amp in abstr_repr["amplitudes"].items()
234
+ }
235
+ initial_state = MPS.from_state_amplitudes(eigenstates=eigs, amplitudes=ampl)
236
+
206
237
  initial_state = MPS(
207
238
  # Deep copy of every tensor of the initial state.
208
239
  [f.clone().detach() for f in initial_state.factors],
@@ -510,6 +541,43 @@ class MPSBackendImpl:
510
541
 
511
542
  callback(self.config, fractional_time, full_state, full_mpo, self.results)
512
543
 
544
+ def permute_results(self, results: Results, permute: bool) -> Results:
545
+ if permute:
546
+ inv_perm = optimat.inv_permutation(self.qubit_permutation)
547
+ permute_bitstrings(results, inv_perm)
548
+ permute_occupations_and_correlations(results, inv_perm)
549
+ permute_atom_order(results, inv_perm)
550
+ return results
551
+
552
+
553
+ def permute_bitstrings(results: Results, perm: torch.Tensor) -> None:
554
+ if "bitstrings" not in results.get_result_tags():
555
+ return
556
+ uuid_bs = results._find_uuid("bitstrings")
557
+
558
+ results._results[uuid_bs] = [
559
+ Counter({optimat.permute_string(bstr, perm): c for bstr, c in bs_counter.items()})
560
+ for bs_counter in results._results[uuid_bs]
561
+ ]
562
+
563
+
564
+ def permute_occupations_and_correlations(results: Results, perm: torch.Tensor) -> None:
565
+ for corr in ["occupation", "correlation_matrix"]:
566
+ if corr not in results.get_result_tags():
567
+ continue
568
+
569
+ uuid_corr = results._find_uuid(corr)
570
+ corrs = results._results[uuid_corr]
571
+ results._results[uuid_corr] = [
572
+ optimat.permute_tensor(corr, perm) for corr in corrs
573
+ ]
574
+
575
+
576
+ def permute_atom_order(results: Results, perm: torch.Tensor) -> None:
577
+ at_ord = list(results.atom_order)
578
+ at_ord = optimat.permute_list(at_ord, perm)
579
+ results.atom_order = tuple(at_ord)
580
+
513
581
 
514
582
  class NoisyMPSBackendImpl(MPSBackendImpl):
515
583
  """
emu_mps/mps_config.py CHANGED
@@ -69,6 +69,7 @@ class MPSConfig(EmulationConfig):
69
69
  max_krylov_dim: int = 100,
70
70
  extra_krylov_tolerance: float = 1e-3,
71
71
  num_gpus_to_use: int = DEVICE_COUNT,
72
+ optimize_qubit_ordering: bool = False,
72
73
  interaction_cutoff: float = 0.0,
73
74
  log_level: int = logging.INFO,
74
75
  log_file: pathlib.Path | None = None,
@@ -84,6 +85,7 @@ class MPSConfig(EmulationConfig):
84
85
  max_krylov_dim=max_krylov_dim,
85
86
  extra_krylov_tolerance=extra_krylov_tolerance,
86
87
  num_gpus_to_use=num_gpus_to_use,
88
+ optimize_qubit_ordering=optimize_qubit_ordering,
87
89
  interaction_cutoff=interaction_cutoff,
88
90
  log_level=log_level,
89
91
  log_file=log_file,
@@ -91,6 +93,8 @@ class MPSConfig(EmulationConfig):
91
93
  autosave_dt=autosave_dt,
92
94
  **kwargs,
93
95
  )
96
+ if self.optimize_qubit_ordering:
97
+ self.check_permutable_observables()
94
98
 
95
99
  if "doppler" in self.noise_model.noise_types:
96
100
  raise NotImplementedError("Unsupported noise type: doppler")
@@ -136,6 +140,7 @@ class MPSConfig(EmulationConfig):
136
140
  "max_krylov_dim",
137
141
  "extra_krylov_tolerance",
138
142
  "num_gpus_to_use",
143
+ "optimize_qubit_ordering",
139
144
  "interaction_cutoff",
140
145
  "log_level",
141
146
  "log_file",
@@ -183,3 +188,27 @@ class MPSConfig(EmulationConfig):
183
188
  filemode="w",
184
189
  force=True,
185
190
  )
191
+
192
+ def check_permutable_observables(self) -> None:
193
+ allowed_permutable_obs = set(
194
+ [
195
+ "bitstrings",
196
+ "occupation",
197
+ "correlation_matrix",
198
+ "statistics",
199
+ "energy",
200
+ "energy_variance",
201
+ "energy_second_moment",
202
+ ]
203
+ )
204
+
205
+ actual_obs = set([obs._base_tag for obs in self.observables])
206
+ not_allowed = actual_obs.difference(allowed_permutable_obs)
207
+ if not_allowed:
208
+ raise ValueError(
209
+ f"emu-mp allows only {allowed_permutable_obs} observables with"
210
+ " `optimize_qubit_ordering = True`."
211
+ f" you provided unsupported {not_allowed}"
212
+ " To use other observables, please set"
213
+ " `optimize_qubit_ordering = False` in `MPSConfig()`."
214
+ )
emu_mps/noise.py CHANGED
@@ -1,26 +1,6 @@
1
- import torch
2
1
  import random
3
2
 
4
3
 
5
- def compute_noise_from_lindbladians(lindbladians: list[torch.Tensor]) -> torch.Tensor:
6
- """
7
- Compute the single-qubit Hamiltonian noise term -0.5i∑L†L from all the given lindbladians.
8
- """
9
-
10
- assert all(
11
- lindbladian.shape == (2, 2) for lindbladian in lindbladians
12
- ), "Only single-qubit lindblad operators are supported"
13
-
14
- return (
15
- -1j
16
- / 2.0
17
- * sum(
18
- (lindbladian.T.conj() @ lindbladian for lindbladian in lindbladians),
19
- start=torch.zeros(2, 2, dtype=torch.complex128),
20
- )
21
- )
22
-
23
-
24
4
  def pick_well_prepared_qubits(eta: float, n: int) -> list[bool]:
25
5
  """
26
6
  Randomly pick n booleans such that ℙ(False) = eta.
@@ -1,9 +1,20 @@
1
1
  from .optimiser import minimize_bandwidth
2
- from .permutations import permute_tensor, inv_permutation, permute_string
2
+ from .permutations import (
3
+ permute_tensor,
4
+ inv_permutation,
5
+ permute_string,
6
+ eye_permutation,
7
+ permute_list,
8
+ permute_tuple,
9
+ )
10
+
3
11
 
4
12
  __all__ = [
5
13
  "minimize_bandwidth",
14
+ "eye_permutation",
6
15
  "permute_string",
7
16
  "permute_tensor",
8
17
  "inv_permutation",
18
+ "permute_list",
19
+ "permute_tuple",
9
20
  ]
@@ -1,6 +1,54 @@
1
1
  import torch
2
2
 
3
3
 
4
+ def eye_permutation(n: int) -> torch.Tensor:
5
+ """
6
+ Returns toch.tensor([0, 1, 2, .., n-1])
7
+ """
8
+ return torch.arange(n)
9
+
10
+
11
+ def permute_list(input_list: list, perm: torch.Tensor) -> list:
12
+ """
13
+ Permutes the input list according to the given permutation.
14
+ Parameters
15
+ -------
16
+ input_list :
17
+ A list to permute.
18
+ permutation :
19
+ A list of indices representing the new order.
20
+ Returns
21
+ -------
22
+ The permuted list.
23
+ Example
24
+ -------
25
+ >>> permute_list(['a','b','c'], torch.tensor([2, 0, 1]))
26
+ ['c', 'a', 'b']
27
+ """
28
+ return [input_list[i] for i in perm.tolist()]
29
+
30
+
31
+ def permute_tuple(input_tuple: tuple, perm: torch.Tensor) -> tuple:
32
+ """
33
+ Permutes the input tuple according to the given permutation.
34
+ Parameters
35
+ -------
36
+ input_tuple :
37
+ A tuple to permute.
38
+ permutation :
39
+ A tuple of indices representing the new order.
40
+ Returns
41
+ -------
42
+ The permuted tuple.
43
+ Example
44
+ -------
45
+ >>> permute_tuple(('a','b','c'), torch.tensor([2, 0, 1]))
46
+ ('c', 'a', 'b')
47
+ """
48
+ lst_elem = list(input_tuple)
49
+ return tuple(permute_list(lst_elem, perm))
50
+
51
+
4
52
  def permute_string(input_str: str, perm: torch.Tensor) -> str:
5
53
  """
6
54
  Permutes the input string according to the given permutation.
@@ -18,8 +66,7 @@ def permute_string(input_str: str, perm: torch.Tensor) -> str:
18
66
  >>> permute_string("abc", torch.tensor([2, 0, 1]))
19
67
  'cab'
20
68
  """
21
- char_list = list(input_str)
22
- permuted = [char_list[i] for i in perm.tolist()]
69
+ permuted = permute_list(list(input_str), perm)
23
70
  return "".join(permuted)
24
71
 
25
72
 
emu_mps/tdvp.py CHANGED
@@ -73,8 +73,16 @@ def apply_effective_Hamiltonian(
73
73
  # this order seems to be pretty balanced, but needs to be
74
74
  # revisited when use-cases are more well-known
75
75
  state = torch.tensordot(left_bath, state, 1)
76
- state = torch.tensordot(state, ham, ([1, 2], [0, 2]))
77
- state = torch.tensordot(state, right_bath, ([3, 1], [1, 2]))
76
+ state = state.permute(0, 3, 1, 2)
77
+ ham = ham.permute(0, 2, 1, 3)
78
+ state = state.reshape(state.shape[0], state.shape[1], -1).contiguous()
79
+ ham = ham.reshape(-1, ham.shape[2], ham.shape[3]).contiguous()
80
+ state = torch.tensordot(state, ham, 1)
81
+ state = state.permute(0, 2, 1, 3)
82
+ state = state.reshape(state.shape[0], state.shape[1], -1).contiguous()
83
+ right_bath = right_bath.permute(2, 1, 0)
84
+ right_bath = right_bath.reshape(-1, right_bath.shape[2]).contiguous()
85
+ state = torch.tensordot(state, right_bath, 1)
78
86
  return state
79
87
 
80
88
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emu-mps
3
- Version: 2.0.2
3
+ Version: 2.0.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.0.2
28
+ Requires-Dist: emu-base==2.0.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=K556l0ESPENNk_HUeBzdsP2viJKqMt7eeN0TKilKmFo,734
2
+ emu_mps/algebra.py,sha256=ngPtTH-j2ZCBWoaJZXlkUyIlug7dY7Q92gzfnRlpPMA,5485
3
+ emu_mps/custom_callback_implementations.py,sha256=CUs0kW3HRaPE7UeFNQOFbeWJMsz4hS2q4rgS57BBp-A,2411
4
+ emu_mps/hamiltonian.py,sha256=gOPxNOBmk6jRPPjevERuCP_scGv0EKYeAJ0uxooihes,15622
5
+ emu_mps/mpo.py,sha256=1ogQ25GZCwMzZ_m449oGHcYyDKrofBCr1eyzzrIPMhQ,8824
6
+ emu_mps/mps.py,sha256=GIiWxctNmHARgf-PgQc6IHKNCe5HYSnbtlXI6Hc-0wI,20085
7
+ emu_mps/mps_backend.py,sha256=bS83qFxvdoK-c12_1WaPw6O7xUc7vdWifZNHUzNP5sM,2091
8
+ emu_mps/mps_backend_impl.py,sha256=CLWbtzDLLYh92xD9_GLQVteSYGd_kylSh6DwnDbf0K8,25657
9
+ emu_mps/mps_config.py,sha256=JCK_frUVyMRJjI0NPU-dpCg-uO3JKeVzQMOLDkQ9XVk,8104
10
+ emu_mps/noise.py,sha256=5BXthepWLKnuSTJfIFuPl2AcYPxUeTJdRc2b28ekkhg,208
11
+ emu_mps/observables.py,sha256=7GQDH5kyaVNrwckk2f8ZJRV9Ca4jKhWWDsOCqYWsoEk,1349
12
+ emu_mps/tdvp.py,sha256=0qTw9qhg0WbaAyBgeTpULHrNL0ytj80ZUb1P6GKD7Ww,6172
13
+ emu_mps/utils.py,sha256=BqRJYAcXqprtZVJ0V_j954ON2bhTdtZiaTojsYyrWrg,8193
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.0.4.dist-info/METADATA,sha256=nbKzGgXJPvZO4u0PL8zXN6H-9lV6VRsp__-FCD4c3Xs,3505
18
+ emu_mps-2.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ emu_mps-2.0.4.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- emu_mps/__init__.py,sha256=cjDXI6YNKEZ_qT8eIT8z9HUowyx4Mh8fNTFciJpMm9k,734
2
- emu_mps/algebra.py,sha256=ngPtTH-j2ZCBWoaJZXlkUyIlug7dY7Q92gzfnRlpPMA,5485
3
- emu_mps/custom_callback_implementations.py,sha256=CUs0kW3HRaPE7UeFNQOFbeWJMsz4hS2q4rgS57BBp-A,2411
4
- emu_mps/hamiltonian.py,sha256=gOPxNOBmk6jRPPjevERuCP_scGv0EKYeAJ0uxooihes,15622
5
- emu_mps/mpo.py,sha256=1ogQ25GZCwMzZ_m449oGHcYyDKrofBCr1eyzzrIPMhQ,8824
6
- emu_mps/mps.py,sha256=GIiWxctNmHARgf-PgQc6IHKNCe5HYSnbtlXI6Hc-0wI,20085
7
- emu_mps/mps_backend.py,sha256=_3rlg6XeI4fHaDiJRfPL6pDkX9k48hAHKXd8fkvkOFs,2004
8
- emu_mps/mps_backend_impl.py,sha256=P8GYzM4C7r6Sgf9eSPQuwzQup4nwy3tPzlcQgGFkxpI,23132
9
- emu_mps/mps_config.py,sha256=89nu5OhNUX31eAeeYvvKnAHegpPVD43jH5Nmp635HVU,6984
10
- emu_mps/noise.py,sha256=h4X2EFjoC_Ok0gZ8I9wN77RANXaVehTBbjkcbY_GAmY,784
11
- emu_mps/observables.py,sha256=7GQDH5kyaVNrwckk2f8ZJRV9Ca4jKhWWDsOCqYWsoEk,1349
12
- emu_mps/tdvp.py,sha256=MMkYtVRNovNZiLO6BO1mmr6I6yYDYUJC3MGapfGlrQw,5756
13
- emu_mps/utils.py,sha256=BqRJYAcXqprtZVJ0V_j954ON2bhTdtZiaTojsYyrWrg,8193
14
- emu_mps/optimatrix/__init__.py,sha256=iFftb0AtiT_va153B_rZuY_PWWCo1SHnS8sRKjz8cS0,224
15
- emu_mps/optimatrix/optimiser.py,sha256=k9suYmKLKlaZ7ozFuIqvXHyCBoCtGgkX1mpen9GOdOo,6977
16
- emu_mps/optimatrix/permutations.py,sha256=kQKPF1Owe_f63zEhfaDadlxkqotIkAPK4kSzfqELIaM,2597
17
- emu_mps-2.0.2.dist-info/METADATA,sha256=p6h2IcYP-wiR9yrs6Q_9lzgz5DpDn9YvM8dpLQmgbhI,3505
18
- emu_mps-2.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- emu_mps-2.0.2.dist-info/RECORD,,