emu-mps 2.0.1__py3-none-any.whl → 2.0.2__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/mpo.py CHANGED
@@ -83,7 +83,7 @@ class MPO(Operator[complex, torch.Tensor, MPS]):
83
83
  other.factors,
84
84
  config=other.config,
85
85
  )
86
- return MPS(factors, orthogonality_center=0)
86
+ return MPS(factors, orthogonality_center=0, eigenstates=other.eigenstates)
87
87
 
88
88
  def __add__(self, other: MPO) -> MPO:
89
89
  """
emu_mps/mps.py CHANGED
@@ -38,6 +38,7 @@ class MPS(State[complex, torch.Tensor]):
38
38
  orthogonality_center: Optional[int] = None,
39
39
  config: Optional[MPSConfig] = None,
40
40
  num_gpus_to_use: Optional[int] = DEVICE_COUNT,
41
+ eigenstates: Sequence[Eigenstate] = ("r", "g"),
41
42
  ):
42
43
  """
43
44
  This constructor creates a MPS directly from a list of tensors. It is for internal use only.
@@ -56,7 +57,7 @@ class MPS(State[complex, torch.Tensor]):
56
57
  num_gpus_to_use: distribute the factors over this many GPUs
57
58
  0=all factors to cpu, None=keep the existing device assignment.
58
59
  """
59
- self._eigenstates = ["0", "1"]
60
+ super().__init__(eigenstates=eigenstates)
60
61
  self.config = config if config is not None else MPSConfig()
61
62
  assert all(
62
63
  factors[i - 1].shape[2] == factors[i].shape[0] for i in range(1, len(factors))
@@ -88,6 +89,7 @@ class MPS(State[complex, torch.Tensor]):
88
89
  num_sites: int,
89
90
  config: Optional[MPSConfig] = None,
90
91
  num_gpus_to_use: int = DEVICE_COUNT,
92
+ eigenstates: Sequence[Eigenstate] = ["0", "1"],
91
93
  ) -> MPS:
92
94
  """
93
95
  Returns a MPS in ground state |000..0>.
@@ -111,6 +113,7 @@ class MPS(State[complex, torch.Tensor]):
111
113
  config=config,
112
114
  num_gpus_to_use=num_gpus_to_use,
113
115
  orthogonality_center=0, # Arbitrary: every qubit is an orthogonality center.
116
+ eigenstates=eigenstates,
114
117
  )
115
118
 
116
119
  def __repr__(self) -> str:
@@ -350,12 +353,16 @@ class MPS(State[complex, torch.Tensor]):
350
353
  the summed state
351
354
  """
352
355
  assert isinstance(other, MPS), "Other state also needs to be an MPS"
356
+ assert (
357
+ self.eigenstates == other.eigenstates
358
+ ), f"`Other` state has basis {other.eigenstates} != {self.eigenstates}"
353
359
  new_tt = add_factors(self.factors, other.factors)
354
360
  result = MPS(
355
361
  new_tt,
356
362
  config=self.config,
357
363
  num_gpus_to_use=None,
358
364
  orthogonality_center=None, # Orthogonality is lost.
365
+ eigenstates=self.eigenstates,
359
366
  )
360
367
  result.truncate()
361
368
  return result
@@ -381,6 +388,7 @@ class MPS(State[complex, torch.Tensor]):
381
388
  config=self.config,
382
389
  num_gpus_to_use=None,
383
390
  orthogonality_center=self.orthogonality_center,
391
+ eigenstates=self.eigenstates,
384
392
  )
385
393
 
386
394
  def __imul__(self, scalar: complex) -> MPS:
@@ -390,7 +398,7 @@ class MPS(State[complex, torch.Tensor]):
390
398
  def _from_state_amplitudes(
391
399
  cls,
392
400
  *,
393
- eigenstates: Sequence[str],
401
+ eigenstates: Sequence[Eigenstate],
394
402
  amplitudes: Mapping[str, complex],
395
403
  ) -> tuple[MPS, Mapping[str, complex]]:
396
404
  """
@@ -404,8 +412,6 @@ class MPS(State[complex, torch.Tensor]):
404
412
  Returns:
405
413
  The resulting MPS representation of the state.s
