tensorcircuit-nightly 1.2.1.dev20250724__py3-none-any.whl → 1.2.1.dev20250726__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 +1 -1
- tensorcircuit/backends/jax_backend.py +8 -1
- tensorcircuit/circuit.py +48 -0
- tensorcircuit/cons.py +60 -2
- tensorcircuit/fgs.py +251 -70
- tensorcircuit/mpscircuit.py +45 -4
- tensorcircuit/stabilizercircuit.py +26 -15
- tensorcircuit/translation.py +10 -2
- {tensorcircuit_nightly-1.2.1.dev20250724.dist-info → tensorcircuit_nightly-1.2.1.dev20250726.dist-info}/METADATA +1 -1
- {tensorcircuit_nightly-1.2.1.dev20250724.dist-info → tensorcircuit_nightly-1.2.1.dev20250726.dist-info}/RECORD +17 -17
- tests/test_circuit.py +20 -6
- tests/test_dmcircuit.py +1 -1
- tests/test_miscs.py +20 -0
- tests/test_stabilizer.py +9 -0
- {tensorcircuit_nightly-1.2.1.dev20250724.dist-info → tensorcircuit_nightly-1.2.1.dev20250726.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.2.1.dev20250724.dist-info → tensorcircuit_nightly-1.2.1.dev20250726.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.2.1.dev20250724.dist-info → tensorcircuit_nightly-1.2.1.dev20250726.dist-info}/top_level.txt +0 -0
tensorcircuit/__init__.py
CHANGED
|
@@ -442,7 +442,14 @@ class JaxBackend(jax_backend.JaxBackend, ExtendedBackend): # type: ignore
|
|
|
442
442
|
def to_dlpack(self, a: Tensor) -> Any:
|
|
443
443
|
import jax.dlpack
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
try:
|
|
446
|
+
return jax.dlpack.to_dlpack(a) # type: ignore
|
|
447
|
+
except AttributeError: # jax >v0.7
|
|
448
|
+
# jax.dlpack.to_dlpack was deprecated in JAX v0.6.0 and removed in JAX v0.7.0.
|
|
449
|
+
# Please use the newer DLPack API based on __dlpack__ and __dlpack_device__ instead.
|
|
450
|
+
# Typically, you can pass a JAX array directly to the `from_dlpack` function of
|
|
451
|
+
# another framework without using `to_dlpack`.
|
|
452
|
+
return a.__dlpack__()
|
|
446
453
|
|
|
447
454
|
def set_random_state(
|
|
448
455
|
self, seed: Optional[Union[int, PRNGKeyArray]] = None, get_only: bool = False
|
tensorcircuit/circuit.py
CHANGED
|
@@ -231,6 +231,25 @@ class Circuit(BaseCircuit):
|
|
|
231
231
|
pz: float,
|
|
232
232
|
status: Optional[float] = None,
|
|
233
233
|
) -> float:
|
|
234
|
+
"""
|
|
235
|
+
Apply a depolarizing channel to the circuit in a Monte Carlo way.
|
|
236
|
+
For each call, one of the Pauli gates (X, Y, Z) or an Identity gate is applied to the qubit
|
|
237
|
+
at the given index based on the probabilities `px`, `py`, and `pz`.
|
|
238
|
+
|
|
239
|
+
:param index: The index of the qubit to apply the depolarizing channel on.
|
|
240
|
+
:type index: int
|
|
241
|
+
:param px: The probability of applying an X gate.
|
|
242
|
+
:type px: float
|
|
243
|
+
:param py: The probability of applying a Y gate.
|
|
244
|
+
:type py: float
|
|
245
|
+
:param pz: The probability of applying a Z gate.
|
|
246
|
+
:type pz: float
|
|
247
|
+
:param status: A random number between 0 and 1 to determine which gate to apply. If None,
|
|
248
|
+
a random number is generated automatically. Defaults to None.
|
|
249
|
+
:type status: Optional[float], optional
|
|
250
|
+
:return: Returns 0.0. The function modifies the circuit in place.
|
|
251
|
+
:rtype: float
|
|
252
|
+
"""
|
|
234
253
|
if status is None:
|
|
235
254
|
status = backend.implicit_randu()[0]
|
|
236
255
|
g = backend.cond(
|
|
@@ -323,6 +342,35 @@ class Circuit(BaseCircuit):
|
|
|
323
342
|
status: Optional[float] = None,
|
|
324
343
|
name: Optional[str] = None,
|
|
325
344
|
) -> Tensor:
|
|
345
|
+
"""
|
|
346
|
+
Apply a unitary Kraus channel to the circuit using a Monte Carlo approach. This method is functionally
|
|
347
|
+
similar to `unitary_kraus` but uses `backend.switch` for selecting the Kraus operator, which can have
|
|
348
|
+
different performance characteristics on some backends.
|
|
349
|
+
|
|
350
|
+
A random Kraus operator from the provided list is applied to the circuit based on the given probabilities.
|
|
351
|
+
This method is jittable and suitable for simulating noisy quantum circuits where the noise is represented
|
|
352
|
+
by unitary Kraus operators.
|
|
353
|
+
|
|
354
|
+
.. warning::
|
|
355
|
+
This method may have issues with `vmap` due to potential concurrent access locks, potentially related with
|
|
356
|
+
`backend.switch`. `unitary_kraus` is generally recommended.
|
|
357
|
+
|
|
358
|
+
:param kraus: A sequence of `Gate` objects representing the unitary Kraus operators.
|
|
359
|
+
:type kraus: Sequence[Gate]
|
|
360
|
+
:param index: The qubit indices on which to apply the Kraus channel.
|
|
361
|
+
:type index: int
|
|
362
|
+
:param prob: A sequence of probabilities corresponding to each Kraus operator. If None, probabilities
|
|
363
|
+
are derived from the operators themselves. Defaults to None.
|
|
364
|
+
:type prob: Optional[Sequence[float]], optional
|
|
365
|
+
:param status: A random number between 0 and 1 to determine which Kraus operator to apply. If None,
|
|
366
|
+
a random number is generated automatically. Defaults to None.
|
|
367
|
+
:type status: Optional[float], optional
|
|
368
|
+
:param name: An optional name for the operation. Defaults to None.
|
|
369
|
+
:type name: Optional[str], optional
|
|
370
|
+
:return: A tensor indicating which Kraus operator was applied.
|
|
371
|
+
:rtype: Tensor
|
|
372
|
+
"""
|
|
373
|
+
|
|
326
374
|
# dont use, has issue conflicting with vmap, concurrent access lock emerged
|
|
327
375
|
# potential issue raised from switch
|
|
328
376
|
# general impl from Monte Carlo trajectory depolarizing above
|
tensorcircuit/cons.py
CHANGED
|
@@ -516,8 +516,8 @@ def _get_path_cache_friendly(
|
|
|
516
516
|
nodes = list(nodes)
|
|
517
517
|
|
|
518
518
|
nodes_new = sorted(nodes, key=lambda node: getattr(node, "_stable_id_", -1))
|
|
519
|
-
if isinstance(algorithm, list):
|
|
520
|
-
|
|
519
|
+
# if isinstance(algorithm, list):
|
|
520
|
+
# return algorithm, [nodes_new]
|
|
521
521
|
|
|
522
522
|
all_edges = tn.get_all_edges(nodes_new)
|
|
523
523
|
all_edges_sorted = sorted_edges(all_edges)
|
|
@@ -693,6 +693,51 @@ def _base(
|
|
|
693
693
|
return final_node
|
|
694
694
|
|
|
695
695
|
|
|
696
|
+
class NodesReturn(Exception):
|
|
697
|
+
"""
|
|
698
|
+
Intentionally stop execution to return a value.
|
|
699
|
+
"""
|
|
700
|
+
|
|
701
|
+
def __init__(self, value_to_return: Any):
|
|
702
|
+
self.value = value_to_return
|
|
703
|
+
super().__init__(
|
|
704
|
+
f"Intentionally stopping execution to return: {value_to_return}"
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def _get_sorted_nodes(nodes: List[Any], *args: Any, **kws: Any) -> Any:
|
|
709
|
+
nodes_new = sorted(nodes, key=lambda node: getattr(node, "_stable_id_", -1))
|
|
710
|
+
raise NodesReturn(nodes_new)
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def function_nodes_capture(func: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
|
714
|
+
@wraps(func)
|
|
715
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
716
|
+
with runtime_contractor(method="before"):
|
|
717
|
+
try:
|
|
718
|
+
result = func(*args, **kwargs)
|
|
719
|
+
return result
|
|
720
|
+
except NodesReturn as e:
|
|
721
|
+
return e.value
|
|
722
|
+
|
|
723
|
+
return wrapper
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
@contextmanager
|
|
727
|
+
def runtime_nodes_capture(key: str = "nodes") -> Iterator[Any]:
|
|
728
|
+
old_contractor = getattr(thismodule, "contractor")
|
|
729
|
+
set_contractor(method="before")
|
|
730
|
+
captured_value: Dict[str, List[tn.Node]] = {}
|
|
731
|
+
try:
|
|
732
|
+
yield captured_value
|
|
733
|
+
except NodesReturn as e:
|
|
734
|
+
captured_value[key] = e.value
|
|
735
|
+
finally:
|
|
736
|
+
for module in sys.modules:
|
|
737
|
+
if module.startswith(package_name):
|
|
738
|
+
setattr(sys.modules[module], "contractor", old_contractor)
|
|
739
|
+
|
|
740
|
+
|
|
696
741
|
def custom(
|
|
697
742
|
nodes: List[Any],
|
|
698
743
|
optimizer: Any,
|
|
@@ -763,6 +808,16 @@ def custom_stateful(
|
|
|
763
808
|
|
|
764
809
|
# only work for custom
|
|
765
810
|
def contraction_info_decorator(algorithm: Callable[..., Any]) -> Callable[..., Any]:
|
|
811
|
+
"""Decorator to add contraction information logging to an optimizer.
|
|
812
|
+
|
|
813
|
+
This decorator wraps an optimization algorithm and prints detailed information
|
|
814
|
+
about the contraction cost (FLOPs, size, write) and path finding time.
|
|
815
|
+
|
|
816
|
+
:param algorithm: The optimization algorithm to decorate.
|
|
817
|
+
:type algorithm: Callable[..., Any]
|
|
818
|
+
:return: The decorated optimization algorithm.
|
|
819
|
+
:rtype: Callable[..., Any]
|
|
820
|
+
"""
|
|
766
821
|
from cotengra import ContractionTree
|
|
767
822
|
|
|
768
823
|
def new_algorithm(
|
|
@@ -869,6 +924,9 @@ def set_contractor(
|
|
|
869
924
|
**kws,
|
|
870
925
|
)
|
|
871
926
|
|
|
927
|
+
elif method == "before": # a hack way to get the nodes
|
|
928
|
+
cf = _get_sorted_nodes
|
|
929
|
+
|
|
872
930
|
else:
|
|
873
931
|
# cf = getattr(tn.contractors, method, None)
|
|
874
932
|
# if not cf:
|
tensorcircuit/fgs.py
CHANGED
|
@@ -73,17 +73,26 @@ class FGSSimulator:
|
|
|
73
73
|
cmatrix: Optional[Tensor] = None,
|
|
74
74
|
):
|
|
75
75
|
"""
|
|
76
|
-
|
|
76
|
+
Initializes the Fermion Gaussian State (FGS) simulator.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
The state can be initialized in one of four ways:
|
|
79
|
+
1. By specifying the system size `L` and a list of `filled` sites, creating a product state.
|
|
80
|
+
2. By providing a quadratic Hamiltonian `hc`, the ground state of which is used as the initial state.
|
|
81
|
+
3. By directly providing the `alpha` matrix, which defines the Bogoliubov transformation.
|
|
82
|
+
4. For debugging, by providing a pre-computed correlation matrix `cmatrix`.
|
|
83
|
+
|
|
84
|
+
:param L: The number of fermionic sites (system size).
|
|
79
85
|
:type L: int
|
|
80
|
-
:param filled:
|
|
86
|
+
:param filled: A list of site indices that are occupied by fermions in the initial state.
|
|
87
|
+
Defaults to None (no sites filled).
|
|
81
88
|
:type filled: Optional[List[int]], optional
|
|
82
|
-
:param alpha:
|
|
89
|
+
:param alpha: The matrix defining the Bogoliubov transformation from the vacuum state, of shape `(2L, L)`.
|
|
90
|
+
If provided, it directly specifies the initial state. Defaults to None.
|
|
83
91
|
:type alpha: Optional[Tensor], optional
|
|
84
|
-
:param hc:
|
|
92
|
+
:param hc: A quadratic Hamiltonian. The ground state of this Hamiltonian will be used as the initial state.
|
|
93
|
+
Defaults to None.
|
|
85
94
|
:type hc: Optional[Tensor], optional
|
|
86
|
-
:param cmatrix:
|
|
95
|
+
:param cmatrix: A pre-computed correlation matrix, primarily for debugging purposes. Defaults to None.
|
|
87
96
|
:type cmatrix: Optional[Tensor], optional
|
|
88
97
|
"""
|
|
89
98
|
if filled is None:
|
|
@@ -105,6 +114,19 @@ class FGSSimulator:
|
|
|
105
114
|
def fermion_diagonalization(
|
|
106
115
|
cls, hc: Tensor, L: int
|
|
107
116
|
) -> Tuple[Tensor, Tensor, Tensor]:
|
|
117
|
+
"""
|
|
118
|
+
Diagonalizes a quadratic fermionic Hamiltonian.
|
|
119
|
+
|
|
120
|
+
This method computes the eigenvalues, eigenvectors, and the alpha matrix for a given
|
|
121
|
+
quadratic Hamiltonian `hc`.
|
|
122
|
+
|
|
123
|
+
:param hc: The quadratic Hamiltonian to be diagonalized.
|
|
124
|
+
:type hc: Tensor
|
|
125
|
+
:param L: The number of fermionic sites.
|
|
126
|
+
:type L: int
|
|
127
|
+
:return: A tuple containing the eigenvalues, eigenvectors, and the alpha matrix.
|
|
128
|
+
:rtype: Tuple[Tensor, Tensor, Tensor]
|
|
129
|
+
"""
|
|
108
130
|
es, u = backend.eigh(hc)
|
|
109
131
|
es = es[::-1]
|
|
110
132
|
u = u[:, ::-1]
|
|
@@ -115,6 +137,19 @@ class FGSSimulator:
|
|
|
115
137
|
def fermion_diagonalization_2(
|
|
116
138
|
cls, hc: Tensor, L: int
|
|
117
139
|
) -> Tuple[Tensor, Tensor, Tensor]:
|
|
140
|
+
"""
|
|
141
|
+
Alternative method for diagonalizing a quadratic fermionic Hamiltonian.
|
|
142
|
+
|
|
143
|
+
This method uses a different approach based on the Schur decomposition to diagonalize
|
|
144
|
+
the Hamiltonian.
|
|
145
|
+
|
|
146
|
+
:param hc: The quadratic Hamiltonian to be diagonalized.
|
|
147
|
+
:type hc: Tensor
|
|
148
|
+
:param L: The number of fermionic sites.
|
|
149
|
+
:type L: int
|
|
150
|
+
:return: A tuple containing the eigenvalues, eigenvectors, and the alpha matrix.
|
|
151
|
+
:rtype: Tuple[Tensor, Tensor, Tensor]
|
|
152
|
+
"""
|
|
118
153
|
w = cls.wmatrix(L)
|
|
119
154
|
hm = 0.25 * w @ hc @ backend.adjoint(w)
|
|
120
155
|
hm = backend.real((-1.0j) * hm)
|
|
@@ -140,6 +175,16 @@ class FGSSimulator:
|
|
|
140
175
|
|
|
141
176
|
@staticmethod
|
|
142
177
|
def wmatrix(L: int) -> Tensor:
|
|
178
|
+
"""
|
|
179
|
+
Constructs the transformation matrix W.
|
|
180
|
+
|
|
181
|
+
This matrix transforms from the fermionic basis to the Majorana basis.
|
|
182
|
+
|
|
183
|
+
:param L: The number of fermionic sites.
|
|
184
|
+
:type L: int
|
|
185
|
+
:return: The transformation matrix W.
|
|
186
|
+
:rtype: Tensor
|
|
187
|
+
"""
|
|
143
188
|
w = np.zeros([2 * L, 2 * L], dtype=complex)
|
|
144
189
|
for i in range(2 * L):
|
|
145
190
|
if i % 2 == 1:
|
|
@@ -152,6 +197,16 @@ class FGSSimulator:
|
|
|
152
197
|
|
|
153
198
|
@staticmethod
|
|
154
199
|
def init_alpha(filled: List[int], L: int) -> Tensor:
|
|
200
|
+
"""
|
|
201
|
+
Initializes the alpha matrix for a given set of filled sites.
|
|
202
|
+
|
|
203
|
+
:param filled: A list of site indices that are occupied by fermions.
|
|
204
|
+
:type filled: List[int]
|
|
205
|
+
:param L: The number of fermionic sites.
|
|
206
|
+
:type L: int
|
|
207
|
+
:return: The initialized alpha matrix.
|
|
208
|
+
:rtype: Tensor
|
|
209
|
+
"""
|
|
155
210
|
alpha = np.zeros([2 * L, L])
|
|
156
211
|
for i in range(L):
|
|
157
212
|
if i not in filled:
|
|
@@ -163,9 +218,29 @@ class FGSSimulator:
|
|
|
163
218
|
return alpha
|
|
164
219
|
|
|
165
220
|
def get_alpha(self) -> Tensor:
|
|
221
|
+
"""
|
|
222
|
+
Returns the current alpha matrix of the FGS.
|
|
223
|
+
|
|
224
|
+
:return: The alpha matrix.
|
|
225
|
+
:rtype: Tensor
|
|
226
|
+
"""
|
|
166
227
|
return self.alpha
|
|
167
228
|
|
|
168
229
|
def get_cmatrix(self, now_i: bool = True, now_j: bool = True) -> Tensor:
|
|
230
|
+
"""
|
|
231
|
+
Calculates the correlation matrix.
|
|
232
|
+
|
|
233
|
+
The correlation matrix is defined as :math:`C_{ij} = \langle c_i^\dagger c_j \rangle`.
|
|
234
|
+
This method can also compute out-of-time-ordered correlators (OTOC) by specifying
|
|
235
|
+
whether to use the current or initial state for the `i` and `j` indices.
|
|
236
|
+
|
|
237
|
+
:param now_i: If True, use the current state for the `i` index. Defaults to True.
|
|
238
|
+
:type now_i: bool, optional
|
|
239
|
+
:param now_j: If True, use the current state for the `j` index. Defaults to True.
|
|
240
|
+
:type now_j: bool, optional
|
|
241
|
+
:return: The correlation matrix.
|
|
242
|
+
:rtype: Tensor
|
|
243
|
+
"""
|
|
169
244
|
# otoc in FGS language, see: https://arxiv.org/pdf/1908.03292.pdf
|
|
170
245
|
# https://journals.aps.org/prb/pdf/10.1103/PhysRevB.99.054205
|
|
171
246
|
# otoc for non=hermitian system is more subtle due to the undefined normalization
|
|
@@ -197,11 +272,11 @@ class FGSSimulator:
|
|
|
197
272
|
|
|
198
273
|
def get_reduced_cmatrix(self, subsystems_to_trace_out: List[int]) -> Tensor:
|
|
199
274
|
"""
|
|
200
|
-
|
|
275
|
+
Calculates the reduced correlation matrix by tracing out specified subsystems.
|
|
201
276
|
|
|
202
|
-
:param subsystems_to_trace_out: list of
|
|
277
|
+
:param subsystems_to_trace_out: A list of site indices to be traced out.
|
|
203
278
|
:type subsystems_to_trace_out: List[int]
|
|
204
|
-
:return: reduced
|
|
279
|
+
:return: The reduced correlation matrix.
|
|
205
280
|
:rtype: Tensor
|
|
206
281
|
"""
|
|
207
282
|
m = self.get_cmatrix()
|
|
@@ -222,13 +297,13 @@ class FGSSimulator:
|
|
|
222
297
|
|
|
223
298
|
def renyi_entropy(self, n: int, subsystems_to_trace_out: List[int]) -> Tensor:
|
|
224
299
|
"""
|
|
225
|
-
|
|
300
|
+
Computes the Renyi entropy of order n for a given subsystem.
|
|
226
301
|
|
|
227
|
-
:param n:
|
|
302
|
+
:param n: The order of the Renyi entropy.
|
|
228
303
|
:type n: int
|
|
229
|
-
:param subsystems_to_trace_out:
|
|
304
|
+
:param subsystems_to_trace_out: A list of site indices to be traced out, defining the subsystem.
|
|
230
305
|
:type subsystems_to_trace_out: List[int]
|
|
231
|
-
:return:
|
|
306
|
+
:return: The Renyi entropy of order n.
|
|
232
307
|
:rtype: Tensor
|
|
233
308
|
"""
|
|
234
309
|
m = self.get_reduced_cmatrix(subsystems_to_trace_out)
|
|
@@ -248,15 +323,17 @@ class FGSSimulator:
|
|
|
248
323
|
subsystems_to_trace_out: List[int],
|
|
249
324
|
) -> Tensor:
|
|
250
325
|
"""
|
|
326
|
+
Computes the charge moment of order n.
|
|
327
|
+
|
|
251
328
|
Ref: https://arxiv.org/abs/2302.03330
|
|
252
329
|
|
|
253
|
-
:param alpha:
|
|
330
|
+
:param alpha: The alpha parameter for the charge moment calculation.
|
|
254
331
|
:type alpha: Tensor
|
|
255
|
-
:param n:
|
|
332
|
+
:param n: The order of the charge moment (Renyi-n).
|
|
256
333
|
:type n: int
|
|
257
|
-
:param subsystems_to_trace_out:
|
|
334
|
+
:param subsystems_to_trace_out: A list of site indices to be traced out.
|
|
258
335
|
:type subsystems_to_trace_out: List[int]
|
|
259
|
-
:return:
|
|
336
|
+
:return: The charge moment.
|
|
260
337
|
:rtype: Tensor
|
|
261
338
|
"""
|
|
262
339
|
m = self.get_reduced_cmatrix(subsystems_to_trace_out)
|
|
@@ -297,18 +374,23 @@ class FGSSimulator:
|
|
|
297
374
|
with_std: bool = False,
|
|
298
375
|
) -> Tensor:
|
|
299
376
|
"""
|
|
377
|
+
Computes the Renyi entanglement asymmetry.
|
|
378
|
+
|
|
300
379
|
Ref: https://arxiv.org/abs/2302.03330
|
|
301
380
|
|
|
302
|
-
:param n:
|
|
381
|
+
:param n: The order of the Renyi entanglement asymmetry.
|
|
303
382
|
:type n: int
|
|
304
|
-
:param subsystems_to_trace_out:
|
|
383
|
+
:param subsystems_to_trace_out: A list of site indices to be traced out.
|
|
305
384
|
:type subsystems_to_trace_out: List[int]
|
|
306
|
-
:param batch:
|
|
307
|
-
:type batch: int
|
|
308
|
-
:param status: random
|
|
309
|
-
|
|
310
|
-
:type status: Optional[Tensor]
|
|
311
|
-
:
|
|
385
|
+
:param batch: The number of samples to use for the Monte Carlo estimation. Defaults to 100.
|
|
386
|
+
:type batch: int, optional
|
|
387
|
+
:param status: A tensor of random numbers for the sampling. If None, it will be generated internally.
|
|
388
|
+
Defaults to None.
|
|
389
|
+
:type status: Optional[Tensor], optional
|
|
390
|
+
:param with_std: If True, also return the standard deviation of the estimation. Defaults to False.
|
|
391
|
+
:type with_std: bool, optional
|
|
392
|
+
:return: The Renyi entanglement asymmetry.
|
|
393
|
+
If `with_std` is True, a tuple containing the asymmetry and its standard deviation is returned.
|
|
312
394
|
:rtype: Tensor
|
|
313
395
|
"""
|
|
314
396
|
r = []
|
|
@@ -354,11 +436,12 @@ class FGSSimulator:
|
|
|
354
436
|
|
|
355
437
|
def entropy(self, subsystems_to_trace_out: Optional[List[int]] = None) -> Tensor:
|
|
356
438
|
"""
|
|
357
|
-
|
|
439
|
+
Computes the von Neumann entropy of a subsystem.
|
|
358
440
|
|
|
359
|
-
:param subsystems_to_trace_out:
|
|
441
|
+
:param subsystems_to_trace_out: A list of site indices to be traced out, defining the subsystem.
|
|
442
|
+
If None, the entropy of the entire system is computed. Defaults to None.
|
|
360
443
|
:type subsystems_to_trace_out: Optional[List[int]], optional
|
|
361
|
-
:return:
|
|
444
|
+
:return: The von Neumann entropy.
|
|
362
445
|
:rtype: Tensor
|
|
363
446
|
"""
|
|
364
447
|
m = self.get_reduced_cmatrix(subsystems_to_trace_out) # type: ignore
|
|
@@ -374,9 +457,11 @@ class FGSSimulator:
|
|
|
374
457
|
|
|
375
458
|
def evol_hamiltonian(self, h: Tensor) -> None:
|
|
376
459
|
r"""
|
|
377
|
-
|
|
460
|
+
Evolves the state with a given Hamiltonian.
|
|
461
|
+
|
|
462
|
+
The evolution is given by :math:`e^{-i/2 \hat{h}}`.
|
|
378
463
|
|
|
379
|
-
:param h:
|
|
464
|
+
:param h: The Hamiltonian for the evolution.
|
|
380
465
|
:type h: Tensor
|
|
381
466
|
"""
|
|
382
467
|
# e^{-i/2 H}
|
|
@@ -387,9 +472,11 @@ class FGSSimulator:
|
|
|
387
472
|
|
|
388
473
|
def evol_ihamiltonian(self, h: Tensor) -> None:
|
|
389
474
|
r"""
|
|
390
|
-
|
|
475
|
+
Evolves the state with a given Hamiltonian using imaginary time evolution.
|
|
391
476
|
|
|
392
|
-
:
|
|
477
|
+
The evolution is given by :math:`e^{-1/2 \hat{h}}`.
|
|
478
|
+
|
|
479
|
+
:param h: The Hamiltonian for the evolution.
|
|
393
480
|
:type h: Tensor
|
|
394
481
|
"""
|
|
395
482
|
# e^{-1/2 H}
|
|
@@ -401,9 +488,11 @@ class FGSSimulator:
|
|
|
401
488
|
|
|
402
489
|
def evol_ghamiltonian(self, h: Tensor) -> None:
|
|
403
490
|
r"""
|
|
404
|
-
|
|
491
|
+
Evolves the state with a general non-Hermitian Hamiltonian.
|
|
492
|
+
|
|
493
|
+
The evolution is given by :math:`e^{-1/2 i \hat{h}}`.
|
|
405
494
|
|
|
406
|
-
:param h:
|
|
495
|
+
:param h: The non-Hermitian Hamiltonian for the evolution.
|
|
407
496
|
:type h: Tensor
|
|
408
497
|
"""
|
|
409
498
|
# e^{-1/2 H}
|
|
@@ -414,11 +503,28 @@ class FGSSimulator:
|
|
|
414
503
|
self.otcmatrix = {}
|
|
415
504
|
|
|
416
505
|
def orthogonal(self) -> None:
|
|
506
|
+
"""Orthogonalizes the alpha matrix using QR decomposition."""
|
|
417
507
|
q, _ = backend.qr(self.alpha)
|
|
418
508
|
self.alpha = q
|
|
419
509
|
|
|
420
510
|
@staticmethod
|
|
421
511
|
def hopping(chi: Tensor, i: int, j: int, L: int) -> Tensor:
|
|
512
|
+
"""
|
|
513
|
+
Constructs the hopping Hamiltonian between two sites.
|
|
514
|
+
|
|
515
|
+
The hopping Hamiltonian is given by :math:`\chi c_i^\dagger c_j + h.c.`.
|
|
516
|
+
|
|
517
|
+
:param chi: The hopping strength.
|
|
518
|
+
:type chi: Tensor
|
|
519
|
+
:param i: The index of the first site.
|
|
520
|
+
:type i: int
|
|
521
|
+
:param j: The index of the second site.
|
|
522
|
+
:type j: int
|
|
523
|
+
:param L: The number of fermionic sites.
|
|
524
|
+
:type L: int
|
|
525
|
+
:return: The hopping Hamiltonian.
|
|
526
|
+
:rtype: Tensor
|
|
527
|
+
"""
|
|
422
528
|
# chi * ci dagger cj + hc.
|
|
423
529
|
chi = backend.convert_to_tensor(chi)
|
|
424
530
|
chi = backend.cast(chi, dtypestr)
|
|
@@ -429,19 +535,35 @@ class FGSSimulator:
|
|
|
429
535
|
|
|
430
536
|
def evol_hp(self, i: int, j: int, chi: Tensor = 0) -> None:
|
|
431
537
|
r"""
|
|
432
|
-
|
|
538
|
+
Evolves the state with a hopping Hamiltonian.
|
|
539
|
+
|
|
540
|
+
The evolution is governed by the Hamiltonian :math:`\chi c_i^\dagger c_j + h.c.`.
|
|
433
541
|
|
|
434
|
-
:param i:
|
|
542
|
+
:param i: The index of the first site.
|
|
435
543
|
:type i: int
|
|
436
|
-
:param j:
|
|
544
|
+
:param j: The index of the second site.
|
|
437
545
|
:type j: int
|
|
438
|
-
:param chi:
|
|
546
|
+
:param chi: The hopping strength. Defaults to 0.
|
|
439
547
|
:type chi: Tensor, optional
|
|
440
548
|
"""
|
|
441
549
|
self.evol_hamiltonian(self.hopping(chi, i, j, self.L))
|
|
442
550
|
|
|
443
551
|
@staticmethod
|
|
444
552
|
def chemical_potential(chi: Tensor, i: int, L: int) -> Tensor:
|
|
553
|
+
"""
|
|
554
|
+
Constructs the chemical potential Hamiltonian for a single site.
|
|
555
|
+
|
|
556
|
+
The chemical potential Hamiltonian is given by :math:`\chi c_i^\dagger c_i`.
|
|
557
|
+
|
|
558
|
+
:param chi: The chemical potential strength.
|
|
559
|
+
:type chi: Tensor
|
|
560
|
+
:param i: The index of the site.
|
|
561
|
+
:type i: int
|
|
562
|
+
:param L: The number of fermionic sites.
|
|
563
|
+
:type L: int
|
|
564
|
+
:return: The chemical potential Hamiltonian.
|
|
565
|
+
:rtype: Tensor
|
|
566
|
+
"""
|
|
445
567
|
chi = backend.convert_to_tensor(chi)
|
|
446
568
|
chi = backend.cast(chi, dtypestr)
|
|
447
569
|
m = chi / 2 * onehot_matrix(i, i, 2 * L)
|
|
@@ -450,6 +572,22 @@ class FGSSimulator:
|
|
|
450
572
|
|
|
451
573
|
@staticmethod
|
|
452
574
|
def sc_pairing(chi: Tensor, i: int, j: int, L: int) -> Tensor:
|
|
575
|
+
"""
|
|
576
|
+
Constructs the superconducting pairing Hamiltonian between two sites.
|
|
577
|
+
|
|
578
|
+
The superconducting pairing Hamiltonian is given by :math:`\chi c_i^\dagger c_j^\dagger + h.c.`.
|
|
579
|
+
|
|
580
|
+
:param chi: The pairing strength.
|
|
581
|
+
:type chi: Tensor
|
|
582
|
+
:param i: The index of the first site.
|
|
583
|
+
:type i: int
|
|
584
|
+
:param j: The index of the second site.
|
|
585
|
+
:type j: int
|
|
586
|
+
:param L: The number of fermionic sites.
|
|
587
|
+
:type L: int
|
|
588
|
+
:return: The superconducting pairing Hamiltonian.
|
|
589
|
+
:rtype: Tensor
|
|
590
|
+
"""
|
|
453
591
|
chi = backend.convert_to_tensor(chi)
|
|
454
592
|
chi = backend.cast(chi, dtypestr)
|
|
455
593
|
m = chi / 2 * onehot_matrix(i, j + L, 2 * L)
|
|
@@ -459,41 +597,57 @@ class FGSSimulator:
|
|
|
459
597
|
|
|
460
598
|
def evol_sp(self, i: int, j: int, chi: Tensor = 0) -> None:
|
|
461
599
|
r"""
|
|
462
|
-
|
|
600
|
+
Evolves the state with a superconducting pairing Hamiltonian.
|
|
463
601
|
|
|
602
|
+
The evolution is governed by the Hamiltonian :math:`\chi c_i^\dagger c_j^\dagger + h.c.`.
|
|
464
603
|
|
|
465
|
-
:param i:
|
|
604
|
+
:param i: The index of the first site.
|
|
466
605
|
:type i: int
|
|
467
|
-
:param j:
|
|
606
|
+
:param j: The index of the second site.
|
|
468
607
|
:type j: int
|
|
469
|
-
:param chi:
|
|
608
|
+
:param chi: The pairing strength. Defaults to 0.
|
|
470
609
|
:type chi: Tensor, optional
|
|
471
610
|
"""
|
|
472
611
|
self.evol_hamiltonian(self.sc_pairing(chi, i, j, self.L))
|
|
473
612
|
|
|
474
613
|
def evol_cp(self, i: int, chi: Tensor = 0) -> None:
|
|
475
614
|
r"""
|
|
476
|
-
|
|
615
|
+
Evolves the state with a chemical potential Hamiltonian.
|
|
477
616
|
|
|
478
|
-
|
|
617
|
+
The evolution is governed by the Hamiltonian :math:`\chi c_i^\dagger c_i`.
|
|
618
|
+
|
|
619
|
+
:param i: The index of the site.
|
|
479
620
|
:type i: int
|
|
480
|
-
:param chi:
|
|
621
|
+
:param chi: The chemical potential strength. Defaults to 0.
|
|
481
622
|
:type chi: Tensor, optional
|
|
482
623
|
"""
|
|
483
624
|
self.evol_hamiltonian(self.chemical_potential(chi, i, self.L))
|
|
484
625
|
|
|
485
626
|
def evol_icp(self, i: int, chi: Tensor = 0) -> None:
|
|
486
627
|
r"""
|
|
487
|
-
|
|
628
|
+
Evolves the state with a chemical potential Hamiltonian using imaginary time evolution.
|
|
629
|
+
|
|
630
|
+
The evolution is governed by :math:`e^{-H/2}` where :math:`H = \chi c_i^\dagger c_i`.
|
|
488
631
|
|
|
489
|
-
:param i:
|
|
632
|
+
:param i: The index of the site.
|
|
490
633
|
:type i: int
|
|
491
|
-
:param chi:
|
|
634
|
+
:param chi: The chemical potential strength. Defaults to 0.
|
|
492
635
|
:type chi: Tensor, optional
|
|
493
636
|
"""
|
|
494
637
|
self.evol_ihamiltonian(self.chemical_potential(chi, i, self.L))
|
|
495
638
|
|
|
496
639
|
def get_bogoliubov_uv(self) -> Tuple[Tensor, Tensor]:
|
|
640
|
+
"""
|
|
641
|
+
Returns the u and v matrices of the Bogoliubov transformation.
|
|
642
|
+
|
|
643
|
+
The Bogoliubov transformation is defined as:
|
|
644
|
+
:math:`b_k = u_{k,i} a_i + v_{k,i} a_i^\dagger`
|
|
645
|
+
|
|
646
|
+
where :math:`b_k` are the new fermionic operators and :math:`a_i` are the original ones.
|
|
647
|
+
|
|
648
|
+
:return: A tuple containing the u and v matrices.
|
|
649
|
+
:rtype: Tuple[Tensor, Tensor]
|
|
650
|
+
"""
|
|
497
651
|
return (
|
|
498
652
|
backend.gather1d(
|
|
499
653
|
self.alpha, backend.convert_to_tensor([i for i in range(self.L)])
|
|
@@ -506,17 +660,27 @@ class FGSSimulator:
|
|
|
506
660
|
|
|
507
661
|
def get_cmatrix_majorana(self) -> Tensor:
|
|
508
662
|
r"""
|
|
509
|
-
correlation matrix
|
|
510
|
-
convention: :math:`gamma_0 = c_0 + c_0^\dagger`
|
|
511
|
-
:math:`gamma_1 = i(c_0 - c_0^\dagger)`
|
|
663
|
+
Calculates the correlation matrix in the Majorana basis.
|
|
512
664
|
|
|
513
|
-
:
|
|
665
|
+
The Majorana operators are defined as:
|
|
666
|
+
:math:`\gamma_{2i} = c_i + c_i^\dagger`
|
|
667
|
+
:math:`\gamma_{2i+1} = -i(c_i - c_i^\dagger)`
|
|
668
|
+
|
|
669
|
+
:return: The correlation matrix in the Majorana basis.
|
|
514
670
|
:rtype: Tensor
|
|
515
671
|
"""
|
|
516
672
|
c = self.get_cmatrix()
|
|
517
673
|
return self.wtransform @ c @ backend.adjoint(self.wtransform)
|
|
518
674
|
|
|
519
675
|
def get_covariance_matrix(self) -> Tensor:
|
|
676
|
+
"""
|
|
677
|
+
Calculates the covariance matrix.
|
|
678
|
+
|
|
679
|
+
The covariance matrix is defined from the Majorana correlation matrix.
|
|
680
|
+
|
|
681
|
+
:return: The covariance matrix.
|
|
682
|
+
:rtype: Tensor
|
|
683
|
+
"""
|
|
520
684
|
m = self.get_cmatrix_majorana()
|
|
521
685
|
return -1.0j * (2 * m - backend.eye(self.L * 2))
|
|
522
686
|
|
|
@@ -524,34 +688,38 @@ class FGSSimulator:
|
|
|
524
688
|
self, i: int, j: int, now_i: bool = True, now_j: bool = True
|
|
525
689
|
) -> Tensor:
|
|
526
690
|
r"""
|
|
527
|
-
expectation of two
|
|
528
|
-
convention: (c, c^\dagger)
|
|
529
|
-
for i>L, c_{i-L}^\dagger is assumed
|
|
691
|
+
Calculates the expectation value of a two-fermion term.
|
|
530
692
|
|
|
531
|
-
|
|
693
|
+
The convention for the operators is (c, c^\dagger). For i >= L, the operator is c_{i-L}^\dagger.
|
|
694
|
+
|
|
695
|
+
:param i: The index of the first fermion operator.
|
|
532
696
|
:type i: int
|
|
533
|
-
:param j:
|
|
697
|
+
:param j: The index of the second fermion operator.
|
|
534
698
|
:type j: int
|
|
535
|
-
:
|
|
699
|
+
:param now_i: Whether to use the current state for the first operator. Defaults to True.
|
|
700
|
+
:type now_i: bool, optional
|
|
701
|
+
:param now_j: Whether to use the current state for the second operator. Defaults to True.
|
|
702
|
+
:type now_j: bool, optional
|
|
703
|
+
:return: The expectation value of the two-fermion term.
|
|
536
704
|
:rtype: Tensor
|
|
537
705
|
"""
|
|
538
706
|
return self.get_cmatrix(now_i, now_j)[i][(j + self.L) % (2 * self.L)]
|
|
539
707
|
|
|
540
708
|
def expectation_4body(self, i: int, j: int, k: int, l: int) -> Tensor:
|
|
541
709
|
r"""
|
|
542
|
-
expectation of four
|
|
543
|
-
convention: (c, c^\dagger)
|
|
544
|
-
for i>L, c_{i-L}^\dagger is assumed
|
|
710
|
+
Calculates the expectation value of a four-fermion term using Wick's theorem.
|
|
545
711
|
|
|
546
|
-
|
|
712
|
+
The convention for the operators is (c, c^\dagger). For an index m >= L, the operator is c_{m-L}^\dagger.
|
|
713
|
+
|
|
714
|
+
:param i: The index of the first fermion operator.
|
|
547
715
|
:type i: int
|
|
548
|
-
:param j:
|
|
716
|
+
:param j: The index of the second fermion operator.
|
|
549
717
|
:type j: int
|
|
550
|
-
:param k:
|
|
718
|
+
:param k: The index of the third fermion operator.
|
|
551
719
|
:type k: int
|
|
552
|
-
:param l:
|
|
720
|
+
:param l: The index of the fourth fermion operator.
|
|
553
721
|
:type l: int
|
|
554
|
-
:return:
|
|
722
|
+
:return: The expectation value of the four-fermion term.
|
|
555
723
|
:rtype: Tensor
|
|
556
724
|
"""
|
|
557
725
|
e = (
|
|
@@ -563,12 +731,11 @@ class FGSSimulator:
|
|
|
563
731
|
|
|
564
732
|
def post_select(self, i: int, keep: int = 1) -> None:
|
|
565
733
|
"""
|
|
566
|
-
|
|
567
|
-
<n_i> = ``keep``
|
|
734
|
+
Post-selects the state based on the occupation of a specific site.
|
|
568
735
|
|
|
569
|
-
:param i:
|
|
736
|
+
:param i: The index of the site to post-select on.
|
|
570
737
|
:type i: int
|
|
571
|
-
:param keep:
|
|
738
|
+
:param keep: The desired occupation number (0 or 1). Defaults to 1.
|
|
572
739
|
:type keep: int, optional
|
|
573
740
|
"""
|
|
574
741
|
# i is not jittable, keep is jittable
|
|
@@ -628,6 +795,20 @@ class FGSSimulator:
|
|
|
628
795
|
self.alpha = q
|
|
629
796
|
|
|
630
797
|
def cond_measure(self, ind: int, status: float, with_prob: bool = False) -> Tensor:
|
|
798
|
+
"""
|
|
799
|
+
Performs a conditional measurement on a specific site.
|
|
800
|
+
The fermion Gaussian state is collapsed in the consistent way accordingly.
|
|
801
|
+
|
|
802
|
+
:param ind: The index of the site to measure.
|
|
803
|
+
:type ind: int
|
|
804
|
+
:param status: A random number between 0 and 1 to determine the measurement outcome.
|
|
805
|
+
:type status: float
|
|
806
|
+
:param with_prob: If True, also return the probabilities of the measurement outcomes. Defaults to False.
|
|
807
|
+
:type with_prob: bool, optional
|
|
808
|
+
:return: The measurement outcome (0 or 1). If `with_prob` is True,
|
|
809
|
+
a tuple containing the outcome and the probabilities is returned.
|
|
810
|
+
:rtype: Tensor
|
|
811
|
+
"""
|
|
631
812
|
p0 = backend.real(self.get_cmatrix()[ind, ind])
|
|
632
813
|
prob = backend.convert_to_tensor([p0, 1 - p0])
|
|
633
814
|
status = backend.convert_to_tensor(status)
|
tensorcircuit/mpscircuit.py
CHANGED
|
@@ -260,6 +260,17 @@ class MPSCircuit(AbstractCircuit):
|
|
|
260
260
|
index_to: int,
|
|
261
261
|
split: Optional[Dict[str, Any]] = None,
|
|
262
262
|
) -> None:
|
|
263
|
+
"""
|
|
264
|
+
Apply a series of SWAP gates to move a qubit from ``index_from`` to ``index_to``.
|
|
265
|
+
|
|
266
|
+
:param index_from: The starting index of the qubit.
|
|
267
|
+
:type index_from: int
|
|
268
|
+
:param index_to: The destination index of the qubit.
|
|
269
|
+
:type index_to: int
|
|
270
|
+
:param split: Truncation options for the SWAP gates. Defaults to None.
|
|
271
|
+
consistent with the split option of the class.
|
|
272
|
+
:type split: Optional[Dict[str, Any]], optional
|
|
273
|
+
"""
|
|
263
274
|
if split is None:
|
|
264
275
|
split = self.split
|
|
265
276
|
self.position(index_from)
|
|
@@ -437,7 +448,15 @@ class MPSCircuit(AbstractCircuit):
|
|
|
437
448
|
split: Optional[Dict[str, Any]] = None,
|
|
438
449
|
) -> None:
|
|
439
450
|
"""
|
|
440
|
-
Reduce the bond dimension between two adjacent sites
|
|
451
|
+
Reduce the bond dimension between two adjacent sites using SVD.
|
|
452
|
+
|
|
453
|
+
:param index_left: The index of the left tensor of the bond to be truncated.
|
|
454
|
+
:type index_left: int
|
|
455
|
+
:param center_left: If True, the orthogonality center will be on the left tensor after truncation.
|
|
456
|
+
Otherwise, it will be on the right tensor. Defaults to True.
|
|
457
|
+
:type center_left: bool, optional
|
|
458
|
+
:param split: Truncation options for the SVD. Defaults to None.
|
|
459
|
+
:type split: Optional[Dict[str, Any]], optional
|
|
441
460
|
"""
|
|
442
461
|
if split is None:
|
|
443
462
|
split = self.split
|
|
@@ -463,7 +482,22 @@ class MPSCircuit(AbstractCircuit):
|
|
|
463
482
|
split: Optional[Dict[str, Any]] = None,
|
|
464
483
|
) -> None:
|
|
465
484
|
"""
|
|
466
|
-
Apply a MPO to the MPS
|
|
485
|
+
Apply a Matrix Product Operator (MPO) to the MPS.
|
|
486
|
+
|
|
487
|
+
The application involves three main steps:
|
|
488
|
+
1. Contract the MPO tensors with the corresponding MPS tensors.
|
|
489
|
+
2. Canonicalize the resulting tensors by moving the orthogonality center.
|
|
490
|
+
3. Truncate the bond dimensions to control complexity.
|
|
491
|
+
|
|
492
|
+
:param tensors: A sequence of tensors representing the MPO.
|
|
493
|
+
:type tensors: Sequence[Tensor]
|
|
494
|
+
:param index_left: The starting index on the MPS where the MPO is applied.
|
|
495
|
+
:type index_left: int
|
|
496
|
+
:param center_left: If True, the final orthogonality center will be at the left end of the MPO.
|
|
497
|
+
Otherwise, it will be at the right end. Defaults to True.
|
|
498
|
+
:type center_left: bool, optional
|
|
499
|
+
:param split: Truncation options for bond dimension reduction. Defaults to None.
|
|
500
|
+
:type split: Optional[Dict[str, Any]], optional
|
|
467
501
|
"""
|
|
468
502
|
# step 1:
|
|
469
503
|
# contract tensor
|
|
@@ -521,10 +555,17 @@ class MPSCircuit(AbstractCircuit):
|
|
|
521
555
|
*index: int,
|
|
522
556
|
split: Optional[Dict[str, Any]] = None,
|
|
523
557
|
) -> None:
|
|
524
|
-
# TODO(@SUSYUSTC): jax autograd is wrong on this function
|
|
525
558
|
"""
|
|
526
|
-
Apply
|
|
559
|
+
Apply an n-qubit gate to the MPS by converting it to an MPO.
|
|
560
|
+
|
|
561
|
+
:param gate: The n-qubit gate to apply.
|
|
562
|
+
:type gate: Gate
|
|
563
|
+
:param index: The indices of the qubits to apply the gate to.
|
|
564
|
+
:type index: int
|
|
565
|
+
:param split: Truncation options for the MPO application. Defaults to None.
|
|
566
|
+
:type split: Optional[Dict[str, Any]], optional
|
|
527
567
|
"""
|
|
568
|
+
# TODO(@SUSYUSTC): jax autograd is wrong on this function
|
|
528
569
|
ordered = np.all(np.diff(index) > 0)
|
|
529
570
|
if not ordered:
|
|
530
571
|
order = np.argsort(index)
|
|
@@ -147,14 +147,16 @@ class StabilizerCircuit(AbstractCircuit):
|
|
|
147
147
|
|
|
148
148
|
def measure(self, *index: int, with_prob: bool = False) -> Tensor:
|
|
149
149
|
"""
|
|
150
|
-
Measure qubits in Z basis.
|
|
150
|
+
Measure qubits in the Z basis.
|
|
151
151
|
|
|
152
|
-
:param index: Indices of qubits to measure
|
|
152
|
+
:param index: Indices of the qubits to measure.
|
|
153
153
|
:type index: int
|
|
154
|
-
:param with_prob:
|
|
154
|
+
:param with_prob: If True, returns the theoretical probability of the measurement outcome.
|
|
155
|
+
defaults to False
|
|
155
156
|
:type with_prob: bool, optional
|
|
156
|
-
:return:
|
|
157
|
-
|
|
157
|
+
:return: A tensor containing the measurement results.
|
|
158
|
+
If `with_prob` is True, a tuple containing the results and the probability is returned.
|
|
159
|
+
:rtype: Tensor
|
|
158
160
|
"""
|
|
159
161
|
# Convert negative indices
|
|
160
162
|
|
|
@@ -162,20 +164,29 @@ class StabilizerCircuit(AbstractCircuit):
|
|
|
162
164
|
|
|
163
165
|
# Add measurement instructions
|
|
164
166
|
s1 = self.current_simulator().copy()
|
|
165
|
-
m = s1.measure_many(*index)
|
|
166
167
|
# Sample once from the circuit using sampler
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
if with_prob:
|
|
170
|
+
num_random_measurements = 0
|
|
171
|
+
for i in index:
|
|
172
|
+
print(i, s1.peek_z(i))
|
|
173
|
+
if s1.peek_z(i) == 0:
|
|
174
|
+
num_random_measurements += 1
|
|
175
|
+
probability = (0.5) ** num_random_measurements
|
|
176
|
+
|
|
177
|
+
m = s1.measure_many(*index)
|
|
178
|
+
if with_prob:
|
|
179
|
+
return m, probability
|
|
169
180
|
return m
|
|
170
181
|
|
|
171
182
|
def cond_measurement(self, index: int) -> Tensor:
|
|
172
183
|
"""
|
|
173
|
-
Measure
|
|
184
|
+
Measure a single qubit in the Z basis and collapse the state.
|
|
174
185
|
|
|
175
|
-
:param index:
|
|
186
|
+
:param index: The index of the qubit to measure.
|
|
176
187
|
:type index: int
|
|
177
|
-
:return:
|
|
178
|
-
:rtype:
|
|
188
|
+
:return: The measurement result (0 or 1).
|
|
189
|
+
:rtype: Tensor
|
|
179
190
|
"""
|
|
180
191
|
# Convert negative indices
|
|
181
192
|
|
|
@@ -191,12 +202,12 @@ class StabilizerCircuit(AbstractCircuit):
|
|
|
191
202
|
|
|
192
203
|
def cond_measure_many(self, *index: int) -> Tensor:
|
|
193
204
|
"""
|
|
194
|
-
Measure qubits in Z basis
|
|
205
|
+
Measure multiple qubits in the Z basis and collapse the state.
|
|
195
206
|
|
|
196
|
-
:param index:
|
|
207
|
+
:param index: The indices of the qubits to measure.
|
|
197
208
|
:type index: int
|
|
198
|
-
:return:
|
|
199
|
-
:rtype:
|
|
209
|
+
:return: A tensor containing the measurement results.
|
|
210
|
+
:rtype: Tensor
|
|
200
211
|
"""
|
|
201
212
|
# Convert negative indices
|
|
202
213
|
|
tensorcircuit/translation.py
CHANGED
|
@@ -13,8 +13,6 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
|
|
14
14
|
try:
|
|
15
15
|
import qiskit.quantum_info as qi
|
|
16
|
-
import symengine
|
|
17
|
-
import sympy
|
|
18
16
|
from qiskit import QuantumCircuit
|
|
19
17
|
from qiskit.circuit import Parameter, ParameterExpression
|
|
20
18
|
from qiskit.circuit.exceptions import CircuitError
|
|
@@ -28,6 +26,14 @@ except ImportError:
|
|
|
28
26
|
CircuitInstruction = Any
|
|
29
27
|
QuantumCircuit = Any
|
|
30
28
|
|
|
29
|
+
try:
|
|
30
|
+
import symengine
|
|
31
|
+
import sympy
|
|
32
|
+
except ImportError:
|
|
33
|
+
logger.info(
|
|
34
|
+
"Please first ``pip install -U sympy symengine`` to enable `qiskit2tc` in translation module"
|
|
35
|
+
)
|
|
36
|
+
|
|
31
37
|
try:
|
|
32
38
|
import cirq
|
|
33
39
|
except ImportError:
|
|
@@ -325,7 +331,9 @@ def qir2qiskit(
|
|
|
325
331
|
qiskit_circ.append(gate, index_reversed)
|
|
326
332
|
elif gate_name == "multicontrol":
|
|
327
333
|
unitary = backend.numpy(backend.convert_to_tensor(parameters["unitary"]))
|
|
334
|
+
k = int(np.log(unitary.shape[-1]) / np.log(2) + 1e-7)
|
|
328
335
|
ctrl_str = "".join(map(str, parameters["ctrl"]))[::-1]
|
|
336
|
+
unitary = perm_matrix(k) @ unitary @ perm_matrix(k)
|
|
329
337
|
gate = UnitaryGate(unitary, label=qis_name).control(
|
|
330
338
|
len(ctrl_str), ctrl_state=ctrl_str
|
|
331
339
|
)
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
tensorcircuit/__init__.py,sha256=
|
|
1
|
+
tensorcircuit/__init__.py,sha256=f4VIGH0ZgN9St3QabFoG3CUJqNw3FwknLJRl8PU1nD8,2032
|
|
2
2
|
tensorcircuit/about.py,sha256=DazTswU2nAwOmASTaDII3L04PVtaQ7oiWPty5YMI3Wk,5267
|
|
3
3
|
tensorcircuit/abstractcircuit.py,sha256=0osacPqq7B1EJki-cI1aLYoVRmjFaG9q3XevWMs7SsA,44125
|
|
4
4
|
tensorcircuit/asciiart.py,sha256=neY1OWFwtoW5cHPNwkQHgRPktDniQvdlP9QKHkk52fM,8236
|
|
5
5
|
tensorcircuit/basecircuit.py,sha256=ipCg3J55sgkciUZ2qCZqpVqE00YIWRlACu509nktg3I,37203
|
|
6
6
|
tensorcircuit/channels.py,sha256=CFQxWI-JmkIxexslCBdjp_RSxUbHs6eAJv4LvlXXXCY,28637
|
|
7
|
-
tensorcircuit/circuit.py,sha256=
|
|
8
|
-
tensorcircuit/cons.py,sha256=
|
|
7
|
+
tensorcircuit/circuit.py,sha256=mE4b_9xRu3ydoB8iDffdx35V9GZLhAQD_tkjZDLnLjg,39105
|
|
8
|
+
tensorcircuit/cons.py,sha256=uYKBeYKkDoJEqJTNrOZPRM31tBtkqe5aAg8GtVidJ1Y,33014
|
|
9
9
|
tensorcircuit/densitymatrix.py,sha256=VqMBnWCxO5-OsOp6LOdc5RS2AzmB3U4-w40Vn_lqygo,14865
|
|
10
10
|
tensorcircuit/experimental.py,sha256=RW97ncitCfO1QJLAUbKBvm2Tsc0hzKhqkC65ShA9-Q0,34456
|
|
11
|
-
tensorcircuit/fgs.py,sha256=
|
|
11
|
+
tensorcircuit/fgs.py,sha256=dBzRz0-9o8QQ6GkA1xzBZgZvqe_APp5jazJxzvbBVaY,49515
|
|
12
12
|
tensorcircuit/gates.py,sha256=x-wA7adVpP7o0AQLt_xYUScFKj8tU_wUOV2mR1GyrPc,29322
|
|
13
13
|
tensorcircuit/keras.py,sha256=5OF4dfhEeS8sRYglpqYtQsWPeqp7uK0i7-P-6RRJ7zQ,10126
|
|
14
14
|
tensorcircuit/mps_base.py,sha256=UZ-v8vsr_rAsKrfun8prVgbXJ-qsdqKy2DZIHpq3sxo,15400
|
|
15
|
-
tensorcircuit/mpscircuit.py,sha256=
|
|
15
|
+
tensorcircuit/mpscircuit.py,sha256=COO9xzvA2Whe7Ncp6OqrgtXKmahHgTHxXTELAVHzFSY,36777
|
|
16
16
|
tensorcircuit/noisemodel.py,sha256=vzxpoYEZbHVC4a6g7_Jk4dxsHi4wvhpRFwud8b616Qo,11878
|
|
17
17
|
tensorcircuit/quantum.py,sha256=LNkIv5cJ2KG6puC18zTuXi-5cojW1Tnz-N-WjZ0Qu5Q,90217
|
|
18
18
|
tensorcircuit/shadows.py,sha256=6XmWNubbuaxFNvZVWu-RXd0lN9Jkk-xwong_K8o8_KE,17014
|
|
19
19
|
tensorcircuit/simplify.py,sha256=O11G3UYiVAc30GOfwXXmhLXwGZrQ8OVwLTMQMZp_XBc,9414
|
|
20
|
-
tensorcircuit/stabilizercircuit.py,sha256=
|
|
20
|
+
tensorcircuit/stabilizercircuit.py,sha256=yoF03HfPYPhsoRrnp3JosoNde7eYluNbcM-PRYL-KfM,15504
|
|
21
21
|
tensorcircuit/torchnn.py,sha256=z_QpM0QC3mydGyWpyp877j-tSFCPyzynCwqrTWaw-IA,4637
|
|
22
|
-
tensorcircuit/translation.py,sha256=
|
|
22
|
+
tensorcircuit/translation.py,sha256=VnU7DnYmbk1cWjqa7N68WNLNDn3DwENrMzmbG4_CQco,28611
|
|
23
23
|
tensorcircuit/utils.py,sha256=CH9gTV4iKIikSS8KajIu3ttyC8i_1tBPf5PAYH1fgxs,7060
|
|
24
24
|
tensorcircuit/vis.py,sha256=O4hm050KKfOAoVyHsjpMg6NBNVoWhLSlv-xsCx4opsU,12196
|
|
25
25
|
tensorcircuit/applications/__init__.py,sha256=nAX-Am6JoL9k53iJ_CjZJ2NcjIpaz21H87nrW4Op03k,246
|
|
@@ -42,7 +42,7 @@ tensorcircuit/backends/__init__.py,sha256=WiUmbUFzM29w3hKfhuKxVUk3PpqDFiXf4za9g0
|
|
|
42
42
|
tensorcircuit/backends/abstract_backend.py,sha256=Ob8zp-AgovoY3uYFNEUA_WlJz9YtgnaJvugKUXWttAA,59018
|
|
43
43
|
tensorcircuit/backends/backend_factory.py,sha256=Z0aQ-RnxOnQzp-SRw8sefAH8XyBSlj2NXZwOlHinbfY,1713
|
|
44
44
|
tensorcircuit/backends/cupy_backend.py,sha256=4vgO3lnQnsvWL5hukhskjJp37EAHqio6z6TVXTQcdjs,15077
|
|
45
|
-
tensorcircuit/backends/jax_backend.py,sha256=
|
|
45
|
+
tensorcircuit/backends/jax_backend.py,sha256=dkDQ380CJHIdlt1fZvlN_g8DIowWPEcTTV_XBcs0YB0,26088
|
|
46
46
|
tensorcircuit/backends/jax_ops.py,sha256=o7tLlQMRnaKWcr5rVnOMqwG6KZVpR8M8ryNQ-ceXVxs,4789
|
|
47
47
|
tensorcircuit/backends/numpy_backend.py,sha256=sd1migp_E2FWjchvOeYRuyM47yexegT2_SW_ukSYSF8,14171
|
|
48
48
|
tensorcircuit/backends/pytorch_backend.py,sha256=yhfZSrm99yNW-dmijk8t6zAkbVgLRd4b_aIWKrpT7bY,24230
|
|
@@ -84,23 +84,23 @@ tensorcircuit/templates/dataset.py,sha256=ldPvCUlwjHU_S98E2ISQp34KqJzJPpPHmDIKJ4
|
|
|
84
84
|
tensorcircuit/templates/graphs.py,sha256=cPYrxjoem0xZ-Is9dZKAvEzWZL_FejfIRiCEOTA4qd4,3935
|
|
85
85
|
tensorcircuit/templates/lattice.py,sha256=F35ebANk0DSmSHLR0-Q_hUbcznyCmZjb4fKmvCMywmA,58575
|
|
86
86
|
tensorcircuit/templates/measurements.py,sha256=pzc5Aa9S416Ilg4aOY77Z6ZhUlYcXnAkQNQFTuHjFFs,10943
|
|
87
|
-
tensorcircuit_nightly-1.2.1.
|
|
87
|
+
tensorcircuit_nightly-1.2.1.dev20250726.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
88
88
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
tests/conftest.py,sha256=J9nHlLE3Zspz1rMyzadEuBWhaS5I4Q9sq0lnWybcdIA,1457
|
|
90
90
|
tests/test_backends.py,sha256=rClxb2gyAoGeXd_ZYVSAJ0zEvJ7z_2btAeFM_Iy_wwY,33925
|
|
91
91
|
tests/test_calibrating.py,sha256=D1Tlv8mucUhg3ULvB5QlYyaDfw7aEERwq69-aGSb1A4,3805
|
|
92
92
|
tests/test_channels.py,sha256=BL4CirU8ku9-_NrI6PZAS5xZ0wrL1UEC1S3wPI9dYQM,12628
|
|
93
|
-
tests/test_circuit.py,sha256=
|
|
93
|
+
tests/test_circuit.py,sha256=IsSIFEs7hUCSYexMb-ESt1ZUpztHtLA0qz0CZolGdc4,52240
|
|
94
94
|
tests/test_cloud.py,sha256=241ng6LnG_o_2PKR-BuUFfmrj3V1aeFiI-_bcWuPFyo,5606
|
|
95
95
|
tests/test_compiler.py,sha256=R1t0MDQR01uEbY2wxqzQEf-LkSehrfZWmLvPuguC2JI,3419
|
|
96
|
-
tests/test_dmcircuit.py,sha256=
|
|
96
|
+
tests/test_dmcircuit.py,sha256=ZtTS-Jcpt-oN3yafYee9ZZCFW8-2I0MaLpyaDPve0PA,17234
|
|
97
97
|
tests/test_ensemble.py,sha256=0RzJkv-5D8LeZxS0Q0MwtEcgnXd2zefMquPHRNYT6RY,2109
|
|
98
98
|
tests/test_fgs.py,sha256=nv3E_F_SAF4ChsoT8Ihm3FtSpOmTGJr_Jf2MoKXXceE,10162
|
|
99
99
|
tests/test_gates.py,sha256=rAIV2QFpFsA5bT1QivTSkhdarvwu5t0N3IOz4SEDrzg,4593
|
|
100
100
|
tests/test_interfaces.py,sha256=iJPmes8S8HkA9_PGjsu4Ike-vCXYyS1EMgnNKKXDNaU,16938
|
|
101
101
|
tests/test_keras.py,sha256=U453jukavmx0RMeTSDEgPzrNdHNEfK1CW0CqO3XCNKo,4841
|
|
102
102
|
tests/test_lattice.py,sha256=_ptDVK3EhS-X5fCQWiP8sHk3azdyGFuwqg6KMkBTkDE,65789
|
|
103
|
-
tests/test_miscs.py,sha256=
|
|
103
|
+
tests/test_miscs.py,sha256=mm6kv5LqLkwHxWrGzLxajyOp1RaaKoHxq2OT1J3DpIM,9741
|
|
104
104
|
tests/test_mpscircuit.py,sha256=mDXX8oQeFeHr_PdZvwqyDs_tVcVAqLmCERqlTAU7590,10552
|
|
105
105
|
tests/test_noisemodel.py,sha256=UYoMtCjwDaB-CCn5kLosofz-qTMiY4KGAFBjVtqqLPE,5637
|
|
106
106
|
tests/test_qaoa.py,sha256=hEcC_XVmKBGt9XgUGtbTO8eQQK4mjorgTIrfqZCeQls,2616
|
|
@@ -110,11 +110,11 @@ tests/test_quantum_attr.py,sha256=Zl6WbkbnTWVp6FL2rR21qBGsLoheoIEZXqWZKxfpDRs,12
|
|
|
110
110
|
tests/test_results.py,sha256=8cQO0ShkBc4_pB-fi9s35WJbuZl5ex5y1oElSV-GlRo,11882
|
|
111
111
|
tests/test_shadows.py,sha256=1T3kJesVJ5XfZrSncL80xdq-taGCSnTDF3eL15UlavY,5160
|
|
112
112
|
tests/test_simplify.py,sha256=35tbOu1QANsPvY1buLwNhqPnMkBOsnBtHn82qaukmgI,1175
|
|
113
|
-
tests/test_stabilizer.py,sha256=
|
|
113
|
+
tests/test_stabilizer.py,sha256=MivuZ5pY7GOcEPTanhtrflXostyLBToHyjfPqCU0tG0,5450
|
|
114
114
|
tests/test_templates.py,sha256=Xm9otFFaaBWG9TZpgJ-nNh9MBfRipTzFWL8fBOnie2k,7192
|
|
115
115
|
tests/test_torchnn.py,sha256=CHLTfWkF7Ses5_XnGFN_uv_JddfgenFEFzaDtSH8XYU,2848
|
|
116
116
|
tests/test_van.py,sha256=kAWz860ivlb5zAJuYpzuBe27qccT-Yf0jatf5uXtTo4,3163
|
|
117
|
-
tensorcircuit_nightly-1.2.1.
|
|
118
|
-
tensorcircuit_nightly-1.2.1.
|
|
119
|
-
tensorcircuit_nightly-1.2.1.
|
|
120
|
-
tensorcircuit_nightly-1.2.1.
|
|
117
|
+
tensorcircuit_nightly-1.2.1.dev20250726.dist-info/METADATA,sha256=rqna4H-ixz2BjdKLCDo3EUlJAoUr2XrJqs0V537BCfo,34831
|
|
118
|
+
tensorcircuit_nightly-1.2.1.dev20250726.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
119
|
+
tensorcircuit_nightly-1.2.1.dev20250726.dist-info/top_level.txt,sha256=O_Iqeh2x02lasEYMI9iyPNNNtMzcpg5qvwMOkZQ7n4A,20
|
|
120
|
+
tensorcircuit_nightly-1.2.1.dev20250726.dist-info/RECORD,,
|
tests/test_circuit.py
CHANGED
|
@@ -889,6 +889,19 @@ def test_circuit_quoperator(backend):
|
|
|
889
889
|
np.testing.assert_allclose(qo.eval_matrix(), c.matrix(), atol=1e-5)
|
|
890
890
|
|
|
891
891
|
|
|
892
|
+
def test_perm_matrix():
|
|
893
|
+
from tensorcircuit.translation import perm_matrix
|
|
894
|
+
|
|
895
|
+
p2 = perm_matrix(2)
|
|
896
|
+
np.testing.assert_allclose(
|
|
897
|
+
p2, np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
|
|
898
|
+
)
|
|
899
|
+
p3 = perm_matrix(3)
|
|
900
|
+
v = np.arange(8)
|
|
901
|
+
vt = np.array([0, 4, 2, 6, 1, 5, 3, 7])
|
|
902
|
+
np.testing.assert_allclose(p3 @ v, vt)
|
|
903
|
+
|
|
904
|
+
|
|
892
905
|
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
893
906
|
def test_qir2cirq(backend):
|
|
894
907
|
try:
|
|
@@ -1094,7 +1107,7 @@ def test_qiskit2tc():
|
|
|
1094
1107
|
import qiskit.quantum_info as qi
|
|
1095
1108
|
from qiskit import QuantumCircuit
|
|
1096
1109
|
from qiskit.circuit.library import HamiltonianGate
|
|
1097
|
-
from qiskit.circuit.library.standard_gates import MCXGate, SwapGate
|
|
1110
|
+
from qiskit.circuit.library.standard_gates import MCXGate, SwapGate, CXGate
|
|
1098
1111
|
|
|
1099
1112
|
from tensorcircuit.translation import perm_matrix
|
|
1100
1113
|
except ImportError:
|
|
@@ -1150,12 +1163,13 @@ def test_qiskit2tc():
|
|
|
1150
1163
|
mcx_g = MCXGate(3, ctrl_state="010")
|
|
1151
1164
|
qisc.append(mcx_g, [0, 1, 2, 3])
|
|
1152
1165
|
qisc.ccx(0, 1, 2, ctrl_state="01")
|
|
1153
|
-
|
|
1154
|
-
qisc.append(
|
|
1166
|
+
CCswap = SwapGate().control(2, ctrl_state="01")
|
|
1167
|
+
qisc.append(CCswap, [0, 1, 2, 3])
|
|
1168
|
+
CCCX = CXGate().control(2, ctrl_state="01")
|
|
1169
|
+
qisc.append(CCCX, [1, 2, 3, 4])
|
|
1155
1170
|
|
|
1156
|
-
c = tc.Circuit.from_qiskit(qisc, n
|
|
1157
|
-
tc_unitary = c.
|
|
1158
|
-
tc_unitary = np.reshape(tc_unitary, [2**n, 2**n])
|
|
1171
|
+
c = tc.Circuit.from_qiskit(qisc, n)
|
|
1172
|
+
tc_unitary = c.matrix()
|
|
1159
1173
|
qis_unitary = qi.Operator(qisc)
|
|
1160
1174
|
qis_unitary = np.reshape(qis_unitary, [2**n, 2**n])
|
|
1161
1175
|
p_mat = perm_matrix(n)
|
tests/test_dmcircuit.py
CHANGED
tests/test_miscs.py
CHANGED
|
@@ -312,3 +312,23 @@ def test_distrubuted_contractor(jaxb):
|
|
|
312
312
|
return c.expectation_ps(z=[-1])
|
|
313
313
|
|
|
314
314
|
np.testing.assert_allclose(value, baseline(params), atol=1e-6)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
318
|
+
def test_runtime_nodes_capture(backend):
|
|
319
|
+
with tc.cons.runtime_nodes_capture() as captured:
|
|
320
|
+
c = tc.Circuit(3)
|
|
321
|
+
c.h(0)
|
|
322
|
+
c.amplitude("010")
|
|
323
|
+
len(captured["nodes"]) == 7
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
327
|
+
def test_function_nodes_capture(backend):
|
|
328
|
+
@tc.cons.function_nodes_capture
|
|
329
|
+
def exp(theta):
|
|
330
|
+
c = tc.Circuit(3)
|
|
331
|
+
c.h(0)
|
|
332
|
+
return c.expectation_ps(z=[-3], reuse=False)
|
|
333
|
+
|
|
334
|
+
assert len(exp(0.3)) == 9
|
tests/test_stabilizer.py
CHANGED
|
@@ -215,3 +215,12 @@ def test_mipt():
|
|
|
215
215
|
return c.entanglement_entropy(list(range(n // 2)))
|
|
216
216
|
|
|
217
217
|
print(ruc(50, 10, 0.1))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_measure_with_prob():
|
|
221
|
+
c = tc.StabilizerCircuit(3)
|
|
222
|
+
c.h(0)
|
|
223
|
+
c.cnot(0, 1)
|
|
224
|
+
m, p = c.measure(0, 2, with_prob=True)
|
|
225
|
+
np.testing.assert_allclose(p, 0.5, atol=1e-6)
|
|
226
|
+
print(m)
|
|
File without changes
|
|
File without changes
|