tensorcircuit-nightly 1.2.1.dev20250725__py3-none-any.whl → 1.3.0.dev20250727__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 +254 -72
- tensorcircuit/mpscircuit.py +45 -4
- tensorcircuit/results/counts.py +99 -0
- tensorcircuit/stabilizercircuit.py +25 -15
- tensorcircuit/templates/__init__.py +1 -0
- tensorcircuit/templates/hamiltonians.py +153 -0
- tensorcircuit/utils.py +7 -0
- {tensorcircuit_nightly-1.2.1.dev20250725.dist-info → tensorcircuit_nightly-1.3.0.dev20250727.dist-info}/METADATA +2 -2
- {tensorcircuit_nightly-1.2.1.dev20250725.dist-info → tensorcircuit_nightly-1.3.0.dev20250727.dist-info}/RECORD +22 -20
- tests/test_circuit.py +13 -0
- tests/test_dmcircuit.py +1 -1
- tests/test_fgs.py +8 -0
- tests/test_hamiltonians.py +159 -0
- tests/test_miscs.py +20 -0
- tests/test_stabilizer.py +9 -0
- {tensorcircuit_nightly-1.2.1.dev20250725.dist-info → tensorcircuit_nightly-1.3.0.dev20250727.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.2.1.dev20250725.dist-info → tensorcircuit_nightly-1.3.0.dev20250727.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.2.1.dev20250725.dist-info → tensorcircuit_nightly-1.3.0.dev20250727.dist-info}/top_level.txt +0 -0
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,20 +297,20 @@ 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)
|
|
235
310
|
lbd, _ = backend.eigh(m)
|
|
236
311
|
lbd = backend.real(lbd)
|
|
237
312
|
lbd = backend.relu(lbd)
|
|
238
|
-
eps = 1e-
|
|
313
|
+
eps = 1e-9
|
|
239
314
|
|
|
240
315
|
entropy = backend.sum(backend.log(lbd**n + (1 - lbd) ** n + eps))
|
|
241
316
|
s = 1 / (2 * (1 - n)) * entropy
|
|
@@ -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
|
|
@@ -366,7 +449,7 @@ class FGSSimulator:
|
|
|
366
449
|
lbd = backend.real(lbd)
|
|
367
450
|
lbd = backend.relu(lbd)
|
|
368
451
|
# lbd /= backend.sum(lbd)
|
|
369
|
-
eps = 1e-
|
|
452
|
+
eps = 1e-9
|
|
370
453
|
entropy = -backend.sum(
|
|
371
454
|
lbd * backend.log(lbd + eps) + (1 - lbd) * backend.log(1 - lbd + eps)
|
|
372
455
|
)
|
|
@@ -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.
|
|
616
|
+
|
|
617
|
+
The evolution is governed by the Hamiltonian :math:`\chi c_i^\dagger c_i`.
|
|
477
618
|
|
|
478
|
-
:param i:
|
|
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.
|
|
488
629
|
|
|
489
|
-
:
|
|
630
|
+
The evolution is governed by :math:`e^{-H/2}` where :math:`H = \chi c_i^\dagger c_i`.
|
|
631
|
+
|
|
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
|
-
|
|
529
|
-
for i
|
|
691
|
+
Calculates the expectation value of a two-fermion term.
|
|
692
|
+
|
|
693
|
+
The convention for the operators is (c, c^\dagger). For i >= L, the operator is c_{i-L}^\dagger.
|
|
530
694
|
|
|
531
|
-
:param i:
|
|
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
|
-
|
|
544
|
-
for
|
|
710
|
+
Calculates the expectation value of a four-fermion term using Wick's theorem.
|
|
711
|
+
|
|
712
|
+
The convention for the operators is (c, c^\dagger). For an index m >= L, the operator is c_{m-L}^\dagger.
|
|
545
713
|
|
|
546
|
-
:param i:
|
|
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
|
|
@@ -626,8 +793,23 @@ class FGSSimulator:
|
|
|
626
793
|
alpha1 = alpha1 * mask02d + backend.tile(newcol[:, None], [1, self.L]) * mask12d
|
|
627
794
|
q, _ = backend.qr(alpha1)
|
|
628
795
|
self.alpha = q
|
|
796
|
+
self.cmatrix = None
|
|
629
797
|
|
|
630
798
|
def cond_measure(self, ind: int, status: float, with_prob: bool = False) -> Tensor:
|
|
799
|
+
"""
|
|
800
|
+
Performs a conditional measurement on a specific site.
|
|
801
|
+
The fermion Gaussian state is collapsed in the consistent way accordingly.
|
|
802
|
+
|
|
803
|
+
:param ind: The index of the site to measure.
|
|
804
|
+
:type ind: int
|
|
805
|
+
:param status: A random number between 0 and 1 to determine the measurement outcome.
|
|
806
|
+
:type status: float
|
|
807
|
+
:param with_prob: If True, also return the probabilities of the measurement outcomes. Defaults to False.
|
|
808
|
+
:type with_prob: bool, optional
|
|
809
|
+
:return: The measurement outcome (0 or 1). If `with_prob` is True,
|
|
810
|
+
a tuple containing the outcome and the probabilities is returned.
|
|
811
|
+
:rtype: Tensor
|
|
812
|
+
"""
|
|
631
813
|
p0 = backend.real(self.get_cmatrix()[ind, ind])
|
|
632
814
|
prob = backend.convert_to_tensor([p0, 1 - p0])
|
|
633
815
|
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)
|