406
414
  """
407
-
408
- nqubits = len(next(iter(amplitudes.keys())))
409
415
  basis = set(eigenstates)
410
416
  if basis == {"r", "g"}:
411
417
  one = "r"
@@ -414,17 +420,20 @@ class MPS(State[complex, torch.Tensor]):
414
420
  else:
415
421
  raise ValueError("Unsupported basis provided")
416
422
 
423
+ nqubits = cls._validate_amplitudes(amplitudes, eigenstates)
424
+
417
425
  basis_0 = torch.tensor([[[1.0], [0.0]]], dtype=torch.complex128) # ground state
418
426
  basis_1 = torch.tensor([[[0.0], [1.0]]], dtype=torch.complex128) # excited state
419
427
 
420
428
  accum_mps = MPS(
421
429
  [torch.zeros((1, 2, 1), dtype=torch.complex128)] * nqubits,
422
430
  orthogonality_center=0,
431
+ eigenstates=eigenstates,
423
432
  )
424
433
 
425
434
  for state, amplitude in amplitudes.items():
426
435
  factors = [basis_1 if ch == one else basis_0 for ch in state]
427
- accum_mps += amplitude * MPS(factors)
436
+ accum_mps += amplitude * MPS(factors, eigenstates=eigenstates)
428
437
  norm = accum_mps.norm()
429
438
  if not math.isclose(1.0, norm, rel_tol=1e-5, abs_tol=0.0):
430
439
  print("\nThe state is not normalized, normalizing it for you.")
@@ -208,6 +208,7 @@ class MPSBackendImpl:
208
208
  [f.clone().detach() for f in initial_state.factors],
209
209
  config=self.config,
210
210
  num_gpus_to_use=self.config.num_gpus_to_use,
211
+ eigenstates=initial_state.eigenstates,
211
212
  )
212
213
  initial_state.truncate()
213
214
  initial_state *= 1 / initial_state.norm()
@@ -496,13 +497,15 @@ class MPSBackendImpl:
496
497
  )
497
498
  full_state = MPS(
498
499
  extended_mps_factors(
499
- normalized_state.factors, self.well_prepared_qubits_filter
500
+ normalized_state.factors,
501
+ self.well_prepared_qubits_filter,
500
502
  ),
501
503
  num_gpus_to_use=None, # Keep the already assigned devices.
502
504
  orthogonality_center=get_extended_site_index(
503
505
  self.well_prepared_qubits_filter,
504
506
  normalized_state.orthogonality_center,
505
507
  ),
508
+ eigenstates=normalized_state.eigenstates,
506
509
  )
507
510
 
508
511
  callback(self.config, fractional_time, full_state, full_mpo, self.results)
@@ -535,7 +538,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
535
538
 
536
539
  def set_jump_threshold(self, bound: float) -> None:
537
540
  self.jump_threshold = random.uniform(0.0, bound)
538
- self.norm_gap_before_jump = self.state.norm() ** 2 - self.jump_threshold
541
+ self.norm_gap_before_jump = self.state.norm().item() ** 2 - self.jump_threshold
539
542
 
540
543
  def init(self) -> None:
541
544
  self.init_lindblad_noise()
@@ -546,7 +549,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
546
549
  previous_time = self.current_time
547
550
  self.current_time = self.target_time
548
551
  previous_norm_gap_before_jump = self.norm_gap_before_jump
549
- self.norm_gap_before_jump = self.state.norm() ** 2 - self.jump_threshold
552
+ self.norm_gap_before_jump = self.state.norm().item() ** 2 - self.jump_threshold
550
553
 
551
554
  if self.root_finder is None:
552
555
  # No quantum jump location finding in progress
@@ -566,7 +569,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
566
569
 
567
570
  return
568
571
 
569
- self.norm_gap_before_jump = self.state.norm() ** 2 - self.jump_threshold
572
+ self.norm_gap_before_jump = self.state.norm().item() ** 2 - self.jump_threshold
570
573
  self.root_finder.provide_ordinate(self.current_time, self.norm_gap_before_jump)
571
574
 
572
575
  if self.root_finder.is_converged(tolerance=1):
@@ -592,7 +595,7 @@ class NoisyMPSBackendImpl(MPSBackendImpl):
592
595
  self.state *= 1 / self.state.norm()
593
596
  self.init_baths()
594
597
 
595
- norm_after_normalizing = self.state.norm()
598
+ norm_after_normalizing = self.state.norm().item()
596
599
  assert math.isclose(norm_after_normalizing, 1, abs_tol=1e-10)
597
600
  self.set_jump_threshold(norm_after_normalizing**2)
598
601
 
@@ -1,9 +1,9 @@
1
1
  from .optimiser import minimize_bandwidth
2
- from .permutations import permute_list, permute_matrix, invert_permutation
2
+ from .permutations import permute_tensor, inv_permutation, permute_string
3
3
 
4
4
  __all__ = [
5
5
  "minimize_bandwidth",
6
- "permute_list",
7
- "permute_matrix",
8
- "invert_permutation",
6
+ "permute_string",
7
+ "permute_tensor",
8
+ "inv_permutation",
9
9
  ]
@@ -1,21 +1,18 @@
1
+ import itertools
2
+
3
+ import torch
1
4
  from scipy.sparse import csr_matrix
2
5
  from scipy.sparse.csgraph import reverse_cuthill_mckee
3
- import numpy as np
4
- from emu_mps.optimatrix.permutations import permute_matrix, permute_list
5
- import itertools
6
6
 
7
+ from emu_mps.optimatrix.permutations import permute_tensor
7
8
 
8
- def is_symmetric(mat: np.ndarray) -> bool:
9
- if mat.shape[0] != mat.shape[1]:
10
- return False
11
- if not np.allclose(mat, mat.T, atol=1e-8):
12
- return False
13
9
 
14
- return True
10
+ def is_symmetric(matrix: torch.Tensor, tol: float = 1e-8) -> bool:
11
+ return torch.allclose(matrix, matrix.T, atol=tol)
15
12
 
16
13
 
17
- def matrix_bandwidth(mat: np.ndarray) -> float:
18
- """matrix_bandwidth(matrix: np.ndarray) -> float
14
+ def matrix_bandwidth(mat: torch.Tensor) -> float:
15
+ """matrix_bandwidth(matrix: torch.tensor) -> torch.Tensor
19
16
 
20
17
  Computes bandwidth as max weighted distance between columns of
21
18
  a square matrix as `max (abs(matrix[i, j] * (j - i))`.
@@ -45,19 +42,27 @@ def matrix_bandwidth(mat: np.ndarray) -> float:
45
42
 
46
43
  Example:
47
44
  -------
48
- >>> matrix = np.array([
49
- ... [ 1, -17, 2.4],
50
- ... [ 9, 1, -10],
51
- ... [-15, 20, 1],])
52
- >>> matrix_bandwidth(matrix) # 30.0 because abs(-15 * (2-0) == 30)
45
+ >>> matrix = torch.tensor([
46
+ ... [1.0, -17.0, 2.4],
47
+ ... [9.0, 1.0, -10.0],
48
+ ... [-15.0, 20.0, 1.0]
49
+ ... ])
50
+ >>> matrix_bandwidth(matrix) # because abs(-15 * (0 - 2)) = 30.0
53
51
  30.0
54
52
  """
