tensorcircuit-nightly 1.3.0.dev20250728__py3-none-any.whl → 1.4.0.dev20251103__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.

Potentially problematic release.


This version of tensorcircuit-nightly might be problematic. Click here for more details.

Files changed (72) hide show
  1. tensorcircuit/__init__.py +5 -1
  2. tensorcircuit/abstractcircuit.py +4 -0
  3. tensorcircuit/analogcircuit.py +413 -0
  4. tensorcircuit/applications/layers.py +1 -1
  5. tensorcircuit/applications/van.py +1 -1
  6. tensorcircuit/backends/abstract_backend.py +312 -5
  7. tensorcircuit/backends/cupy_backend.py +3 -1
  8. tensorcircuit/backends/jax_backend.py +92 -3
  9. tensorcircuit/backends/jax_ops.py +108 -0
  10. tensorcircuit/backends/numpy_backend.py +49 -3
  11. tensorcircuit/backends/pytorch_backend.py +92 -3
  12. tensorcircuit/backends/tensorflow_backend.py +102 -3
  13. tensorcircuit/basecircuit.py +123 -82
  14. tensorcircuit/circuit.py +67 -57
  15. tensorcircuit/cloud/local.py +1 -1
  16. tensorcircuit/cloud/quafu_provider.py +1 -1
  17. tensorcircuit/cloud/tencent.py +1 -1
  18. tensorcircuit/compiler/simple_compiler.py +2 -2
  19. tensorcircuit/cons.py +1 -0
  20. tensorcircuit/densitymatrix.py +16 -11
  21. tensorcircuit/experimental.py +7 -152
  22. tensorcircuit/fgs.py +5 -6
  23. tensorcircuit/gates.py +66 -22
  24. tensorcircuit/keras.py +3 -3
  25. tensorcircuit/mpscircuit.py +109 -61
  26. tensorcircuit/quantum.py +697 -133
  27. tensorcircuit/quditcircuit.py +733 -0
  28. tensorcircuit/quditgates.py +618 -0
  29. tensorcircuit/results/counts.py +45 -31
  30. tensorcircuit/shadows.py +1 -1
  31. tensorcircuit/simplify.py +3 -1
  32. tensorcircuit/stabilizercircuit.py +4 -2
  33. tensorcircuit/templates/blocks.py +2 -2
  34. tensorcircuit/templates/hamiltonians.py +29 -8
  35. tensorcircuit/templates/lattice.py +676 -335
  36. tensorcircuit/timeevol.py +896 -0
  37. {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/METADATA +50 -25
  38. tensorcircuit_nightly-1.4.0.dev20251103.dist-info/RECORD +96 -0
  39. {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/top_level.txt +0 -1
  40. tensorcircuit_nightly-1.3.0.dev20250728.dist-info/RECORD +0 -122
  41. tests/__init__.py +0 -0
  42. tests/conftest.py +0 -67
  43. tests/test_backends.py +0 -1035
  44. tests/test_calibrating.py +0 -149
  45. tests/test_channels.py +0 -409
  46. tests/test_circuit.py +0 -1713
  47. tests/test_cloud.py +0 -219
  48. tests/test_compiler.py +0 -147
  49. tests/test_dmcircuit.py +0 -555
  50. tests/test_ensemble.py +0 -72
  51. tests/test_fgs.py +0 -318
  52. tests/test_gates.py +0 -156
  53. tests/test_hamiltonians.py +0 -159
  54. tests/test_interfaces.py +0 -557
  55. tests/test_keras.py +0 -160
  56. tests/test_lattice.py +0 -1666
  57. tests/test_miscs.py +0 -334
  58. tests/test_mpscircuit.py +0 -341
  59. tests/test_noisemodel.py +0 -156
  60. tests/test_qaoa.py +0 -86
  61. tests/test_qem.py +0 -152
  62. tests/test_quantum.py +0 -549
  63. tests/test_quantum_attr.py +0 -42
  64. tests/test_results.py +0 -379
  65. tests/test_shadows.py +0 -160
  66. tests/test_simplify.py +0 -46
  67. tests/test_stabilizer.py +0 -226
  68. tests/test_templates.py +0 -218
  69. tests/test_torchnn.py +0 -99
  70. tests/test_van.py +0 -102
  71. {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/WHEEL +0 -0
  72. {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/licenses/LICENSE +0 -0
@@ -6,6 +6,7 @@ from typing import Any, Dict, Optional, Sequence
6
6
 
7
7
  import numpy as np
8
8
 
9
+ from ..quantum import _decode_basis_label
9
10
 
10
11
  Tensor = Any
11
12
  ct = Dict[str, int]
@@ -89,14 +90,24 @@ def marginal_count(count: ct, keep_list: Sequence[int]) -> ct:
89
90
  return reverse_count(ncount)
90
91
 
91
92
 
92
- def count2vec(count: ct, normalization: bool = True) -> Tensor:
93
+ def count2vec(
94
+ count: ct, normalization: bool = True, dim: Optional[int] = None
95
+ ) -> Tensor:
93
96
  """
94
- Convert count dictionary to probability vector.
97
+ Convert a dictionary of counts (with string keys) to a probability/count vector.
98
+
99
+ Support:
100
+ - base-d string (d <= 36), characters taken from 0-9A-Z (case-insensitive)
101
+ For example:
102
+ qubit: '0101'
103
+ qudit: '012' or '09A' (A represents 10, which means [0, 9, 10])
95
104
 
96
105
  :param count: A dictionary mapping bit strings to counts
97
106
  :type count: ct
98
107
  :param normalization: Whether to normalize the counts to probabilities, defaults to True
99
108
  :type normalization: bool, optional
109
+ :param dim: Dimensionality of the vector, defaults to 2
110
+ :type dim: int, optional
100
111
  :return: Probability vector as numpy array
101
112
  :rtype: Tensor
102
113
 
@@ -105,44 +116,47 @@ def count2vec(count: ct, normalization: bool = True) -> Tensor:
105
116
  >>> count2vec({"00": 2, "10": 3, "11": 5})
106
117
  array([0.2, 0. , 0.3, 0.5])
107
118
  """
108
- nqubit = len(list(count.keys())[0])
109
- probability = [0] * 2**nqubit
110
- shots = sum([v for k, v in count.items()])
111
- for k, v in count.items():
112
- if normalization is True:
113
- v /= shots # type: ignore
114
- probability[int(k, 2)] = v
115
- return np.array(probability)
116
119
 
120
+ if not count:
121
+ return np.array([], dtype=float)
117
122
 
118
- def vec2count(vec: Tensor, prune: bool = False) -> ct:
119
- """
120
- Convert probability vector to count dictionary.
123
+ dim = 2 if dim is None else dim
121
124
 
122
- :param vec: Probability vector
123
- :type vec: Tensor
124
- :param prune: Whether to remove near-zero probabilities, defaults to False
125
- :type prune: bool, optional
126
- :return: Count dictionary
127
- :rtype: ct
125
+ n = len(next(iter(count)).upper())
126
+ prob = np.zeros(dim**n, dtype=float)
127
+ shots = float(sum(count.values())) if normalization else 1.0
128
+ if shots == 0:
129
+ return prob
130
+
131
+ powers = [dim**p for p in range(n)][::-1]
132
+ for k, v in count.items():
133
+ digits = _decode_basis_label(k, n, dim)
134
+ idx = sum(dig * p for dig, p in zip(digits, powers))
135
+ prob[idx] = (v / shots) if normalization else v
136
+
137
+ return prob
128
138
 
129
- :Example:
130
139
 
131
- >>> vec2count(np.array([0.2, 0.3, 0.1, 0.4]))
132
- {'00': 0.2, '01': 0.3, '10': 0.1, '11': 0.4}
140
+ def vec2count(vec: Tensor, prune: bool = False, dim: Optional[int] = None) -> ct:
133
141
  """
134
- from ..quantum import count_vector2dict
142
+ Map a count/probability vector of length D to a dictionary with base-d string keys (0-9A-Z).
143
+ Only generate string keys when d <= 36; if d is inferred to be > 36, raise a NotImplementedError.
135
144
 
145
+ :param vec: A one-dimensional vector of length D = d**n
146
+ :param prune: Whether to prune near-zero elements (threshold 1e-8)
147
+ :param dim: Dimensionality of the vector, defaults to 2
148
+ :return: {base-d string key: value}, key length n
149
+ """
150
+ from ..quantum import count_vector2dict, _infer_num_sites
151
+
152
+ dim = 2 if dim is None else dim
136
153
  if isinstance(vec, list):
137
154
  vec = np.array(vec)
138
- n = int(np.log(vec.shape[0]) / np.log(2) + 1e-9)
139
- c = count_vector2dict(vec, n, key="bin")
140
- if prune is True:
141
- nc = c.copy()
142
- for k, v in c.items():
143
- if np.abs(v) < 1e-8:
144
- del nc[k]
145
- return nc
155
+ n = _infer_num_sites(int(vec.shape[0]), dim)
156
+ c: ct = count_vector2dict(vec, n, key="bin", dim=dim) # type: ignore
157
+ if prune:
158
+ c = {k: v for k, v in c.items() if np.abs(v) >= 1e-8}
159
+
146
160
  return c
147
161
 
148
162
 
tensorcircuit/shadows.py CHANGED
@@ -334,7 +334,7 @@ def entropy_shadow(
334
334
 
335
335
  def renyi_entropy_2(snapshots: Tensor, sub: Optional[Sequence[int]] = None) -> Tensor:
336
336
  r"""To calculate the second order Renyi entropy of a subsystem from snapshot, please refer to
337
- Brydges, T. et al. Science 364, 260263 (2019). This function is not jitable.
337
+ Brydges, T. et al. Science 364, 260-263 (2019). This function is not jitable.
338
338
 
339
339
  :param snapshots: shape = (ns, repeat, nq)
340
340
  :type: Tensor
tensorcircuit/simplify.py CHANGED
@@ -121,7 +121,9 @@ def _split_two_qubit_gate(
121
121
  if fixed_choice == 2: # swap one
122
122
  return n3, n4, True # swap
123
123
  s2 = n3.tensor.shape[-1]
124
- if (s1 >= 4) and (s2 >= 4):
124
+ if (s1 >= n[0].dimension * n[2].dimension) and (
125
+ s2 >= n[1].dimension * n[3].dimension
126
+ ):
125
127
  # jax jit unspport split_node with trun_err anyway
126
128
  # tf function doesn't work either, though I believe it may work on tf side
127
129
  # CANNOT DONE(@refraction-ray): tf.function version with trun_err set
@@ -96,10 +96,12 @@ class StabilizerCircuit(AbstractCircuit):
96
96
 
97
97
  if name.lower() in self.gate_map:
98
98
  # self._stim_circuit.append(gate_map[name.lower()], list(index))
99
- instruction = f"{self.gate_map[name.lower()]} {' '.join(map(str, index))}"
99
+ gn = self.gate_map[name.lower()]
100
+ instruction = f"{gn} {' '.join(map(str, index))}"
100
101
  self._stim_circuit.append_from_stim_program_text(instruction)
101
102
  # append is much slower
102
- self.current_sim.do(stim.Circuit(instruction))
103
+ # self.current_sim.do(stim.Circuit(instruction))
104
+ getattr(self.current_sim, gn.lower())(*index)
103
105
  else:
104
106
  raise ValueError(f"Gate {name} is not supported in stabilizer simulation")
105
107
 
@@ -91,7 +91,7 @@ def QAOA_block(
91
91
  e2,
92
92
  unitary=G._zz_matrix,
93
93
  theta=paramzz * g[e1][e2].get("weight", 1.0),
94
- **kws
94
+ **kws,
95
95
  )
96
96
  else:
97
97
  i = 0
@@ -157,7 +157,7 @@ def qft(
157
157
  *index: int,
158
158
  do_swaps: bool = True,
159
159
  inverse: bool = False,
160
- insert_barriers: bool = False
160
+ insert_barriers: bool = False,
161
161
  ) -> Circuit:
162
162
  """
163
163
  This function applies quantum fourier transformation (QFT) to the selected circuit lines
@@ -17,13 +17,14 @@ def _create_empty_sparse_matrix(shape: Tuple[int, int]) -> Any:
17
17
  def heisenberg_hamiltonian(
18
18
  lattice: AbstractLattice,
19
19
  j_coupling: Union[float, List[float], Tuple[float, ...]] = 1.0,
20
+ interaction_scope: str = "neighbors",
20
21
  ) -> Any:
21
- """
22
+ r"""
22
23
  Generates the sparse matrix of the Heisenberg Hamiltonian for a given lattice.
23
24
 
24
25
  The Heisenberg Hamiltonian is defined as:
25
- H = J * Σ_{<i,j>} (X_i X_j + Y_i Y_j + Z_i Z_j)
26
- where the sum is over all unique nearest-neighbor pairs <i,j>.
26
+ :math:`H = J\sum_{i,j} (X_i X_j + Y_i Y_j + Z_i Z_j)`
27
+ where the sum is over a specified set of interacting pairs {i,j}.
27
28
 
28
29
  :param lattice: An instance of a class derived from AbstractLattice,
29
30
  which provides the geometric information of the system.
@@ -32,11 +33,23 @@ def heisenberg_hamiltonian(
32
33
  isotropic model (Jx=Jy=Jz) or a list/tuple of 3 floats for an
33
34
  anisotropic model (Jx, Jy, Jz). Defaults to 1.0.
34
35
  :type j_coupling: Union[float, List[float], Tuple[float, ...]], optional
36
+ :param interaction_scope: Defines the range of interactions.
37
+ - "neighbors": Includes only nearest-neighbor pairs (default).
38
+ - "all": Includes all unique pairs of sites.
39
+ :type interaction_scope: str, optional
35
40
  :return: The Hamiltonian as a backend-agnostic sparse matrix.
36
41
  :rtype: Any
37
42
  """
38
43
  num_sites = lattice.num_sites
39
- neighbor_pairs = lattice.get_neighbor_pairs(k=1, unique=True)
44
+ if interaction_scope == "neighbors":
45
+ neighbor_pairs = lattice.get_neighbor_pairs(k=1, unique=True)
46
+ elif interaction_scope == "all":
47
+ neighbor_pairs = lattice.get_all_pairs()
48
+ else:
49
+ raise ValueError(
50
+ f"Invalid interaction_scope: '{interaction_scope}'. "
51
+ "Must be 'neighbors' or 'all'."
52
+ )
40
53
 
41
54
  if isinstance(j_coupling, (float, int)):
42
55
  js = [float(j_coupling)] * 3
@@ -73,13 +86,21 @@ def heisenberg_hamiltonian(
73
86
  def rydberg_hamiltonian(
74
87
  lattice: AbstractLattice, omega: float, delta: float, c6: float
75
88
  ) -> Any:
76
- """
89
+ r"""
77
90
  Generates the sparse matrix of the Rydberg atom array Hamiltonian.
78
91
 
79
92
  The Hamiltonian is defined as:
80
- H = Σ_i (Ω/2)X_i - Σ_i δ(1 - Z_i)/2 + Σ_{i<j} V_ij (1-Z_i)/2 (1-Z_j)/2
81
- = Σ_i (Ω/2)X_i + Σ_i (δ/2)Z_i + Σ_{i<j} (V_ij/4)(Z_iZ_j - Z_i - Z_j)
82
- where V_ij = C6 / |r_i - r_j|^6.
93
+ .. math::
94
+
95
+ H = \sum_i \frac{\Omega}{2} X_i
96
+ - \sum_i \frac{\delta}{2} \bigl(1 - Z_i \bigr)
97
+ + \sum_{i<j} \frac{V_{ij}}{4} \bigl(1 - Z_i \bigr)\bigl(1 - Z_j \bigr)
98
+
99
+ = \sum_i \frac{\Omega}{2} X_i
100
+ + \sum_i \frac{\delta}{2} Z_i
101
+ + \sum_{i<j} \frac{V_{ij}}{4}\,\bigl(Z_i Z_j - Z_i - Z_j \bigr)
102
+
103
+ where :math:`V_{ij} = C6 / |r_i - r_j|^6`.
83
104
 
84
105
  Note: Constant energy offset terms (proportional to the identity operator)
85
106
  are ignored in this implementation.