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.
- tensorcircuit/__init__.py +18 -2
- tensorcircuit/about.py +46 -0
- tensorcircuit/abstractcircuit.py +4 -0
- tensorcircuit/analogcircuit.py +413 -0
- tensorcircuit/applications/layers.py +1 -1
- tensorcircuit/applications/van.py +1 -1
- tensorcircuit/backends/abstract_backend.py +320 -7
- tensorcircuit/backends/cupy_backend.py +3 -1
- tensorcircuit/backends/jax_backend.py +102 -4
- tensorcircuit/backends/jax_ops.py +110 -1
- tensorcircuit/backends/numpy_backend.py +49 -3
- tensorcircuit/backends/pytorch_backend.py +92 -3
- tensorcircuit/backends/tensorflow_backend.py +102 -3
- tensorcircuit/basecircuit.py +157 -98
- tensorcircuit/circuit.py +115 -57
- tensorcircuit/cloud/local.py +1 -1
- tensorcircuit/cloud/quafu_provider.py +1 -1
- tensorcircuit/cloud/tencent.py +1 -1
- tensorcircuit/compiler/simple_compiler.py +2 -2
- tensorcircuit/cons.py +142 -21
- tensorcircuit/densitymatrix.py +43 -14
- tensorcircuit/experimental.py +387 -129
- tensorcircuit/fgs.py +282 -81
- tensorcircuit/gates.py +66 -22
- tensorcircuit/interfaces/__init__.py +1 -3
- tensorcircuit/interfaces/jax.py +189 -0
- tensorcircuit/keras.py +3 -3
- tensorcircuit/mpscircuit.py +154 -65
- tensorcircuit/quantum.py +868 -152
- tensorcircuit/quditcircuit.py +733 -0
- tensorcircuit/quditgates.py +618 -0
- tensorcircuit/results/counts.py +147 -20
- tensorcircuit/results/readout_mitigation.py +4 -1
- tensorcircuit/shadows.py +1 -1
- tensorcircuit/simplify.py +3 -1
- tensorcircuit/stabilizercircuit.py +479 -0
- tensorcircuit/templates/__init__.py +2 -0
- tensorcircuit/templates/blocks.py +2 -2
- tensorcircuit/templates/hamiltonians.py +174 -0
- tensorcircuit/templates/lattice.py +1789 -0
- tensorcircuit/timeevol.py +896 -0
- tensorcircuit/translation.py +10 -3
- tensorcircuit/utils.py +7 -0
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/METADATA +73 -23
- tensorcircuit_nightly-1.4.0.dev20251103.dist-info/RECORD +96 -0
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/WHEEL +1 -1
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/top_level.txt +0 -1
- tensorcircuit_nightly-1.0.2.dev20250108.dist-info/RECORD +0 -115
- tests/__init__.py +0 -0
- tests/conftest.py +0 -67
- tests/test_backends.py +0 -1031
- tests/test_calibrating.py +0 -149
- tests/test_channels.py +0 -365
- tests/test_circuit.py +0 -1699
- tests/test_cloud.py +0 -219
- tests/test_compiler.py +0 -147
- tests/test_dmcircuit.py +0 -555
- tests/test_ensemble.py +0 -72
- tests/test_fgs.py +0 -310
- tests/test_gates.py +0 -156
- tests/test_interfaces.py +0 -429
- tests/test_keras.py +0 -160
- tests/test_miscs.py +0 -277
- tests/test_mpscircuit.py +0 -341
- tests/test_noisemodel.py +0 -156
- tests/test_qaoa.py +0 -86
- tests/test_qem.py +0 -152
- tests/test_quantum.py +0 -526
- tests/test_quantum_attr.py +0 -42
- tests/test_results.py +0 -347
- tests/test_shadows.py +0 -160
- tests/test_simplify.py +0 -46
- tests/test_templates.py +0 -218
- tests/test_torchnn.py +0 -99
- tests/test_van.py +0 -102
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Single-qudit and two-qudit gates and their matrix representations.
|
|
3
|
+
|
|
4
|
+
This module implements gates for **qudits** (d-level systems), providing d-dimensional
|
|
5
|
+
analogues of familiar qubit gates as well as qudit-specific primitives.
|
|
6
|
+
|
|
7
|
+
**Registry**
|
|
8
|
+
Single-qudit builders: ``I``, ``X``, ``Z``, ``H``, ``RX``, ``RY``, ``RZ``, ``U8``.
|
|
9
|
+
Two-qudit builders: ``RXX``, ``RZZ``, ``CPHASE``, ``CSUM``.
|
|
10
|
+
|
|
11
|
+
These names are used by higher-level APIs (e.g. :class:`tensorcircuit.quditcircuit.QuditCircuit`).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Any, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
from .cons import backend, dtypestr
|
|
19
|
+
from .gates import num_to_tensor
|
|
20
|
+
|
|
21
|
+
Tensor = Any
|
|
22
|
+
|
|
23
|
+
SINGLE_BUILDERS = {
|
|
24
|
+
"I": (("none",), lambda d, omega, **kw: i_matrix_func(d)),
|
|
25
|
+
"X": (("none",), lambda d, omega, **kw: x_matrix_func(d)),
|
|
26
|
+
"Z": (("none",), lambda d, omega, **kw: z_matrix_func(d, omega)),
|
|
27
|
+
"H": (("none",), lambda d, omega, **kw: h_matrix_func(d, omega)),
|
|
28
|
+
"RX": (
|
|
29
|
+
("theta", "j", "k"),
|
|
30
|
+
lambda d, omega, **kw: rx_matrix_func(d, kw["theta"], kw["j"], kw["k"]),
|
|
31
|
+
),
|
|
32
|
+
"RY": (
|
|
33
|
+
("theta", "j", "k"),
|
|
34
|
+
lambda d, omega, **kw: ry_matrix_func(d, kw["theta"], kw["j"], kw["k"]),
|
|
35
|
+
),
|
|
36
|
+
"RZ": (
|
|
37
|
+
("theta", "j"),
|
|
38
|
+
lambda d, omega, **kw: rz_matrix_func(d, kw["theta"], kw["j"]),
|
|
39
|
+
),
|
|
40
|
+
"U8": (
|
|
41
|
+
("gamma", "z", "eps"),
|
|
42
|
+
lambda d, omega, **kw: u8_matrix_func(
|
|
43
|
+
d, kw["gamma"], kw["z"], kw["eps"], omega
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
TWO_BUILDERS = {
|
|
49
|
+
"RXX": (
|
|
50
|
+
("theta", "j1", "k1", "j2", "k2"),
|
|
51
|
+
lambda d, omega, **kw: rxx_matrix_func(
|
|
52
|
+
d, kw["theta"], kw["j1"], kw["k1"], kw["j2"], kw["k2"]
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
"RZZ": (("theta",), lambda d, omega, **kw: rzz_matrix_func(d, kw["theta"])),
|
|
56
|
+
"CPHASE": (("cv",), lambda d, omega, **kw: cphase_matrix_func(d, kw["cv"], omega)),
|
|
57
|
+
"CSUM": (("cv",), lambda d, omega, **kw: csum_matrix_func(d, kw["cv"])),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _is_prime(n: int) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Check whether a number is prime.
|
|
64
|
+
|
|
65
|
+
:param n: Integer to test.
|
|
66
|
+
:type n: int
|
|
67
|
+
:return: ``True`` if ``n`` is prime, else ``False``.
|
|
68
|
+
:rtype: bool
|
|
69
|
+
"""
|
|
70
|
+
if n < 2:
|
|
71
|
+
return False
|
|
72
|
+
if n in (2, 3, 5, 7):
|
|
73
|
+
return True
|
|
74
|
+
if n % 2 == 0 or n % 3 == 0:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
r = int(n**0.5) + 1
|
|
78
|
+
for i in range(5, r, 6):
|
|
79
|
+
if n % i == 0 or n % (i + 2) == 0:
|
|
80
|
+
return False
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def i_matrix_func(d: int) -> Tensor:
|
|
85
|
+
"""
|
|
86
|
+
Identity matrix of size ``d``.
|
|
87
|
+
|
|
88
|
+
:param d: Qudit dimension.
|
|
89
|
+
:type d: int
|
|
90
|
+
:return: ``(d, d)`` identity matrix.
|
|
91
|
+
:rtype: Tensor
|
|
92
|
+
"""
|
|
93
|
+
return backend.eye(d, dtype=dtypestr)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def x_matrix_func(d: int) -> Tensor:
|
|
97
|
+
r"""
|
|
98
|
+
Generalized Pauli-X on a ``d``-level system.
|
|
99
|
+
|
|
100
|
+
.. math:: X_d\lvert j \rangle = \lvert (j+1) \bmod d \rangle
|
|
101
|
+
|
|
102
|
+
:param d: Qudit dimension.
|
|
103
|
+
:type d: int
|
|
104
|
+
:return: ``(d, d)`` matrix for :math:`X_d`.
|
|
105
|
+
:rtype: Tensor
|
|
106
|
+
"""
|
|
107
|
+
m = np.roll(np.eye(d), shift=1, axis=0)
|
|
108
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def z_matrix_func(d: int, omega: Optional[complex] = None) -> Tensor:
|
|
112
|
+
r"""
|
|
113
|
+
Generalized Pauli-Z on a ``d``-level system.
|
|
114
|
+
|
|
115
|
+
.. math:: Z_d\lvert j \rangle = \omega^{j}\lvert j \rangle,\quad \omega=e^{2\pi i/d}
|
|
116
|
+
|
|
117
|
+
:param d: Qudit dimension.
|
|
118
|
+
:type d: int
|
|
119
|
+
:param omega: Optional primitive ``d``-th root of unity. Defaults to :math:`e^{2\pi i/d}`.
|
|
120
|
+
:type omega: Optional[complex]
|
|
121
|
+
:return: ``(d, d)`` matrix for :math:`Z_d`.
|
|
122
|
+
:rtype: Tensor
|
|
123
|
+
"""
|
|
124
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
125
|
+
m = np.diag(omega ** np.arange(d))
|
|
126
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def h_matrix_func(d: int, omega: Optional[complex] = None) -> Tensor:
|
|
130
|
+
r"""
|
|
131
|
+
Discrete Fourier transform (Hadamard-like) on ``d`` levels.
|
|
132
|
+
|
|
133
|
+
.. math:: H_d\lvert j \rangle = \frac{1}{\sqrt{d}} \sum_{k=0}^{d-1} \omega^{jk}\lvert k \rangle
|
|
134
|
+
|
|
135
|
+
:param d: Qudit dimension.
|
|
136
|
+
:type d: int
|
|
137
|
+
:param omega: Optional primitive ``d``-th root of unity. Defaults to :math:`e^{2\pi i/d}`.
|
|
138
|
+
:type omega: Optional[complex]
|
|
139
|
+
:return: ``(d, d)`` matrix for :math:`H_d`.
|
|
140
|
+
:rtype: Tensor
|
|
141
|
+
"""
|
|
142
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
143
|
+
j, k = np.arange(d), np.arange(d)
|
|
144
|
+
m = omega ** np.outer(j, k) / np.sqrt(d)
|
|
145
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def s_matrix_func(d: int, omega: Optional[complex] = None) -> Tensor:
|
|
149
|
+
r"""
|
|
150
|
+
Diagonal phase gate ``S_d`` on ``d`` levels.
|
|
151
|
+
|
|
152
|
+
.. math:: S_d\lvert j \rangle = \omega^{j(j+p_d)/2}\lvert j \rangle,\quad p_d = (d \bmod 2)
|
|
153
|
+
|
|
154
|
+
:param d: Qudit dimension.
|
|
155
|
+
:type d: int
|
|
156
|
+
:param omega: Optional primitive ``d``-th root of unity. Defaults to :math:`e^{2\pi i/d}`.
|
|
157
|
+
:type omega: Optional[complex]
|
|
158
|
+
:return: ``(d, d)`` diagonal matrix for :math:`S_d`.
|
|
159
|
+
:rtype: Tensor
|
|
160
|
+
"""
|
|
161
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
162
|
+
pd = 0 if d % 2 == 0 else 1
|
|
163
|
+
|
|
164
|
+
j = np.arange(d)
|
|
165
|
+
m = np.diag(omega ** ((j * (j + pd)) / 2))
|
|
166
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _check_rotation_indices(
|
|
170
|
+
d: int, *indices: int, distinct_pairs: bool = False
|
|
171
|
+
) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Validate subspace indices for rotations and interactions.
|
|
174
|
+
|
|
175
|
+
Ensures every index lies in ``[0, d-1]`` and (optionally) that two selected
|
|
176
|
+
basis pairs are distinct.
|
|
177
|
+
|
|
178
|
+
:param d: Qudit dimension.
|
|
179
|
+
:type d: int
|
|
180
|
+
:param indices: Indices to validate (e.g., ``j, k`` or ``j1, k1, j2, k2``).
|
|
181
|
+
:type indices: int
|
|
182
|
+
:param distinct_pairs: If ``True`` and four indices are provided, enforce that
|
|
183
|
+
``(j1, k1)`` and ``(j2, k2)`` do not denote the same basis state.
|
|
184
|
+
:type distinct_pairs: bool
|
|
185
|
+
:raises ValueError: If any index is out of range or if the distinctness constraint fails.
|
|
186
|
+
"""
|
|
187
|
+
for idx in indices:
|
|
188
|
+
if not (0 <= idx < d):
|
|
189
|
+
raise ValueError(f"Index {idx} must satisfy 0 <= index < d (d={d}).")
|
|
190
|
+
|
|
191
|
+
if len(indices) == 2 and indices[0] == indices[1]:
|
|
192
|
+
raise ValueError("Rotation requires two distinct levels: j != k.")
|
|
193
|
+
|
|
194
|
+
if distinct_pairs and len(indices) == 4:
|
|
195
|
+
j1, k1, j2, k2 = indices
|
|
196
|
+
if j1 == k1 and j2 == k2:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
"Selected basis states must be different: (j1, j2) != (k1, k2)."
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _two_level_projectors(
|
|
203
|
+
d: int, j: int, k: Optional[int] = None
|
|
204
|
+
) -> Tuple[Tensor, ...]:
|
|
205
|
+
r"""
|
|
206
|
+
Construct projectors for single- or two-level subspaces in a ``d``-level qudit.
|
|
207
|
+
|
|
208
|
+
:param d: Qudit dimension.
|
|
209
|
+
:type d: int
|
|
210
|
+
:param j: First level index.
|
|
211
|
+
:type j: int
|
|
212
|
+
:param k: Optional second level index. If None, only projectors for ``j`` are returned.
|
|
213
|
+
:type k: Optional[int]
|
|
214
|
+
:return:
|
|
215
|
+
- If ``k is None``: ``(I, Pjj)``
|
|
216
|
+
- Else: ``(I, Pjj, Pkk, Pjk, Pkj)``
|
|
217
|
+
:rtype: Tuple[Tensor, ...]
|
|
218
|
+
"""
|
|
219
|
+
I = backend.eye(d, dtype=dtypestr)
|
|
220
|
+
ej = I[:, j]
|
|
221
|
+
Pjj = backend.outer_product(ej, ej)
|
|
222
|
+
|
|
223
|
+
if k is None:
|
|
224
|
+
return I, Pjj
|
|
225
|
+
|
|
226
|
+
ek = I[:, k]
|
|
227
|
+
Pkk = backend.outer_product(ek, ek)
|
|
228
|
+
Pjk = backend.outer_product(ej, ek)
|
|
229
|
+
Pkj = backend.outer_product(ek, ej)
|
|
230
|
+
return I, Pjj, Pkk, Pjk, Pkj
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def rx_matrix_func(d: int, theta: float, j: int = 0, k: int = 1) -> Tensor:
|
|
234
|
+
r"""
|
|
235
|
+
Rotation-X (``RX``) on a selected two-level subspace of a qudit.
|
|
236
|
+
|
|
237
|
+
Acts like the qubit :math:`RX(\theta)` on levels :math:`j` and :math:`k`, identity elsewhere. On the
|
|
238
|
+
:math:`\{\lvert j\rangle,\lvert k\rangle\}` subspace the matrix equals
|
|
239
|
+
|
|
240
|
+
.. math::
|
|
241
|
+
RX(\theta) = \cos\tfrac{\theta}{2}\,(\lvert j\rangle\!\langle j\rvert+\lvert k\rangle\!\langle k\rvert)
|
|
242
|
+
- i\,\sin\tfrac{\theta}{2}\,(\lvert j\rangle\!\langle k\rvert + \lvert k\rangle\!\langle j\rvert)
|
|
243
|
+
+ I.
|
|
244
|
+
|
|
245
|
+
:param d: Qudit dimension.
|
|
246
|
+
:type d: int
|
|
247
|
+
:param theta: Rotation angle :math:`\theta`.
|
|
248
|
+
:type theta: float
|
|
249
|
+
:param j: First level index.
|
|
250
|
+
:type j: int
|
|
251
|
+
:param k: Second level index.
|
|
252
|
+
:type k: int
|
|
253
|
+
:return: ``(d, d)`` matrix for :math:`RX(\theta)` on the :math:`j,k` subspace.
|
|
254
|
+
:rtype: Tensor
|
|
255
|
+
"""
|
|
256
|
+
_check_rotation_indices(d, j, k)
|
|
257
|
+
I, Pjj, Pkk, Pjk, Pkj = _two_level_projectors(d, j, k)
|
|
258
|
+
theta = num_to_tensor(theta)
|
|
259
|
+
c = backend.cos(theta / 2.0)
|
|
260
|
+
s = backend.sin(theta / 2.0)
|
|
261
|
+
return I + (c - 1.0) * (Pjj + Pkk) + (-1j * s) * (Pjk + Pkj)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def ry_matrix_func(d: int, theta: float, j: int = 0, k: int = 1) -> Tensor:
|
|
265
|
+
r"""
|
|
266
|
+
Rotation-Y (``RY``) on a selected two-level subspace of a qudit.
|
|
267
|
+
|
|
268
|
+
Acts like the qubit :math:`RY(\theta)` on levels :math:`j` and :math:`k`, identity elsewhere. On the
|
|
269
|
+
:math:`\{\lvert j\rangle,\lvert k\rangle\}` subspace the matrix equals
|
|
270
|
+
|
|
271
|
+
.. math::
|
|
272
|
+
RY(\theta) = \cos\tfrac{\theta}{2}\,(\lvert j\rangle\!\langle j\rvert+\lvert k\rangle\!\langle k\rvert)
|
|
273
|
+
+ \sin\tfrac{\theta}{2}\,(\lvert k\rangle\!\langle j\rvert - \lvert j\rangle\!\langle k\rvert)
|
|
274
|
+
+ I.
|
|
275
|
+
|
|
276
|
+
:param d: Qudit dimension.
|
|
277
|
+
:type d: int
|
|
278
|
+
:param theta: Rotation angle :math:`\theta`.
|
|
279
|
+
:type theta: float
|
|
280
|
+
:param j: First level index.
|
|
281
|
+
:type j: int
|
|
282
|
+
:param k: Second level index.
|
|
283
|
+
:type k: int
|
|
284
|
+
:return: ``(d, d)`` matrix for :math:`RY(\theta)` on the :math:`j,k` subspace.
|
|
285
|
+
:rtype: Tensor
|
|
286
|
+
"""
|
|
287
|
+
_check_rotation_indices(d, j, k)
|
|
288
|
+
I, Pjj, Pkk, Pjk, Pkj = _two_level_projectors(d, j, k)
|
|
289
|
+
theta = num_to_tensor(theta)
|
|
290
|
+
c = backend.cos(theta / 2.0)
|
|
291
|
+
s = backend.sin(theta / 2.0)
|
|
292
|
+
return I + (c - 1.0) * (Pjj + Pkk) - s * Pjk + s * Pkj
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def rz_matrix_func(d: int, theta: float, j: int = 0) -> Tensor:
|
|
296
|
+
r"""
|
|
297
|
+
Rotation-Z (``RZ``) on a selected level of a qudit.
|
|
298
|
+
|
|
299
|
+
Acts like the qubit :math:`RZ(\theta)` but applies a phase only to level :math:`j`. On the computational
|
|
300
|
+
basis it equals
|
|
301
|
+
|
|
302
|
+
.. math::
|
|
303
|
+
RZ(\theta) = I + (e^{i\theta}-1)\,\lvert j\rangle\!\langle j\rvert,
|
|
304
|
+
|
|
305
|
+
i.e. :math:`\lvert j\rangle \mapsto e^{i\theta}\,\lvert j\rangle` and all other levels unchanged.
|
|
306
|
+
|
|
307
|
+
:param d: Qudit dimension.
|
|
308
|
+
:type d: int
|
|
309
|
+
:param theta: Rotation angle :math:`\theta`.
|
|
310
|
+
:type theta: float
|
|
311
|
+
:param j: Level index receiving the phase.
|
|
312
|
+
:type j: int
|
|
313
|
+
:return: ``(d, d)`` diagonal matrix implementing :math:`RZ(\theta)` on level :math:`j`.
|
|
314
|
+
:rtype: Tensor
|
|
315
|
+
"""
|
|
316
|
+
I, Pjj = _two_level_projectors(d, j, k=None)
|
|
317
|
+
theta = num_to_tensor(theta)
|
|
318
|
+
phase = backend.exp(1j * theta)
|
|
319
|
+
return I + (phase - 1.0) * Pjj
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def swap_matrix_func(d: int) -> Tensor:
|
|
323
|
+
r"""
|
|
324
|
+
SWAP gate for two qudits of equal dimension :math:`d`.
|
|
325
|
+
|
|
326
|
+
Exchanges basis states :math:`\lvert i\rangle\lvert j\rangle \to \lvert j\rangle\lvert i\rangle`.
|
|
327
|
+
|
|
328
|
+
:param d: Qudit dimension (for each register).
|
|
329
|
+
:type d: int
|
|
330
|
+
:return: ``(d^2, d^2)`` permutation matrix implementing SWAP.
|
|
331
|
+
:rtype: Tensor
|
|
332
|
+
"""
|
|
333
|
+
D = d * d
|
|
334
|
+
I = np.eye(D, dtype=dtypestr)
|
|
335
|
+
m = I.reshape(d, d, d, d).transpose(1, 0, 2, 3).reshape(D, D)
|
|
336
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def rzz_matrix_func(
|
|
340
|
+
d: int, theta: float, j1: int = 0, k1: int = 1, j2: int = 0, k2: int = 1
|
|
341
|
+
) -> Tensor:
|
|
342
|
+
r"""
|
|
343
|
+
Two-qudit ``RZZ(\theta)`` on a selected two-state subspace.
|
|
344
|
+
|
|
345
|
+
Acts like a qubit :math:`RZZ(\theta)=\exp(-i\,\tfrac{\theta}{2}\,\sigma_z)` on the
|
|
346
|
+
two-dimensional subspace spanned by :math:`\lvert j1, j2\rangle` and :math:`\lvert k1, k2\rangle`,
|
|
347
|
+
and as identity elsewhere. The resulting block is diagonal with phases
|
|
348
|
+
:math:`\mathrm{diag}(e^{-i\theta/2},\, e^{+i\theta/2})`.
|
|
349
|
+
|
|
350
|
+
:param d: Dimension of each qudit (assumed equal).
|
|
351
|
+
:type d: int
|
|
352
|
+
:param theta: Rotation angle.
|
|
353
|
+
:type theta: float
|
|
354
|
+
:param j1: Level on qudit-1 for the first basis state.
|
|
355
|
+
:type j1: int
|
|
356
|
+
:param k1: Level on qudit-1 for the second basis state.
|
|
357
|
+
:type k1: int
|
|
358
|
+
:param j2: Level on qudit-2 for the first basis state.
|
|
359
|
+
:type j2: int
|
|
360
|
+
:param k2: Level on qudit-2 for the second basis state.
|
|
361
|
+
:type k2: int
|
|
362
|
+
:return: ``(d*d, d*d)`` matrix representing subspace :math:`RZZ(\theta)`.
|
|
363
|
+
:rtype: Tensor
|
|
364
|
+
:raises ValueError: If indices are out of range or select the same basis state.
|
|
365
|
+
"""
|
|
366
|
+
_check_rotation_indices(d, j1, k1, j2, k2, distinct_pairs=True)
|
|
367
|
+
idx_a = j1 * d + j2
|
|
368
|
+
idx_b = k1 * d + k2
|
|
369
|
+
theta = num_to_tensor(theta)
|
|
370
|
+
phase_minus = backend.exp(-1j * theta / 2.0)
|
|
371
|
+
phase_plus = backend.exp(+1j * theta / 2.0)
|
|
372
|
+
|
|
373
|
+
I = backend.eye(d * d, dtype=dtypestr)
|
|
374
|
+
ea = I[:, idx_a]
|
|
375
|
+
eb = I[:, idx_b]
|
|
376
|
+
Paa = backend.outer_product(ea, ea)
|
|
377
|
+
Pbb = backend.outer_product(eb, eb)
|
|
378
|
+
return I + (phase_minus - 1.0) * Paa + (phase_plus - 1.0) * Pbb
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def rxx_matrix_func(
|
|
382
|
+
d: int, theta: float, j1: int = 0, k1: int = 1, j2: int = 0, k2: int = 1
|
|
383
|
+
) -> Tensor:
|
|
384
|
+
r"""
|
|
385
|
+
Two-qudit ``RXX(\theta)`` on a selected two-state subspace.
|
|
386
|
+
|
|
387
|
+
Acts like a qubit :math:`RXX` on the subspace spanned by
|
|
388
|
+
:math:`\lvert j1, j2\rangle` and :math:`\lvert k1, k2\rangle`.
|
|
389
|
+
|
|
390
|
+
:param d: Dimension of each qudit (assumed equal).
|
|
391
|
+
:type d: int
|
|
392
|
+
:param theta: Rotation angle.
|
|
393
|
+
:type theta: float
|
|
394
|
+
:param j1: Level on qudit-1.
|
|
395
|
+
:type j1: int
|
|
396
|
+
:param k1: Level on qudit-1.
|
|
397
|
+
:type k1: int
|
|
398
|
+
:param j2: Level on qudit-2.
|
|
399
|
+
:type j2: int
|
|
400
|
+
:param k2: Level on qudit-2.
|
|
401
|
+
:type k2: int
|
|
402
|
+
:return: ``(d*d, d*d)`` matrix representing :math:`RXX(\theta)` on the selected subspace.
|
|
403
|
+
:rtype: Tensor
|
|
404
|
+
"""
|
|
405
|
+
_check_rotation_indices(d, j1, k1, j2, k2, distinct_pairs=True)
|
|
406
|
+
idx_a = j1 * d + j2
|
|
407
|
+
idx_b = k1 * d + k2
|
|
408
|
+
theta = num_to_tensor(theta)
|
|
409
|
+
c = backend.cos(theta / 2.0)
|
|
410
|
+
s = backend.sin(theta / 2.0)
|
|
411
|
+
|
|
412
|
+
I = backend.eye(d * d, dtype=dtypestr)
|
|
413
|
+
ea = I[:, idx_a]
|
|
414
|
+
eb = I[:, idx_b]
|
|
415
|
+
Paa = backend.outer_product(ea, ea)
|
|
416
|
+
Pbb = backend.outer_product(eb, eb)
|
|
417
|
+
Pab = backend.outer_product(ea, eb)
|
|
418
|
+
Pba = backend.outer_product(eb, ea)
|
|
419
|
+
return I + (c - 1.0) * (Paa + Pbb) + (-1j * s) * (Pab + Pba)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def u8_matrix_func(
|
|
423
|
+
d: int,
|
|
424
|
+
gamma: int = 2,
|
|
425
|
+
z: int = 1,
|
|
426
|
+
eps: int = 0,
|
|
427
|
+
omega: Optional[complex] = None,
|
|
428
|
+
) -> Tensor:
|
|
429
|
+
r"""
|
|
430
|
+
``U8`` diagonal single-qudit gate for prime dimensions.
|
|
431
|
+
|
|
432
|
+
See ref: Howard, Mark, and Jiri Vala.
|
|
433
|
+
"Qudit versions of the qubit \pi/8 gate." Physical Review A 86, no. 2 (2012): 022316.
|
|
434
|
+
https://doi.org/10.1103/PhysRevA.86.022316
|
|
435
|
+
|
|
436
|
+
This gate is the qudit analogue of the qubit :math:`\pi/8` gate, defined in
|
|
437
|
+
*Howard & Campbell, Phys. Rev. A 86, 022316 (2012)*. It is diagonal in the
|
|
438
|
+
computational basis with exponents determined by modular polynomials in the
|
|
439
|
+
parameters :math:`\gamma, z, \epsilon`. These gates, together with Pauli and
|
|
440
|
+
Clifford operations, generate the full single-qudit Clifford hierarchy.
|
|
441
|
+
|
|
442
|
+
- For :math:`d=2`, this reduces (up to global phase) to the standard qubit
|
|
443
|
+
:math:`\pi/8` gate.
|
|
444
|
+
- For :math:`d=3`, the exponents live in :math:`\mathbb{Z}_9` and the
|
|
445
|
+
primitive ninth root :math:`\zeta = e^{2\pi i/9}` is used.
|
|
446
|
+
- For prime :math:`d>3`, the construction uses the modular inverse of 12 in
|
|
447
|
+
:math:`\mathbb{Z}_d`.
|
|
448
|
+
|
|
449
|
+
:param d: Qudit dimension (must be prime).
|
|
450
|
+
:type d: int
|
|
451
|
+
:param gamma: Shear parameter :math:`\gamma' \in \mathbb{Z}_d`.
|
|
452
|
+
If :math:`gamma = 0`, the gate is a diagonal Clifford.
|
|
453
|
+
If :math:`gamma \neq 0`, the gate is a genuine non-Clifford (analogue of :math:`\pi/8`).
|
|
454
|
+
:type gamma: int
|
|
455
|
+
:param z: Displacement parameter :math:`z' \in \mathbb{Z}_d`,
|
|
456
|
+
which sets the symplectic part of the associated Clifford.
|
|
457
|
+
:type z: int
|
|
458
|
+
:param eps: Phase offset parameter :math:`\epsilon' \in \mathbb{Z}_d`.
|
|
459
|
+
It only contributes a global phase factor :math:`\omega^{\epsilon'}`.
|
|
460
|
+
:type eps: int
|
|
461
|
+
:param omega: Optional primitive :math:`d`-th root of unity (complex).
|
|
462
|
+
Defaults to :math:`e^{2\pi i/d}` for d>3, and :math:`e^{2\pi i/9}` for d=3.
|
|
463
|
+
:type omega: Optional[complex]
|
|
464
|
+
:return: ``(d, d)`` diagonal matrix of dtype ``npdtype``.
|
|
465
|
+
:rtype: Tensor
|
|
466
|
+
:raises ValueError: If ``d`` is not prime; if 12 has no modular inverse
|
|
467
|
+
mod ``d`` (for ``d>3``); or if the computed exponents do not sum to
|
|
468
|
+
0 mod ``d`` (or 0 mod 3 for ``d=3``).
|
|
469
|
+
"""
|
|
470
|
+
if not _is_prime(d):
|
|
471
|
+
raise ValueError(
|
|
472
|
+
f"Dimension d={d} is not prime, U8 gate requires a prime dimension."
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
476
|
+
|
|
477
|
+
gamma = int(gamma) % d
|
|
478
|
+
z = int(z) % d
|
|
479
|
+
eps = int(eps) % d
|
|
480
|
+
|
|
481
|
+
if d == 3:
|
|
482
|
+
vks = [0, (6 * z + 2 * gamma + 3 * eps) % 9, (6 * z + 1 * gamma + 6 * eps) % 9]
|
|
483
|
+
if sum(vks) % 3 != 0:
|
|
484
|
+
raise ValueError(
|
|
485
|
+
f"Sum of v_k's is not 0 mod 3. Got {sum(vks) % 3}. Check parameters."
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
zeta = np.exp(2j * np.pi / 9)
|
|
489
|
+
m = np.diag([zeta**v for v in vks])
|
|
490
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
491
|
+
|
|
492
|
+
try:
|
|
493
|
+
inv_12 = pow(12, -1, d)
|
|
494
|
+
except ValueError:
|
|
495
|
+
raise ValueError(
|
|
496
|
+
f"Inverse of 12 mod {d} does not exist. Choose a prime d that does not divide 12."
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
vks = [0] * d
|
|
500
|
+
for k in range(1, d):
|
|
501
|
+
term_inner = ((6 * z) % d + ((2 * k - 3) % d) * gamma) % d
|
|
502
|
+
term = (gamma + (k * term_inner) % d) % d
|
|
503
|
+
vk = ((inv_12 * (k % d)) % d) * term % d
|
|
504
|
+
vk = (vk + (eps * (k % d)) % d) % d
|
|
505
|
+
vks[k] = vk
|
|
506
|
+
|
|
507
|
+
if sum(vks) % d != 0:
|
|
508
|
+
raise ValueError(
|
|
509
|
+
f"Sum of v_k's is not 0 mod {d}. Got {sum(vks) % d}. Check parameters."
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
513
|
+
m = np.diag([omega**v for v in vks])
|
|
514
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def cphase_matrix_func(
|
|
518
|
+
d: int, cv: Optional[int] = None, omega: Optional[complex] = None
|
|
519
|
+
) -> Tensor:
|
|
520
|
+
r"""
|
|
521
|
+
Qudit controlled-phase (``CPHASE``) gate.
|
|
522
|
+
|
|
523
|
+
Logical definition:
|
|
524
|
+
|
|
525
|
+
.. math::
|
|
526
|
+
|
|
527
|
+
\lvert r \rangle \lvert s \rangle \;\longmapsto\;
|
|
528
|
+
\omega^{rs} \lvert r \rangle \lvert s \rangle
|
|
529
|
+
|
|
530
|
+
Matrix form:
|
|
531
|
+
|
|
532
|
+
.. math::
|
|
533
|
+
|
|
534
|
+
\mathrm{SUMZ}_d =
|
|
535
|
+
\begin{bmatrix}
|
|
536
|
+
I_d & 0 & 0 & \cdots & 0 \\
|
|
537
|
+
0 & Z_d & 0 & \cdots & 0 \\
|
|
538
|
+
0 & 0 & Z_d^2 & \cdots & 0 \\
|
|
539
|
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
|
540
|
+
0 & 0 & 0 & \cdots & Z_d^{d-1}
|
|
541
|
+
\end{bmatrix}
|
|
542
|
+
|
|
543
|
+
:param d: Qudit dimension (for each register).
|
|
544
|
+
:type d: int
|
|
545
|
+
:param cv: Optional control value in ``[0, d-1]``. If ``None``, builds the full SUMZ block-diagonal.
|
|
546
|
+
:type cv: Optional[int]
|
|
547
|
+
:param omega: Optional primitive ``d``-th root of unity for ``Z_d``.
|
|
548
|
+
:type omega: Optional[complex]
|
|
549
|
+
:return: ``(d*d, d*d)`` matrix representing the controlled-phase.
|
|
550
|
+
:rtype: Tensor
|
|
551
|
+
:raises ValueError: If ``cv`` is provided and is outside ``[0, d-1]``.
|
|
552
|
+
"""
|
|
553
|
+
omega = np.exp(2j * np.pi / d) if omega is None else omega
|
|
554
|
+
r = np.arange(d).reshape(-1, 1)
|
|
555
|
+
s = np.arange(d).reshape(1, -1)
|
|
556
|
+
|
|
557
|
+
if cv is None:
|
|
558
|
+
phase = omega ** (r * s)
|
|
559
|
+
else:
|
|
560
|
+
if not (0 <= cv < d):
|
|
561
|
+
raise ValueError(f"cv must be in [0, {d - 1}], got {cv}")
|
|
562
|
+
phase = 1 + (r == cv) * (omega**s - 1)
|
|
563
|
+
|
|
564
|
+
diag = np.ravel(phase)
|
|
565
|
+
m = np.diag(diag)
|
|
566
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def csum_matrix_func(d: int, cv: Optional[int] = None) -> Tensor:
|
|
570
|
+
r"""
|
|
571
|
+
Qudit controlled-sum (``CSUM`` / ``SUMX``) gate.
|
|
572
|
+
|
|
573
|
+
Logical definition:
|
|
574
|
+
|
|
575
|
+
.. math::
|
|
576
|
+
|
|
577
|
+
\lvert r \rangle \lvert s \rangle \;\longmapsto\;
|
|
578
|
+
\lvert r \rangle \lvert r+s \pmod d \rangle
|
|
579
|
+
|
|
580
|
+
Matrix form:
|
|
581
|
+
|
|
582
|
+
.. math::
|
|
583
|
+
|
|
584
|
+
\mathrm{SUMX}_d =
|
|
585
|
+
\begin{bmatrix}
|
|
586
|
+
I_d & 0 & 0 & \cdots & 0 \\
|
|
587
|
+
0 & X_d & 0 & \cdots & 0 \\
|
|
588
|
+
0 & 0 & X_d^2 & \cdots & 0 \\
|
|
589
|
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
|
590
|
+
0 & 0 & 0 & \cdots & X_d^{d-1}
|
|
591
|
+
\end{bmatrix}
|
|
592
|
+
|
|
593
|
+
:param d: Qudit dimension (for each register).
|
|
594
|
+
:type d: int
|
|
595
|
+
:param cv: Optional control value in ``[0, d-1]``. If ``None``, builds the full SUMX block-diagonal.
|
|
596
|
+
:type cv: Optional[int]
|
|
597
|
+
:return: ``(d*d, d*d)`` matrix representing the controlled-sum.
|
|
598
|
+
:rtype: Tensor
|
|
599
|
+
:raises ValueError: If ``cv`` is provided and is outside ``[0, d-1]``.
|
|
600
|
+
"""
|
|
601
|
+
I = np.eye(d)
|
|
602
|
+
|
|
603
|
+
if cv is None:
|
|
604
|
+
blocks = [np.roll(I, shift=r, axis=0) for r in range(d)]
|
|
605
|
+
m = np.block(
|
|
606
|
+
[
|
|
607
|
+
[blocks[r] if r == c else np.zeros((d, d)) for c in range(d)]
|
|
608
|
+
for r in range(d)
|
|
609
|
+
]
|
|
610
|
+
)
|
|
611
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|
|
612
|
+
|
|
613
|
+
if not (0 <= cv < d):
|
|
614
|
+
raise ValueError(f"cv must be in [0, {d - 1}], got {cv}")
|
|
615
|
+
|
|
616
|
+
X = np.roll(I, shift=1, axis=0)
|
|
617
|
+
m = np.kron(I, I) + np.kron(np.outer(I[:, cv], I[:, cv]), (X - I))
|
|
618
|
+
return backend.cast(backend.convert_to_tensor(m), dtype=dtypestr)
|