55
53
 
56
- bandwidth = max(abs(el * (index[0] - index[1])) for index, el in np.ndenumerate(mat))
57
- return float(bandwidth)
54
+ n = mat.shape[0]
58
55
 
56
+ i_arr = torch.arange(n).view(-1, 1) # shape (n, 1)
57
+ j_arr = torch.arange(n).view(1, -1) # shape (1, n)
59
58
 
60
- def minimize_bandwidth_above_threshold(mat: np.ndarray, threshold: float) -> np.ndarray:
59
+ weighted = torch.abs(mat * (j_arr - i_arr))
60
+ return torch.max(weighted).to(mat.dtype).item()
61
+
62
+
63
+ def minimize_bandwidth_above_threshold(
64
+ mat: torch.Tensor, threshold: float
65
+ ) -> torch.Tensor:
61
66
  """
62
67
  minimize_bandwidth_above_threshold(matrix, trunc) -> permutation_lists
63
68
 
@@ -78,24 +83,25 @@ def minimize_bandwidth_above_threshold(mat: np.ndarray, threshold: float) -> np.
78
83
 
79
84
  Example:
80
85
  -------
81
- >>> matrix = np.array([
82
- ... [1, 2, 3],
83
- ... [2, 5, 6],
84
- ... [3, 6, 9]])
86
+ >>> matrix = torch.tensor([
87
+ ... [1, 2, 3],
88
+ ... [2, 5, 6],
89
+ ... [3, 6, 9]
90
+ ... ], dtype=torch.float32)
85
91
  >>> threshold = 3
86
92
  >>> minimize_bandwidth_above_threshold(matrix, threshold)
87
- array([1, 2, 0], dtype=int32)
93
+ tensor([1, 2, 0], dtype=torch.int32)
88
94
  """
