tensorcircuit-nightly 1.0.2.dev20250108__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 (76) hide show
  1. tensorcircuit/__init__.py +18 -2
  2. tensorcircuit/about.py +46 -0
  3. tensorcircuit/abstractcircuit.py +4 -0
  4. tensorcircuit/analogcircuit.py +413 -0
  5. tensorcircuit/applications/layers.py +1 -1
  6. tensorcircuit/applications/van.py +1 -1
  7. tensorcircuit/backends/abstract_backend.py +320 -7
  8. tensorcircuit/backends/cupy_backend.py +3 -1
  9. tensorcircuit/backends/jax_backend.py +102 -4
  10. tensorcircuit/backends/jax_ops.py +110 -1
  11. tensorcircuit/backends/numpy_backend.py +49 -3
  12. tensorcircuit/backends/pytorch_backend.py +92 -3
  13. tensorcircuit/backends/tensorflow_backend.py +102 -3
  14. tensorcircuit/basecircuit.py +157 -98
  15. tensorcircuit/circuit.py +115 -57
  16. tensorcircuit/cloud/local.py +1 -1
  17. tensorcircuit/cloud/quafu_provider.py +1 -1
  18. tensorcircuit/cloud/tencent.py +1 -1
  19. tensorcircuit/compiler/simple_compiler.py +2 -2
  20. tensorcircuit/cons.py +142 -21
  21. tensorcircuit/densitymatrix.py +43 -14
  22. tensorcircuit/experimental.py +387 -129
  23. tensorcircuit/fgs.py +282 -81
  24. tensorcircuit/gates.py +66 -22
  25. tensorcircuit/interfaces/__init__.py +1 -3
  26. tensorcircuit/interfaces/jax.py +189 -0
  27. tensorcircuit/keras.py +3 -3
  28. tensorcircuit/mpscircuit.py +154 -65
  29. tensorcircuit/quantum.py +868 -152
  30. tensorcircuit/quditcircuit.py +733 -0
  31. tensorcircuit/quditgates.py +618 -0
  32. tensorcircuit/results/counts.py +147 -20
  33. tensorcircuit/results/readout_mitigation.py +4 -1
  34. tensorcircuit/shadows.py +1 -1
  35. tensorcircuit/simplify.py +3 -1
  36. tensorcircuit/stabilizercircuit.py +479 -0
  37. tensorcircuit/templates/__init__.py +2 -0
  38. tensorcircuit/templates/blocks.py +2 -2
  39. tensorcircuit/templates/hamiltonians.py +174 -0
  40. tensorcircuit/templates/lattice.py +1789 -0
  41. tensorcircuit/timeevol.py +896 -0
  42. tensorcircuit/translation.py +10 -3
  43. tensorcircuit/utils.py +7 -0
  44. {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/METADATA +73 -23
  45. tensorcircuit_nightly-1.4.0.dev20251103.dist-info/RECORD +96 -0
  46. {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/WHEEL +1 -1
  47. {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/top_level.txt +0 -1
  48. tensorcircuit_nightly-1.0.2.dev20250108.dist-info/RECORD +0 -115
  49. tests/__init__.py +0 -0
  50. tests/conftest.py +0 -67
  51. tests/test_backends.py +0 -1031
  52. tests/test_calibrating.py +0 -149
  53. tests/test_channels.py +0 -365
  54. tests/test_circuit.py +0 -1699
  55. tests/test_cloud.py +0 -219
  56. tests/test_compiler.py +0 -147
  57. tests/test_dmcircuit.py +0 -555
  58. tests/test_ensemble.py +0 -72
  59. tests/test_fgs.py +0 -310
  60. tests/test_gates.py +0 -156
  61. tests/test_interfaces.py +0 -429
  62. tests/test_keras.py +0 -160
  63. tests/test_miscs.py +0 -277
  64. tests/test_mpscircuit.py +0 -341
  65. tests/test_noisemodel.py +0 -156
  66. tests/test_qaoa.py +0 -86
  67. tests/test_qem.py +0 -152
  68. tests/test_quantum.py +0 -526
  69. tests/test_quantum_attr.py +0 -42
  70. tests/test_results.py +0 -347
  71. tests/test_shadows.py +0 -160
  72. tests/test_simplify.py +0 -46
  73. tests/test_templates.py +0 -218
  74. tests/test_torchnn.py +0 -99
  75. tests/test_van.py +0 -102
  76. {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info/licenses}/LICENSE +0 -0
@@ -6,14 +6,26 @@ 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]
12
13
 
13
- # TODO(@refraction-ray): merge_count
14
-
15
14
 
16
15
  def reverse_count(count: ct) -> ct:
16
+ """
17
+ Reverse the bit string keys in a count dictionary.
18
+
19
+ :param count: A dictionary mapping bit strings to counts
20
+ :type count: ct
21
+ :return: A new dictionary with reversed bit string keys
22
+ :rtype: ct
23
+
24
+ :Example:
25
+
26
+ >>> reverse_count({"01": 10, "10": 20})
27
+ {'10': 10, '01': 20}
28
+ """
17
29
  ncount = {}
18
30
  for k, v in count.items():
19
31
  ncount[k[::-1]] = v
@@ -21,15 +33,56 @@ def reverse_count(count: ct) -> ct:
21
33
 
22
34
 
23
35
  def sort_count(count: ct) -> ct:
36
+ """
37
+ Sort the count dictionary by counts in descending order.
38
+
39
+ :param count: A dictionary mapping bit strings to counts
40
+ :type count: ct
41
+ :return: A new dictionary sorted by count values (descending)
42
+ :rtype: ct
43
+
44
+ :Example:
45
+
46
+ >>> sort_count({"00": 5, "01": 15, "10": 10})
47
+ {'01': 15, '10': 10, '00': 5}
48
+ """
24
49
  return {k: v for k, v in sorted(count.items(), key=lambda item: -item[1])}
25
50
 
26
51
 
27
52
  def normalized_count(count: ct) -> Dict[str, float]:
53
+ """
54
+ Normalize the count dictionary to represent probabilities.
55
+
56
+ :param count: A dictionary mapping bit strings to counts
57
+ :type count: ct
58
+ :return: A new dictionary with probabilities instead of counts
59
+ :rtype: Dict[str, float]
60
+
61
+ :Example:
62
+
63
+ >>> normalized_count({"00": 5, "01": 15})
64
+ {'00': 0.25, '01': 0.75}
65
+ """
28
66
  shots = sum([v for k, v in count.items()])
29
67
  return {k: v / shots for k, v in count.items()}
30
68
 
31
69
 
32
70
  def marginal_count(count: ct, keep_list: Sequence[int]) -> ct:
71
+ """
72
+ Compute the marginal distribution of a count dictionary over specified qubits.
73
+
74
+ :param count: A dictionary mapping bit strings to counts
75
+ :type count: ct
76
+ :param keep_list: List of qubit indices to keep in the marginal distribution
77
+ :type keep_list: Sequence[int]
78
+ :return: A new count dictionary with marginal distribution
79
+ :rtype: ct
80
+
81
+ :Example:
82
+
83
+ >>> marginal_count({"001": 10, "110": 20}, [0, 2])
84
+ {'01': 10, '10': 20}
85
+ """
33
86
  import qiskit
34
87
 
35
88
  count = reverse_count(count)
@@ -37,34 +90,87 @@ def marginal_count(count: ct, keep_list: Sequence[int]) -> ct:
37
90
  return reverse_count(ncount)
38
91
 
39
92
 
40
- def count2vec(count: ct, normalization: bool = True) -> Tensor:
41
- nqubit = len(list(count.keys())[0])
42
- probability = [0] * 2**nqubit
43
- shots = sum([v for k, v in count.items()])
93
+ def count2vec(
94
+ count: ct, normalization: bool = True, dim: Optional[int] = None
95
+ ) -> Tensor:
96
+ """
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])
104
+
105
+ :param count: A dictionary mapping bit strings to counts
106
+ :type count: ct
107
+ :param normalization: Whether to normalize the counts to probabilities, defaults to True
108
+ :type normalization: bool, optional
109
+ :param dim: Dimensionality of the vector, defaults to 2
110
+ :type dim: int, optional
111
+ :return: Probability vector as numpy array
112
+ :rtype: Tensor
113
+
114
+ :Example:
115
+
116
+ >>> count2vec({"00": 2, "10": 3, "11": 5})
117
+ array([0.2, 0. , 0.3, 0.5])
118
+ """
119
+
120
+ if not count:
121
+ return np.array([], dtype=float)
122
+
123
+ dim = 2 if dim is None else dim
124
+
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]
44
132
  for k, v in count.items():
45
- if normalization is True:
46
- v /= shots # type: ignore
47
- probability[int(k, 2)] = v
48
- return np.array(probability)
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
49
136
 
137
+ return prob
138
+
139
+
140
+ def vec2count(vec: Tensor, prune: bool = False, dim: Optional[int] = None) -> ct:
141
+ """
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.
50
144
 
51
- def vec2count(vec: Tensor, prune: bool = False) -> ct:
52
- from ..quantum import count_vector2dict
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
53
151
 
152
+ dim = 2 if dim is None else dim
54
153
  if isinstance(vec, list):
55
154
  vec = np.array(vec)
56
- n = int(np.log(vec.shape[0]) / np.log(2) + 1e-9)
57
- c = count_vector2dict(vec, n, key="bin")
58
- if prune is True:
59
- nc = c.copy()
60
- for k, v in c.items():
61
- if np.abs(v) < 1e-8:
62
- del nc[k]
63
- 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
+
64
160
  return c
65
161
 
66
162
 
67
163
  def kl_divergence(c1: ct, c2: ct) -> float:
164
+ """
165
+ Compute the Kullback-Leibler divergence between two count distributions.
166
+
167
+ :param c1: First count dictionary
168
+ :type c1: ct
169
+ :param c2: Second count dictionary
170
+ :type c2: ct
171
+ :return: KL divergence value
172
+ :rtype: float
173
+ """
68
174
  eps = 1e-4 # typical value for inverse of the total shots
69
175
  c1 = normalized_count(c1) # type: ignore
70
176
  c2 = normalized_count(c2) # type: ignore
@@ -107,6 +213,27 @@ def expectation(
107
213
  return r / shots
108
214
 
109
215
 
216
+ def merge_count(*counts: ct) -> ct:
217
+ """
218
+ Merge multiple count dictionaries by summing up their counts
219
+
220
+ :param counts: Variable number of count dictionaries
221
+ :type counts: ct
222
+ :return: Merged count dictionary
223
+ :rtype: ct
224
+
225
+ :Example:
226
+
227
+ >>> merge_count({"00": 10, "01": 20}, {"00": 5, "10": 15})
228
+ {'00': 15, '01': 20, '10': 15}
229
+ """
230
+ merged: ct = {}
231
+ for count in counts:
232
+ for k, v in count.items():
233
+ merged[k] = merged.get(k, 0) + v
234
+ return merged
235
+
236
+
110
237
  def plot_histogram(data: Any, **kws: Any) -> Any:
111
238
  """
112
239
  See ``qiskit.visualization.plot_histogram``:
@@ -723,7 +723,10 @@ class ReadoutMit:
723
723
  cals = self._form_cals(qubits)
724
724
  M = M3MatVec(dict(counts), cals, distance)
725
725
  L = spla.LinearOperator(
726
- (M.num_elems, M.num_elems), matvec=M.matvec, rmatvec=M.rmatvec
726
+ (M.num_elems, M.num_elems),
727
+ matvec=M.matvec,
728
+ rmatvec=M.rmatvec,
729
+ dtype=np.float64,
727
730
  )
728
731
  diags = M.get_diagonal()
729
732
 
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