89
95
 
90
- matrix_truncated = mat.copy()
91
- matrix_truncated[mat < threshold] = 0
92
- rcm_permutation = reverse_cuthill_mckee(
93
- csr_matrix(matrix_truncated), symmetric_mode=True
94
- )
95
- return np.array(rcm_permutation)
96
+ m_trunc = mat.clone()
97
+ m_trunc[mat < threshold] = 0.0
96
98
 
99
+ matrix_np = csr_matrix(m_trunc.numpy()) # SciPy's RCM compatibility
100
+ rcm_perm = reverse_cuthill_mckee(matrix_np, symmetric_mode=True)
101
+ return torch.from_numpy(rcm_perm.copy()) # translation requires copy
97
102
 
98
- def minimize_bandwidth_global(mat: np.ndarray) -> list[int]:
103
+
104
+ def minimize_bandwidth_global(mat: torch.Tensor) -> torch.Tensor:
99
105
  """
100
106
  minimize_bandwidth_global(matrix) -> list
101
107
 
@@ -111,74 +117,78 @@ def minimize_bandwidth_global(mat: np.ndarray) -> list[int]:
111
117
  -------
112
118
  permutation order that minimizes matrix bandwidth
113
119
 
114
- Example:
120
+ Example
115
121
  -------
116
- >>> matrix = np.array([
117
- ... [1, 2, 3],
118
- ... [2, 5, 6],
119
- ... [3, 6, 9]])
122
+ >>> matrix = torch.tensor([
123
+ ... [1, 2, 3],
124
+ ... [2, 5, 6],
125
+ ... [3, 6, 9]
126
+ ... ], dtype=torch.float32)
120
127
  >>> minimize_bandwidth_global(matrix)
121
- [2, 1, 0]
128
+ tensor([2, 1, 0], dtype=torch.int32)
122
129
  """
123
- mat_amplitude = np.max(np.abs(mat))
124
- # Search from 1.0 to 0.1 doesn't change result
130
+ mat_amplitude = torch.max(torch.abs(mat))
131
+
125
132
  permutations = (
126
- minimize_bandwidth_above_threshold(mat, trunc * mat_amplitude)
127
- for trunc in np.arange(start=0.1, stop=1.0, step=0.01)
133
+ minimize_bandwidth_above_threshold(mat, trunc.item() * mat_amplitude)
134
+ for trunc in torch.arange(0.1, 1.0, 0.01)
128
135
  )
129
136
 
130
137
  opt_permutation = min(
131
- permutations, key=lambda perm: matrix_bandwidth(permute_matrix(mat, list(perm)))
138
+ permutations, key=lambda perm: matrix_bandwidth(permute_tensor(mat, perm))
132
139
  )
133
- return list(opt_permutation) # opt_permutation is np.ndarray
140
+
141
+ return opt_permutation
134
142
 
135
143
 
136
144
  def minimize_bandwidth_impl(
137
- matrix: np.ndarray, initial_perm: list[int]
138
- ) -> tuple[list[int], float]:
145
+ matrix: torch.Tensor, initial_perm: torch.Tensor
146
+ ) -> tuple[torch.Tensor, float]:
139
147
  """
140
- minimize_bandwidth_impl(matrix, initial_perm) -> list
148
+ minimize_bandwidth_impl(matrix, initial_perm) -> (optimal_perm, bandwidth)
141
149
 
142
150
  Applies initial_perm to a matrix and
143
- finds the permutation list for a symmetric matrix that iteratively minimizes matrix bandwidth.
151
+ finds the permutation list for a symmetric matrix
152
+ that iteratively minimizes matrix bandwidth.
144
153
 
145
154
  Parameters
146
155
  -------
147
156
  matrix :
148
157
  symmetric square matrix
149
- initial_perm: list of integers
158
+ initial_perm: torch list of integers
150
159
 
151
160
 
152
161
  Returns
153
162
  -------
154
- permutation order that minimizes matrix bandwidth
163
+ optimal permutation and optimal matrix bandwidth
155
164
 
156
165
  Example:
157
166
  -------
158
167
  Periodic 1D chain
159
- >>> matrix = np.array([
168
+ >>> matrix = torch.tensor([
160
169
  ... [0, 1, 0, 0, 1],
161
170
  ... [1, 0, 1, 0, 0],
162
171
  ... [0, 1, 0, 1, 0],
163
172
  ... [0, 0, 1, 0, 1],
164
- ... [1, 0, 0, 1, 0]])
165
- >>> id_perm = list(range(matrix.shape[0]))
173
+ ... [1, 0, 0, 1, 0]], dtype=torch.float32)
174
+ >>> id_perm = torch.arange(matrix.shape[0])
166
175
  >>> minimize_bandwidth_impl(matrix, id_perm) # [3, 2, 4, 1, 0] does zig-zag
167
- ([3, 2, 4, 1, 0], 2.0)
176
+ (tensor([3, 2, 4, 1, 0]), 2.0)
168
177
 
169
178
  Simple 1D chain. Cannot be optimised further
170
- >>> matrix = np.array([
179
+ >>> matrix = torch.tensor([
171
180
  ... [0, 1, 0, 0, 0],
172
181
  ... [1, 0, 1, 0, 0],
173
182
  ... [0, 1, 0, 1, 0],
174
183
  ... [0, 0, 1, 0, 1],
175
- ... [0, 0, 0, 1, 0]])
176
- >>> id_perm = list(range(matrix.shape[0]))
184
+ ... [0, 0, 0, 1, 0]], dtype=torch.float32)
185
+ >>> id_perm = torch.arange(matrix.shape[0])
177
186
  >>> minimize_bandwidth_impl(matrix, id_perm)
178
- ([0, 1, 2, 3, 4], 1.0)
187
+ (tensor([0, 1, 2, 3, 4]), 1.0)
179
188
  """
180
- if initial_perm != list(range(matrix.shape[0])):
181
- matrix = permute_matrix(matrix, initial_perm)
189
+ L = matrix.shape[0]
190
+ if not torch.equal(initial_perm, torch.arange(L)):
191
+ matrix = permute_tensor(matrix, initial_perm)
182
192
  bandwidth = matrix_bandwidth(matrix)
183
193
  acc_permutation = initial_perm
184
194
 
@@ -191,28 +201,28 @@ def minimize_bandwidth_impl(
191
201
  )
192
202
 
193
203
  optimal_perm = minimize_bandwidth_global(matrix)
194
- test_mat = permute_matrix(matrix, optimal_perm)
204
+ test_mat = permute_tensor(matrix, optimal_perm)
195
205
  new_bandwidth = matrix_bandwidth(test_mat)
196
206
 
197
207
  if bandwidth <= new_bandwidth:
198
208
  break
199
209
 
200
210
  matrix = test_mat
201
- acc_permutation = permute_list(acc_permutation, optimal_perm)
211
+ acc_permutation = permute_tensor(acc_permutation, optimal_perm)
202
212
  bandwidth = new_bandwidth
203
213
 
204
214
  return acc_permutation, bandwidth
205
215
 
206
216
 
207
- def minimize_bandwidth(input_matrix: np.ndarray, samples: int = 100) -> list[int]:
217
+ def minimize_bandwidth(input_matrix: torch.Tensor, samples: int = 100) -> torch.Tensor:
208
218
  assert is_symmetric(input_matrix), "Input matrix is not symmetric"
209
- input_mat = abs(input_matrix)
219
+ input_mat = torch.abs(input_matrix)
210
220
  # We are interested in strength of the interaction, not sign
211
221
 
212
222
  L = input_mat.shape[0]
213
- rnd_permutations: itertools.chain[list[int]] = itertools.chain(
214
- [list(range(L))], # First element is always the identity list
215
- (np.random.permutation(L).tolist() for _ in range(samples)), # type: ignore[misc]
223
+ rnd_permutations = itertools.chain(
224
+ [torch.arange(L)], # identity permutation
225
+ [torch.randperm(L) for _ in range(samples)], # list of random permutations
216
226
  )
217
227
 
218
228
  opt_permutations_and_opt_bandwidth = (
@@ -1,36 +1,31 @@
1
- import numpy as np
1
+ import torch
2
2
 
3
3
 
4
- def permute_list(input_list: list, permutation: list[int]) -> list:
4
+ def permute_string(input_str: str, perm: torch.Tensor) -> str:
5
5
  """
6
- Permutes the input list according to the given permutation.
7
-
6
+ Permutes the input string according to the given permutation.
8
7
  Parameters
9
8
  -------
10
- input_list :
11
- A list to permute.
9
+ input_string :
10
+ A string to permute.
12
11
  permutation :
13
12
  A list of indices representing the new order.
14
-
15
13
  Returns
16
14
  -------
17
- The permuted list.
18
-
15
+ The permuted string.
19
16
  Example
20
17
  -------
21
- >>> permute_list(['a', 'b', 'c'], [2, 0, 1])
22
- ['c', 'a', 'b']
18
+ >>> permute_string("abc", torch.tensor([2, 0, 1]))
19
+ 'cab'
23
20
  """
21
+ char_list = list(input_str)
22
+ permuted = [char_list[i] for i in perm.tolist()]
23
+ return "".join(permuted)
24
24
 
25
- permuted_list = [None] * len(input_list)
26
- for i, p in enumerate(permutation):
27
- permuted_list[i] = input_list[p]
28
- return permuted_list
29
25
 
30
-
31
- def invert_permutation(permutation: list[int]) -> list[int]:
26
+ def inv_permutation(permutation: torch.Tensor) -> torch.Tensor:
32
27
  """
33
- invert_permutation(permutation) -> inv_permutation
28
+ inv_permutation(permutation) -> inverted_perm
34
29
 
35
30
  Inverts the input permutation list.
36
31
 
@@ -45,47 +40,60 @@ def invert_permutation(permutation: list[int]) -> list[int]:
45
40
 
46
41
  Example:
47
42
  -------
48
- >>> invert_permutation([2, 0, 1])
49
- [1, 2, 0]
43
+ >>> inv_permutation(torch.tensor([2, 0, 1]))
44
+ tensor([1, 2, 0])
50
45
  """
51
-
52
- inv_perm = np.empty_like(permutation)
53
- inv_perm[permutation] = np.arange(len(permutation))
54
- return list(inv_perm)
46
+ inv_perm = torch.empty_like(permutation)
47
+ inv_perm[permutation] = torch.arange(len(permutation))
48
+ return inv_perm
55
49
 
56
50
 
57
- def permute_matrix(mat: np.ndarray, permutation: list[int]) -> np.ndarray:
51
+ def permute_tensor(tensor: torch.Tensor, perm: torch.Tensor) -> torch.Tensor:
58
52
  """
59
- permute_matrix(matrix, permutation_list) -> permuted_matrix
60
-
61
- Simultaneously permutes columns and rows according to a permutation list.
53
+ Permute a 1D or square 2D torch tensor using the given permutation indices.
54
+ For 1D tensors, applies the permutation to the elements.
55
+ For 2D square tensors, applies the same permutation to both rows and columns.
62
56
 
63
57
  Parameters
64
- -------
65
- matrix :
66
- square matrix nxn
67
- permutation :
68
- permutation list
58
+ ----------
59
+ tensor : torch.Tensor
60
+ A 1D or 2D square tensor to be permuted.
61
+ perm : torch.Tensor
62
+ A 1D tensor of indices specifying the permutation order.
69
63
 
70
64
  Returns
71
65
  -------
72
- matrix with permuted columns and rows
73
-
74
- Example:
75
- -------
76
- >>> matrix = np.array([
77
- ... [1, 2, 3],
78
- ... [4, 5, 6],
79
- ... [7, 8, 9]])
80
- >>> permutation = [1, 0, 2]
81
- >>> permute_matrix(matrix, permutation)
82
- array([[5, 4, 6],
83
- [2, 1, 3],
84
- [8, 7, 9]])
66
+ torch.Tensor
67
+ A new tensor with elements (1D) or rows and columns (2D) permuted according to `perm`.
68
+
69
+ Raises
70
+ ------
71
+ ValueError
72
+ If tensor is not 1D or square 2D.
73
+
74
+ Examples
75
+ --------
76
+ >>> vector = torch.tensor([10, 20, 30])
77
+ >>> perm = torch.tensor([2, 0, 1])
78
+ >>> permute_tensor(vector, perm)
79
+ tensor([30, 10, 20])
80
+
81
+ >>> matrix = torch.tensor([
82
+ ... [1, 2, 3],
83
+ ... [4, 5, 6],
84
+ ... [7, 8, 9]])
85
+ >>> perm = torch.tensor([1, 0, 2])
86
+ >>> permute_tensor(matrix, perm)
87
+ tensor([[5, 4, 6],
88
+ [2, 1, 3],
89
+ [8, 7, 9]])
85
90
  """
86
-
87
- perm = np.array(permutation)
88
- return mat[perm, :][:, perm]
91
+ if tensor.ndim == 1:
92
+ return tensor[perm]
93
+ elif tensor.ndim == 2 and tensor.shape[0] == tensor.shape[1]:
94
+ return tensor[perm][:, perm]
95
+ else:
96
+ raise ValueError("Only 1D tensors or square 2D tensors are supported.")
89
97
 
90
98
 
91
99
  if __name__ == "__main__":
emu_mps/tdvp.py CHANGED
@@ -78,7 +78,7 @@ def apply_effective_Hamiltonian(
78
78
  return state
79
79
 
80
80
 
81
- _TIME_CONVERSION_COEFF = 0.001 # Omega and delta are given in rad/ms, dt in ns
81
+ _TIME_CONVERSION_COEFF = 0.001 # Omega and delta are given in rad/μs, dt in ns
82
82
 
83
83
 
84
84
  def evolve_pair(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emu-mps
3
- Version: 2.0.1
3
+ Version: 2.0.2
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.1
28
+ Requires-Dist: emu-base==2.0.2
29
29
  Description-Content-Type: text/markdown
30
30
 
31
31
  <div align="center">
@@ -0,0 +1,19 @@
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,,
@@ -1,19 +0,0 @@
1
- emu_mps/__init__.py,sha256=GIR0FJOvmMHby7Vx_Da3w7YqqLpOxa0E8E3poxXsapY,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=LcBs6CKBb643a1e9AAVtQoUfa4L_0dIhLOKecx5OOWs,15864
5
- emu_mps/mpo.py,sha256=wSonS6i3zEt3yRTgyZ7F6vT51pUKavFcLOxFFphBv8k,8793
6
- emu_mps/mps.py,sha256=KqAjo-nxgM-xQSg1NFNchwXKoPRcrKuuycFMsWr7iX8,19610
7
- emu_mps/mps_backend.py,sha256=_3rlg6XeI4fHaDiJRfPL6pDkX9k48hAHKXd8fkvkOFs,2004
8
- emu_mps/mps_backend_impl.py,sha256=M_do7QRBLAoHfwF_EpyMCb6g7w7BSs5hsPa5UE0z9Zs,22958
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=pIQ2NXA2Mrkp3elhqQbX3pdJVbtKkG3c5r9fFlJo7pI,5755
13
- emu_mps/utils.py,sha256=BqRJYAcXqprtZVJ0V_j954ON2bhTdtZiaTojsYyrWrg,8193
14
- emu_mps/optimatrix/__init__.py,sha256=lHWYNeThHp57ZrwTwXd0p8bNvcCv0w_AZ31iCWflBUo,226
15
- emu_mps/optimatrix/optimiser.py,sha256=7j9_jMQC-Uh2DzdIVB44InRzZO6AbbGhvmm7lC6N3tk,6737
16
- emu_mps/optimatrix/permutations.py,sha256=JRXGont8B4QgbkV9CzrA0w7uzLgBrmZ1J9aa0G52hPo,1979
17
- emu_mps-2.0.1.dist-info/METADATA,sha256=aGt2cFa9rplEaJAo8z4J5uoAkqq9WnQIlOhciUc6vkg,3505
18
- emu_mps-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- emu_mps-2.0.1.dist-info/RECORD,,