tensorcircuit-nightly 1.3.0.dev20250728__py3-none-any.whl → 1.3.0.dev20250729__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 +2 -1
- tensorcircuit/backends/abstract_backend.py +3 -0
- tensorcircuit/backends/tensorflow_backend.py +8 -1
- tensorcircuit/experimental.py +7 -152
- tensorcircuit/timeevol.py +346 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250729.dist-info}/METADATA +2 -2
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250729.dist-info}/RECORD +12 -10
- tests/test_miscs.py +0 -30
- tests/test_timeevol.py +238 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250729.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250729.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250729.dist-info}/top_level.txt +0 -0
tensorcircuit/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "1.3.0.
|
|
1
|
+
__version__ = "1.3.0.dev20250729"
|
|
2
2
|
__author__ = "TensorCircuit Authors"
|
|
3
3
|
__creator__ = "refraction-ray"
|
|
4
4
|
|
|
@@ -52,6 +52,7 @@ from . import compiler
|
|
|
52
52
|
from . import cloud
|
|
53
53
|
from . import fgs
|
|
54
54
|
from .fgs import FGSSimulator
|
|
55
|
+
from . import timeevol
|
|
55
56
|
|
|
56
57
|
FGSCircuit = FGSSimulator
|
|
57
58
|
|
|
@@ -700,6 +700,9 @@ class ExtendedBackend:
|
|
|
700
700
|
"Backend '{}' has not implemented `is_tensor`.".format(self.name)
|
|
701
701
|
)
|
|
702
702
|
|
|
703
|
+
def matvec(self: Any, A: Tensor, x: Tensor) -> Tensor:
|
|
704
|
+
return self.tensordot(A, x, axes=[[1], [0]])
|
|
705
|
+
|
|
703
706
|
def cast(self: Any, a: Tensor, dtype: str) -> Tensor:
|
|
704
707
|
"""
|
|
705
708
|
Cast the tensor dtype of a ``a``.
|
|
@@ -678,7 +678,14 @@ class TensorFlowBackend(tensorflow_backend.TensorFlowBackend, ExtendedBackend):
|
|
|
678
678
|
sp_a: Tensor,
|
|
679
679
|
b: Tensor,
|
|
680
680
|
) -> Tensor:
|
|
681
|
-
|
|
681
|
+
is_vec = False
|
|
682
|
+
if len(b.shape) == 1:
|
|
683
|
+
b = self.reshape(b, [-1, 1])
|
|
684
|
+
is_vec = True
|
|
685
|
+
r = tf.sparse.sparse_dense_matmul(sp_a, b)
|
|
686
|
+
if is_vec:
|
|
687
|
+
return self.reshape(r, [-1])
|
|
688
|
+
return r
|
|
682
689
|
|
|
683
690
|
def _densify(self) -> Tensor:
|
|
684
691
|
@partial(self.jit, jit_compile=True)
|
tensorcircuit/experimental.py
CHANGED
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
Experimental features
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
# pylint: disable=unused-import
|
|
6
|
+
|
|
5
7
|
from functools import partial
|
|
6
8
|
import logging
|
|
7
9
|
from typing import Any, Callable, Dict, Optional, Tuple, List, Sequence, Union
|
|
8
10
|
|
|
9
11
|
import numpy as np
|
|
10
12
|
|
|
11
|
-
from .cons import backend, dtypestr,
|
|
13
|
+
from .cons import backend, dtypestr, rdtypestr, get_tn_info
|
|
12
14
|
from .gates import Gate
|
|
15
|
+
from .timeevol import hamiltonian_evol, evol_global, evol_local
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# for backward compatibility
|
|
13
19
|
|
|
14
20
|
Tensor = Any
|
|
15
21
|
Circuit = Any
|
|
@@ -435,157 +441,6 @@ def finite_difference_differentiator(
|
|
|
435
441
|
return tf_function # type: ignore
|
|
436
442
|
|
|
437
443
|
|
|
438
|
-
def hamiltonian_evol(
|
|
439
|
-
tlist: Tensor,
|
|
440
|
-
h: Tensor,
|
|
441
|
-
psi0: Tensor,
|
|
442
|
-
callback: Optional[Callable[..., Any]] = None,
|
|
443
|
-
) -> Tensor:
|
|
444
|
-
"""
|
|
445
|
-
Fast implementation of time independent Hamiltonian evolution using eigendecomposition.
|
|
446
|
-
By default, performs imaginary time evolution.
|
|
447
|
-
|
|
448
|
-
:param tlist: Time points for evolution
|
|
449
|
-
:type tlist: Tensor
|
|
450
|
-
:param h: Time-independent Hamiltonian matrix
|
|
451
|
-
:type h: Tensor
|
|
452
|
-
:param psi0: Initial state vector
|
|
453
|
-
:type psi0: Tensor
|
|
454
|
-
:param callback: Optional function to process state at each time point
|
|
455
|
-
:type callback: Optional[Callable[..., Any]], optional
|
|
456
|
-
:return: Evolution results at each time point. If callback is None, returns state vectors;
|
|
457
|
-
otherwise returns callback results
|
|
458
|
-
:rtype: Tensor
|
|
459
|
-
|
|
460
|
-
:Example:
|
|
461
|
-
|
|
462
|
-
>>> import tensorcircuit as tc
|
|
463
|
-
>>> import numpy as np
|
|
464
|
-
>>> # Define a simple 2-qubit Hamiltonian
|
|
465
|
-
>>> h = tc.array_to_tensor([
|
|
466
|
-
... [1.0, 0.0, 0.0, 0.0],
|
|
467
|
-
... [0.0, -1.0, 2.0, 0.0],
|
|
468
|
-
... [0.0, 2.0, -1.0, 0.0],
|
|
469
|
-
... [0.0, 0.0, 0.0, 1.0]
|
|
470
|
-
... ])
|
|
471
|
-
>>> # Initial state |00⟩
|
|
472
|
-
>>> psi0 = tc.array_to_tensor([1.0, 0.0, 0.0, 0.0])
|
|
473
|
-
>>> # Evolution times
|
|
474
|
-
>>> times = tc.array_to_tensor([0.0, 0.5, 1.0])
|
|
475
|
-
>>> # Evolve and get states
|
|
476
|
-
>>> states = tc.experimental.hamiltonian_evol(times, h, psi0)
|
|
477
|
-
>>> print(states.shape) # (3, 4)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
Note:
|
|
481
|
-
1. The Hamiltonian must be time-independent
|
|
482
|
-
2. For time-dependent Hamiltonians, use ``evol_local`` or ``evol_global`` instead
|
|
483
|
-
3. The evolution is performed in imaginary time by default (factor -t in exponential)
|
|
484
|
-
4. The state is automatically normalized at each time point
|
|
485
|
-
"""
|
|
486
|
-
es, u = backend.eigh(h)
|
|
487
|
-
utpsi0 = backend.reshape(
|
|
488
|
-
backend.transpose(u) @ backend.reshape(psi0, [-1, 1]), [-1]
|
|
489
|
-
)
|
|
490
|
-
|
|
491
|
-
@backend.jit
|
|
492
|
-
def _evol(t: Tensor) -> Tensor:
|
|
493
|
-
ebetah_utpsi0 = backend.exp(-t * es) * utpsi0
|
|
494
|
-
psi_exact = backend.conj(u) @ backend.reshape(ebetah_utpsi0, [-1, 1])
|
|
495
|
-
psi_exact = backend.reshape(psi_exact, [-1])
|
|
496
|
-
psi_exact = psi_exact / backend.norm(psi_exact)
|
|
497
|
-
if callback is None:
|
|
498
|
-
return psi_exact
|
|
499
|
-
return callback(psi_exact)
|
|
500
|
-
|
|
501
|
-
return backend.stack([_evol(t) for t in tlist])
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
def evol_local(
|
|
505
|
-
c: Circuit,
|
|
506
|
-
index: Sequence[int],
|
|
507
|
-
h_fun: Callable[..., Tensor],
|
|
508
|
-
t: float,
|
|
509
|
-
*args: Any,
|
|
510
|
-
**solver_kws: Any,
|
|
511
|
-
) -> Circuit:
|
|
512
|
-
"""
|
|
513
|
-
ode evolution of time dependent Hamiltonian on circuit of given indices
|
|
514
|
-
[only jax backend support for now]
|
|
515
|
-
|
|
516
|
-
:param c: _description_
|
|
517
|
-
:type c: Circuit
|
|
518
|
-
:param index: qubit sites to evolve
|
|
519
|
-
:type index: Sequence[int]
|
|
520
|
-
:param h_fun: h_fun should return a dense Hamiltonian matrix
|
|
521
|
-
with input arguments time and *args
|
|
522
|
-
:type h_fun: Callable[..., Tensor]
|
|
523
|
-
:param t: evolution time
|
|
524
|
-
:type t: float
|
|
525
|
-
:return: _description_
|
|
526
|
-
:rtype: Circuit
|
|
527
|
-
"""
|
|
528
|
-
from jax.experimental.ode import odeint
|
|
529
|
-
|
|
530
|
-
s = c.state()
|
|
531
|
-
n = c._nqubits
|
|
532
|
-
l = len(index)
|
|
533
|
-
|
|
534
|
-
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
535
|
-
y = backend.reshape2(y)
|
|
536
|
-
y = Gate(y)
|
|
537
|
-
h = -1.0j * h_fun(t, *args)
|
|
538
|
-
h = backend.reshape2(h)
|
|
539
|
-
h = Gate(h)
|
|
540
|
-
edges = []
|
|
541
|
-
for i in range(n):
|
|
542
|
-
if i not in index:
|
|
543
|
-
edges.append(y[i])
|
|
544
|
-
else:
|
|
545
|
-
j = index.index(i)
|
|
546
|
-
edges.append(h[j])
|
|
547
|
-
h[j + l] ^ y[i]
|
|
548
|
-
y = contractor([y, h], output_edge_order=edges)
|
|
549
|
-
return backend.reshape(y.tensor, [-1])
|
|
550
|
-
|
|
551
|
-
ts = backend.stack([0.0, t])
|
|
552
|
-
ts = backend.cast(ts, dtype=rdtypestr)
|
|
553
|
-
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
554
|
-
return type(c)(n, inputs=s1[-1])
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def evol_global(
|
|
558
|
-
c: Circuit, h_fun: Callable[..., Tensor], t: float, *args: Any, **solver_kws: Any
|
|
559
|
-
) -> Circuit:
|
|
560
|
-
"""
|
|
561
|
-
ode evolution of time dependent Hamiltonian on circuit of all qubits
|
|
562
|
-
[only jax backend support for now]
|
|
563
|
-
|
|
564
|
-
:param c: _description_
|
|
565
|
-
:type c: Circuit
|
|
566
|
-
:param h_fun: h_fun should return a **SPARSE** Hamiltonian matrix
|
|
567
|
-
with input arguments time and *args
|
|
568
|
-
:type h_fun: Callable[..., Tensor]
|
|
569
|
-
:param t: _description_
|
|
570
|
-
:type t: float
|
|
571
|
-
:return: _description_
|
|
572
|
-
:rtype: Circuit
|
|
573
|
-
"""
|
|
574
|
-
from jax.experimental.ode import odeint
|
|
575
|
-
|
|
576
|
-
s = c.state()
|
|
577
|
-
n = c._nqubits
|
|
578
|
-
|
|
579
|
-
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
580
|
-
h = -1.0j * h_fun(t, *args)
|
|
581
|
-
return backend.sparse_dense_matmul(h, y)
|
|
582
|
-
|
|
583
|
-
ts = backend.stack([0.0, t])
|
|
584
|
-
ts = backend.cast(ts, dtype=rdtypestr)
|
|
585
|
-
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
586
|
-
return type(c)(n, inputs=s1[-1])
|
|
587
|
-
|
|
588
|
-
|
|
589
444
|
def jax_jitted_function_save(filename: str, f: Callable[..., Any], *args: Any) -> None:
|
|
590
445
|
"""
|
|
591
446
|
save a jitted jax function as a file
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analog time evolution engines
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Tuple, Optional, Callable, List, Sequence
|
|
6
|
+
|
|
7
|
+
from .cons import backend, dtypestr, rdtypestr, contractor
|
|
8
|
+
from .gates import Gate
|
|
9
|
+
|
|
10
|
+
Tensor = Any
|
|
11
|
+
Circuit = Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def lanczos_iteration(
|
|
15
|
+
hamiltonian: Tensor, initial_vector: Tensor, subspace_dimension: int
|
|
16
|
+
) -> Tuple[Tensor, Tensor]:
|
|
17
|
+
"""
|
|
18
|
+
Use Lanczos algorithm to construct orthogonal basis and projected Hamiltonian
|
|
19
|
+
of Krylov subspace.
|
|
20
|
+
|
|
21
|
+
:param hamiltonian: Sparse or dense Hamiltonian matrix
|
|
22
|
+
:type hamiltonian: Tensor
|
|
23
|
+
:param initial_vector: Initial quantum state vector
|
|
24
|
+
:type initial_vector: Tensor
|
|
25
|
+
:param subspace_dimension: Dimension of Krylov subspace
|
|
26
|
+
:type subspace_dimension: int
|
|
27
|
+
:return: Tuple containing (basis matrix, projected Hamiltonian)
|
|
28
|
+
:rtype: Tuple[Tensor, Tensor]
|
|
29
|
+
"""
|
|
30
|
+
# Initialize
|
|
31
|
+
vector = initial_vector
|
|
32
|
+
vector = backend.cast(vector, dtypestr)
|
|
33
|
+
|
|
34
|
+
# Use list to store basis vectors
|
|
35
|
+
basis_vectors: List[Any] = []
|
|
36
|
+
|
|
37
|
+
# Store alpha and beta coefficients for constructing tridiagonal matrix
|
|
38
|
+
alphas = []
|
|
39
|
+
betas = []
|
|
40
|
+
|
|
41
|
+
# Normalize initial vector
|
|
42
|
+
vector_norm = backend.norm(vector)
|
|
43
|
+
vector = vector / vector_norm
|
|
44
|
+
|
|
45
|
+
# Add first basis vector
|
|
46
|
+
basis_vectors.append(vector)
|
|
47
|
+
|
|
48
|
+
# Lanczos iteration (fixed number of iterations for JIT compatibility)
|
|
49
|
+
for j in range(subspace_dimension):
|
|
50
|
+
# Calculate H|v_j>
|
|
51
|
+
if backend.is_sparse(hamiltonian):
|
|
52
|
+
w = backend.sparse_dense_matmul(hamiltonian, vector)
|
|
53
|
+
else:
|
|
54
|
+
w = backend.matvec(hamiltonian, vector)
|
|
55
|
+
|
|
56
|
+
# Calculate alpha_j = <v_j|H|v_j>
|
|
57
|
+
alpha = backend.real(backend.sum(backend.conj(vector) * w))
|
|
58
|
+
alphas.append(alpha)
|
|
59
|
+
|
|
60
|
+
# w = H|v_j> - alpha_j|v_j> - beta_{j-1}|v_{j-1}>
|
|
61
|
+
# is not sufficient, require re-normalization
|
|
62
|
+
w = w - backend.cast(alpha, dtypestr) * vector
|
|
63
|
+
|
|
64
|
+
for k in range(j + 1):
|
|
65
|
+
v_k = basis_vectors[k]
|
|
66
|
+
projection = backend.sum(backend.conj(v_k) * w)
|
|
67
|
+
w = w - projection * v_k
|
|
68
|
+
|
|
69
|
+
# if j > 0:
|
|
70
|
+
# w = w - prev_beta * basis_vectors[-2]
|
|
71
|
+
|
|
72
|
+
# Calculate beta_{j+1} = ||w||
|
|
73
|
+
beta = backend.norm(w)
|
|
74
|
+
betas.append(beta)
|
|
75
|
+
|
|
76
|
+
# Use regularization technique to avoid division by zero error,
|
|
77
|
+
# adding small epsilon value to ensure numerical stability
|
|
78
|
+
epsilon = 1e-15
|
|
79
|
+
norm_factor = 1.0 / (beta + epsilon)
|
|
80
|
+
|
|
81
|
+
# Normalize w to get |v_{j+1}> (except for the last iteration)
|
|
82
|
+
if j < subspace_dimension - 1:
|
|
83
|
+
vector = w * backend.cast(norm_factor, dtypestr)
|
|
84
|
+
basis_vectors.append(vector)
|
|
85
|
+
|
|
86
|
+
# Construct final basis matrix
|
|
87
|
+
basis_matrix = backend.stack(basis_vectors, axis=1)
|
|
88
|
+
|
|
89
|
+
# Construct tridiagonal projected Hamiltonian
|
|
90
|
+
# Use vectorized method to construct tridiagonal matrix at once
|
|
91
|
+
alphas_tensor = backend.stack(alphas)
|
|
92
|
+
# Only use first krylov_dim-1 beta values to construct off-diagonal
|
|
93
|
+
betas_tensor = backend.stack(betas[:-1]) if len(betas) > 1 else backend.stack([])
|
|
94
|
+
|
|
95
|
+
# Convert to correct data type
|
|
96
|
+
alphas_tensor = backend.cast(alphas_tensor, dtype=dtypestr)
|
|
97
|
+
if len(betas_tensor) > 0:
|
|
98
|
+
betas_tensor = backend.cast(betas_tensor, dtype=dtypestr)
|
|
99
|
+
|
|
100
|
+
# Construct diagonal and off-diagonal parts
|
|
101
|
+
diag_part = backend.diagflat(alphas_tensor)
|
|
102
|
+
if len(betas_tensor) > 0:
|
|
103
|
+
off_diag_part = backend.diagflat(betas_tensor, k=1)
|
|
104
|
+
projected_hamiltonian = (
|
|
105
|
+
diag_part + off_diag_part + backend.transpose(off_diag_part)
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
projected_hamiltonian = diag_part
|
|
109
|
+
|
|
110
|
+
return basis_matrix, projected_hamiltonian
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def krylov_evol(
|
|
114
|
+
hamiltonian: Tensor,
|
|
115
|
+
initial_state: Tensor,
|
|
116
|
+
time_points: Tensor,
|
|
117
|
+
subspace_dimension: int,
|
|
118
|
+
callback: Optional[Callable[[Any], Any]] = None,
|
|
119
|
+
) -> Any:
|
|
120
|
+
"""
|
|
121
|
+
Perform quantum state time evolution using Krylov subspace method.
|
|
122
|
+
|
|
123
|
+
:param hamiltonian: Sparse or dense Hamiltonian matrix
|
|
124
|
+
:type hamiltonian: Tensor
|
|
125
|
+
:param initial_state: Initial quantum state
|
|
126
|
+
:type initial_state: Tensor
|
|
127
|
+
:param time_points: List of time points
|
|
128
|
+
:type time_points: Tensor
|
|
129
|
+
:param subspace_dimension: Krylov subspace dimension
|
|
130
|
+
:type subspace_dimension: int
|
|
131
|
+
:param callback: Optional callback function applied to quantum state at
|
|
132
|
+
each evolution time point, return some observables
|
|
133
|
+
:type callback: Optional[Callable[[Any], Any]], optional
|
|
134
|
+
:return: List of evolved quantum states, or list of callback function results
|
|
135
|
+
(if callback provided)
|
|
136
|
+
:rtype: Any
|
|
137
|
+
"""
|
|
138
|
+
# TODO(@refraction-ray): stable and efficient AD is to be investigated
|
|
139
|
+
basis_matrix, projected_hamiltonian = lanczos_iteration(
|
|
140
|
+
hamiltonian, initial_state, subspace_dimension
|
|
141
|
+
)
|
|
142
|
+
initial_state = backend.cast(initial_state, dtypestr)
|
|
143
|
+
# Project initial state to Krylov subspace: |psi_proj> = V_m^† |psi(0)>
|
|
144
|
+
projected_state = backend.matvec(
|
|
145
|
+
backend.conj(backend.transpose(basis_matrix)), initial_state
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Perform spectral decomposition of projected Hamiltonian: T_m = U D U^†
|
|
149
|
+
eigenvalues, eigenvectors = backend.eigh(projected_hamiltonian)
|
|
150
|
+
eigenvalues = backend.cast(eigenvalues, dtypestr)
|
|
151
|
+
time_points = backend.convert_to_tensor(time_points)
|
|
152
|
+
time_points = backend.cast(time_points, dtypestr)
|
|
153
|
+
|
|
154
|
+
# Transform projected state to eigenbasis: |psi_coeff> = U^† |psi_proj>
|
|
155
|
+
eigenvectors_projected_state = backend.matvec(
|
|
156
|
+
backend.conj(backend.transpose(eigenvectors)), projected_state
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Calculate exp(-i*projected_H*t) * projected_state
|
|
160
|
+
results = []
|
|
161
|
+
for t in time_points:
|
|
162
|
+
# Calculate exp(-i*eigenvalues*t)
|
|
163
|
+
exp_diagonal = backend.exp(-1j * eigenvalues * t)
|
|
164
|
+
|
|
165
|
+
# Evolve state in eigenbasis: |psi_evolved_coeff> = exp(-i*D*t) |psi_coeff>
|
|
166
|
+
evolved_projected_coeff = exp_diagonal * eigenvectors_projected_state
|
|
167
|
+
|
|
168
|
+
# Transform back to eigenbasis: |psi_evolved_proj> = U |psi_evolved_coeff>
|
|
169
|
+
evolved_projected = backend.matvec(eigenvectors, evolved_projected_coeff)
|
|
170
|
+
|
|
171
|
+
# Transform back to original basis: |psi(t)> = V_m |psi_evolved_proj>
|
|
172
|
+
evolved_state = backend.matvec(basis_matrix, evolved_projected)
|
|
173
|
+
|
|
174
|
+
# Apply callback function if provided
|
|
175
|
+
if callback is not None:
|
|
176
|
+
result = callback(evolved_state)
|
|
177
|
+
else:
|
|
178
|
+
result = evolved_state
|
|
179
|
+
|
|
180
|
+
results.append(result)
|
|
181
|
+
|
|
182
|
+
return backend.stack(results)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def hamiltonian_evol(
|
|
186
|
+
tlist: Tensor,
|
|
187
|
+
h: Tensor,
|
|
188
|
+
psi0: Tensor,
|
|
189
|
+
callback: Optional[Callable[..., Any]] = None,
|
|
190
|
+
) -> Tensor:
|
|
191
|
+
"""
|
|
192
|
+
Fast implementation of time independent Hamiltonian evolution using eigendecomposition.
|
|
193
|
+
By default, performs imaginary time evolution.
|
|
194
|
+
|
|
195
|
+
:param tlist: Time points for evolution
|
|
196
|
+
:type tlist: Tensor
|
|
197
|
+
:param h: Time-independent Hamiltonian matrix
|
|
198
|
+
:type h: Tensor
|
|
199
|
+
:param psi0: Initial state vector
|
|
200
|
+
:type psi0: Tensor
|
|
201
|
+
:param callback: Optional function to process state at each time point
|
|
202
|
+
:type callback: Optional[Callable[..., Any]], optional
|
|
203
|
+
:return: Evolution results at each time point. If callback is None, returns state vectors;
|
|
204
|
+
otherwise returns callback results
|
|
205
|
+
:rtype: Tensor
|
|
206
|
+
|
|
207
|
+
:Example:
|
|
208
|
+
|
|
209
|
+
>>> import tensorcircuit as tc
|
|
210
|
+
>>> import numpy as np
|
|
211
|
+
>>> # Define a simple 2-qubit Hamiltonian
|
|
212
|
+
>>> h = tc.array_to_tensor([
|
|
213
|
+
... [1.0, 0.0, 0.0, 0.0],
|
|
214
|
+
... [0.0, -1.0, 2.0, 0.0],
|
|
215
|
+
... [0.0, 2.0, -1.0, 0.0],
|
|
216
|
+
... [0.0, 0.0, 0.0, 1.0]
|
|
217
|
+
... ])
|
|
218
|
+
>>> # Initial state |00⟩
|
|
219
|
+
>>> psi0 = tc.array_to_tensor([1.0, 0.0, 0.0, 0.0])
|
|
220
|
+
>>> # Evolution times
|
|
221
|
+
>>> times = tc.array_to_tensor([0.0, 0.5, 1.0])
|
|
222
|
+
>>> # Evolve and get states
|
|
223
|
+
>>> states = tc.experimental.hamiltonian_evol(times, h, psi0)
|
|
224
|
+
>>> print(states.shape) # (3, 4)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
Note:
|
|
228
|
+
1. The Hamiltonian must be time-independent
|
|
229
|
+
2. For time-dependent Hamiltonians, use ``evol_local`` or ``evol_global`` instead
|
|
230
|
+
3. The evolution is performed in imaginary time by default (factor -t in exponential)
|
|
231
|
+
4. The state is automatically normalized at each time point
|
|
232
|
+
"""
|
|
233
|
+
psi0 = backend.cast(psi0, dtypestr)
|
|
234
|
+
es, u = backend.eigh(h)
|
|
235
|
+
u = backend.cast(u, dtypestr)
|
|
236
|
+
utpsi0 = backend.reshape(
|
|
237
|
+
backend.transpose(u) @ backend.reshape(psi0, [-1, 1]), [-1]
|
|
238
|
+
)
|
|
239
|
+
es = backend.cast(es, dtypestr)
|
|
240
|
+
tlist = backend.cast(backend.convert_to_tensor(tlist), dtypestr)
|
|
241
|
+
|
|
242
|
+
@backend.jit
|
|
243
|
+
def _evol(t: Tensor) -> Tensor:
|
|
244
|
+
ebetah_utpsi0 = backend.exp(-t * es) * utpsi0
|
|
245
|
+
psi_exact = backend.conj(u) @ backend.reshape(ebetah_utpsi0, [-1, 1])
|
|
246
|
+
psi_exact = backend.reshape(psi_exact, [-1])
|
|
247
|
+
psi_exact = psi_exact / backend.norm(psi_exact)
|
|
248
|
+
if callback is None:
|
|
249
|
+
return psi_exact
|
|
250
|
+
return callback(psi_exact)
|
|
251
|
+
|
|
252
|
+
return backend.stack([_evol(t) for t in tlist])
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
ed_evol = hamiltonian_evol
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def evol_local(
|
|
259
|
+
c: Circuit,
|
|
260
|
+
index: Sequence[int],
|
|
261
|
+
h_fun: Callable[..., Tensor],
|
|
262
|
+
t: float,
|
|
263
|
+
*args: Any,
|
|
264
|
+
**solver_kws: Any,
|
|
265
|
+
) -> Circuit:
|
|
266
|
+
"""
|
|
267
|
+
ode evolution of time dependent Hamiltonian on circuit of given indices
|
|
268
|
+
[only jax backend support for now]
|
|
269
|
+
|
|
270
|
+
:param c: _description_
|
|
271
|
+
:type c: Circuit
|
|
272
|
+
:param index: qubit sites to evolve
|
|
273
|
+
:type index: Sequence[int]
|
|
274
|
+
:param h_fun: h_fun should return a dense Hamiltonian matrix
|
|
275
|
+
with input arguments time and *args
|
|
276
|
+
:type h_fun: Callable[..., Tensor]
|
|
277
|
+
:param t: evolution time
|
|
278
|
+
:type t: float
|
|
279
|
+
:return: _description_
|
|
280
|
+
:rtype: Circuit
|
|
281
|
+
"""
|
|
282
|
+
from jax.experimental.ode import odeint
|
|
283
|
+
|
|
284
|
+
s = c.state()
|
|
285
|
+
n = c._nqubits
|
|
286
|
+
l = len(index)
|
|
287
|
+
|
|
288
|
+
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
289
|
+
y = backend.reshape2(y)
|
|
290
|
+
y = Gate(y)
|
|
291
|
+
h = -1.0j * h_fun(t, *args)
|
|
292
|
+
h = backend.reshape2(h)
|
|
293
|
+
h = Gate(h)
|
|
294
|
+
edges = []
|
|
295
|
+
for i in range(n):
|
|
296
|
+
if i not in index:
|
|
297
|
+
edges.append(y[i])
|
|
298
|
+
else:
|
|
299
|
+
j = index.index(i)
|
|
300
|
+
edges.append(h[j])
|
|
301
|
+
h[j + l] ^ y[i]
|
|
302
|
+
y = contractor([y, h], output_edge_order=edges)
|
|
303
|
+
return backend.reshape(y.tensor, [-1])
|
|
304
|
+
|
|
305
|
+
ts = backend.stack([0.0, t])
|
|
306
|
+
ts = backend.cast(ts, dtype=rdtypestr)
|
|
307
|
+
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
308
|
+
return type(c)(n, inputs=s1[-1])
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
ode_evol_local = evol_local
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def evol_global(
|
|
315
|
+
c: Circuit, h_fun: Callable[..., Tensor], t: float, *args: Any, **solver_kws: Any
|
|
316
|
+
) -> Circuit:
|
|
317
|
+
"""
|
|
318
|
+
ode evolution of time dependent Hamiltonian on circuit of all qubits
|
|
319
|
+
[only jax backend support for now]
|
|
320
|
+
|
|
321
|
+
:param c: _description_
|
|
322
|
+
:type c: Circuit
|
|
323
|
+
:param h_fun: h_fun should return a **SPARSE** Hamiltonian matrix
|
|
324
|
+
with input arguments time and *args
|
|
325
|
+
:type h_fun: Callable[..., Tensor]
|
|
326
|
+
:param t: _description_
|
|
327
|
+
:type t: float
|
|
328
|
+
:return: _description_
|
|
329
|
+
:rtype: Circuit
|
|
330
|
+
"""
|
|
331
|
+
from jax.experimental.ode import odeint
|
|
332
|
+
|
|
333
|
+
s = c.state()
|
|
334
|
+
n = c._nqubits
|
|
335
|
+
|
|
336
|
+
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
337
|
+
h = -1.0j * h_fun(t, *args)
|
|
338
|
+
return backend.sparse_dense_matmul(h, y)
|
|
339
|
+
|
|
340
|
+
ts = backend.stack([0.0, t])
|
|
341
|
+
ts = backend.cast(ts, dtype=rdtypestr)
|
|
342
|
+
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
343
|
+
return type(c)(n, inputs=s1[-1])
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
ode_evol_global = evol_global
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tensorcircuit-nightly
|
|
3
|
-
Version: 1.3.0.
|
|
3
|
+
Version: 1.3.0.dev20250729
|
|
4
4
|
Summary: nightly release for tensorcircuit
|
|
5
5
|
Home-page: https://github.com/refraction-ray/tensorcircuit-dev
|
|
6
6
|
Author: TensorCircuit Authors
|
|
@@ -70,7 +70,7 @@ TensorCircuit-NG is the actively maintained official version and a [fully compat
|
|
|
70
70
|
|
|
71
71
|
Please begin with [Quick Start](/docs/source/quickstart.rst) in the [full documentation](https://tensorcircuit-ng.readthedocs.io/).
|
|
72
72
|
|
|
73
|
-
For more information on software usage, sota algorithm implementation and engineer paradigm demonstration, please refer to 80+ [example scripts](/examples) and 30+ [tutorial notebooks](https://tensorcircuit-ng.readthedocs.io/en/latest/#tutorials). API docstrings and test cases in [tests](/tests) are also informative. One can also refer to tensorcircuit-ng [
|
|
73
|
+
For more information on software usage, sota algorithm implementation and engineer paradigm demonstration, please refer to 80+ [example scripts](/examples) and 30+ [tutorial notebooks](https://tensorcircuit-ng.readthedocs.io/en/latest/#tutorials). API docstrings and test cases in [tests](/tests) are also informative. One can also refer to AI-native docs for tensorcircuit-ng: [Devin Deepwiki](https://deepwiki.com/tensorcircuit/tensorcircuit-ng) and [Context7 MCP](https://context7.com/tensorcircuit/tensorcircuit-ng).
|
|
74
74
|
|
|
75
75
|
For beginners, please refer to [quantum computing lectures with TC-NG](https://github.com/sxzgroup/qc_lecture) to learn both quantum computing basics and representative usage of TensorCircuit-NG.
|
|
76
76
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
tensorcircuit/__init__.py,sha256=
|
|
1
|
+
tensorcircuit/__init__.py,sha256=GNg45VO8J-m5ApDmmQbFvTPIzXvBpREXDg5MiVgCqqI,2055
|
|
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
|
|
@@ -7,7 +7,7 @@ tensorcircuit/channels.py,sha256=CFQxWI-JmkIxexslCBdjp_RSxUbHs6eAJv4LvlXXXCY,286
|
|
|
7
7
|
tensorcircuit/circuit.py,sha256=mE4b_9xRu3ydoB8iDffdx35V9GZLhAQD_tkjZDLnLjg,39105
|
|
8
8
|
tensorcircuit/cons.py,sha256=uYKBeYKkDoJEqJTNrOZPRM31tBtkqe5aAg8GtVidJ1Y,33014
|
|
9
9
|
tensorcircuit/densitymatrix.py,sha256=VqMBnWCxO5-OsOp6LOdc5RS2AzmB3U4-w40Vn_lqygo,14865
|
|
10
|
-
tensorcircuit/experimental.py,sha256=
|
|
10
|
+
tensorcircuit/experimental.py,sha256=TGK4FaS6TS_ZhtjcIZgYVuAkGdRW50LN0DdXp-h4bos,29906
|
|
11
11
|
tensorcircuit/fgs.py,sha256=pzaZuzPIFPpfr5Z-UsBQ_Yp0x7mbSM2sUc4dO2SUmVs,49543
|
|
12
12
|
tensorcircuit/gates.py,sha256=x-wA7adVpP7o0AQLt_xYUScFKj8tU_wUOV2mR1GyrPc,29322
|
|
13
13
|
tensorcircuit/keras.py,sha256=5OF4dfhEeS8sRYglpqYtQsWPeqp7uK0i7-P-6RRJ7zQ,10126
|
|
@@ -18,6 +18,7 @@ tensorcircuit/quantum.py,sha256=LNkIv5cJ2KG6puC18zTuXi-5cojW1Tnz-N-WjZ0Qu5Q,9021
|
|
|
18
18
|
tensorcircuit/shadows.py,sha256=6XmWNubbuaxFNvZVWu-RXd0lN9Jkk-xwong_K8o8_KE,17014
|
|
19
19
|
tensorcircuit/simplify.py,sha256=O11G3UYiVAc30GOfwXXmhLXwGZrQ8OVwLTMQMZp_XBc,9414
|
|
20
20
|
tensorcircuit/stabilizercircuit.py,sha256=yNqcEKtYzRYrgqGil8QEyKN4OEMp9g6uOG2zuRaU8uc,15465
|
|
21
|
+
tensorcircuit/timeevol.py,sha256=LWKOw4Y4CUtUO4a_72BVzYPH26PbubGYOaJlXkmA350,11439
|
|
21
22
|
tensorcircuit/torchnn.py,sha256=z_QpM0QC3mydGyWpyp877j-tSFCPyzynCwqrTWaw-IA,4637
|
|
22
23
|
tensorcircuit/translation.py,sha256=VnU7DnYmbk1cWjqa7N68WNLNDn3DwENrMzmbG4_CQco,28611
|
|
23
24
|
tensorcircuit/utils.py,sha256=nEDR1wTh1WF_yV6UyZYlifqOPWdKk_Krr4HjhrWHnGQ,7228
|
|
@@ -39,7 +40,7 @@ tensorcircuit/applications/physics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
|
|
|
39
40
|
tensorcircuit/applications/physics/baseline.py,sha256=RWrzMGnC0PtmpYSFkvCE7r1llR88gncXuCakAAhFE-w,1775
|
|
40
41
|
tensorcircuit/applications/physics/fss.py,sha256=ny3U9ZDmT459PXjA1oUGfarBOlSKSy6fs04vD9s1XH4,3633
|
|
41
42
|
tensorcircuit/backends/__init__.py,sha256=WiUmbUFzM29w3hKfhuKxVUk3PpqDFiXf4za9g0ctpZA,80
|
|
42
|
-
tensorcircuit/backends/abstract_backend.py,sha256=
|
|
43
|
+
tensorcircuit/backends/abstract_backend.py,sha256=tzhOFZSOkZ_iraMvY7g84VbWBpYcELX1O0bhUipZ7YQ,59131
|
|
43
44
|
tensorcircuit/backends/backend_factory.py,sha256=Z0aQ-RnxOnQzp-SRw8sefAH8XyBSlj2NXZwOlHinbfY,1713
|
|
44
45
|
tensorcircuit/backends/cupy_backend.py,sha256=4vgO3lnQnsvWL5hukhskjJp37EAHqio6z6TVXTQcdjs,15077
|
|
45
46
|
tensorcircuit/backends/jax_backend.py,sha256=dkDQ380CJHIdlt1fZvlN_g8DIowWPEcTTV_XBcs0YB0,26088
|
|
@@ -47,7 +48,7 @@ tensorcircuit/backends/jax_ops.py,sha256=o7tLlQMRnaKWcr5rVnOMqwG6KZVpR8M8ryNQ-ce
|
|
|
47
48
|
tensorcircuit/backends/numpy_backend.py,sha256=sd1migp_E2FWjchvOeYRuyM47yexegT2_SW_ukSYSF8,14171
|
|
48
49
|
tensorcircuit/backends/pytorch_backend.py,sha256=yhfZSrm99yNW-dmijk8t6zAkbVgLRd4b_aIWKrpT7bY,24230
|
|
49
50
|
tensorcircuit/backends/pytorch_ops.py,sha256=lLxpK6OqfpVwifyFlgsqhpnt-oIn4R5paPMVg51WaW0,3826
|
|
50
|
-
tensorcircuit/backends/tensorflow_backend.py,sha256=
|
|
51
|
+
tensorcircuit/backends/tensorflow_backend.py,sha256=N8iL2P76ZRp3USJSyFdZ2LWG8muXYe1kOuengv1ijXo,36444
|
|
51
52
|
tensorcircuit/backends/tf_ops.py,sha256=FJwDU7LhZrt0VUIx12DJU0gZnWhMv7B7r9sAKG710As,3378
|
|
52
53
|
tensorcircuit/cloud/__init__.py,sha256=n0Lx07GYF6YbdIa6AJCLJk4zlAm5CqaeHszvkxxuoI4,139
|
|
53
54
|
tensorcircuit/cloud/abstraction.py,sha256=6aSxbz0MP21jBVdFbSMrvJPLQH117vGz9sSHbMFoodE,14582
|
|
@@ -85,7 +86,7 @@ tensorcircuit/templates/graphs.py,sha256=cPYrxjoem0xZ-Is9dZKAvEzWZL_FejfIRiCEOTA
|
|
|
85
86
|
tensorcircuit/templates/hamiltonians.py,sha256=Ag8djD6lckTeU7I99gCbXiQAb2VYqzm_p7-hpXo-5u4,5554
|
|
86
87
|
tensorcircuit/templates/lattice.py,sha256=F35ebANk0DSmSHLR0-Q_hUbcznyCmZjb4fKmvCMywmA,58575
|
|
87
88
|
tensorcircuit/templates/measurements.py,sha256=pzc5Aa9S416Ilg4aOY77Z6ZhUlYcXnAkQNQFTuHjFFs,10943
|
|
88
|
-
tensorcircuit_nightly-1.3.0.
|
|
89
|
+
tensorcircuit_nightly-1.3.0.dev20250729.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
89
90
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
91
|
tests/conftest.py,sha256=J9nHlLE3Zspz1rMyzadEuBWhaS5I4Q9sq0lnWybcdIA,1457
|
|
91
92
|
tests/test_backends.py,sha256=rClxb2gyAoGeXd_ZYVSAJ0zEvJ7z_2btAeFM_Iy_wwY,33925
|
|
@@ -102,7 +103,7 @@ tests/test_hamiltonians.py,sha256=E0E5ABhUeG7XLMLRkb3AIAPi7aJgnIeMWTgqzF1Q6yc,57
|
|
|
102
103
|
tests/test_interfaces.py,sha256=iJPmes8S8HkA9_PGjsu4Ike-vCXYyS1EMgnNKKXDNaU,16938
|
|
103
104
|
tests/test_keras.py,sha256=U453jukavmx0RMeTSDEgPzrNdHNEfK1CW0CqO3XCNKo,4841
|
|
104
105
|
tests/test_lattice.py,sha256=_ptDVK3EhS-X5fCQWiP8sHk3azdyGFuwqg6KMkBTkDE,65789
|
|
105
|
-
tests/test_miscs.py,sha256=
|
|
106
|
+
tests/test_miscs.py,sha256=4fXKsW0kYu2JYO0iGlwWLAYlkFD1rfeVc4xG4Zjn5FQ,8935
|
|
106
107
|
tests/test_mpscircuit.py,sha256=mDXX8oQeFeHr_PdZvwqyDs_tVcVAqLmCERqlTAU7590,10552
|
|
107
108
|
tests/test_noisemodel.py,sha256=UYoMtCjwDaB-CCn5kLosofz-qTMiY4KGAFBjVtqqLPE,5637
|
|
108
109
|
tests/test_qaoa.py,sha256=hEcC_XVmKBGt9XgUGtbTO8eQQK4mjorgTIrfqZCeQls,2616
|
|
@@ -114,9 +115,10 @@ tests/test_shadows.py,sha256=1T3kJesVJ5XfZrSncL80xdq-taGCSnTDF3eL15UlavY,5160
|
|
|
114
115
|
tests/test_simplify.py,sha256=35tbOu1QANsPvY1buLwNhqPnMkBOsnBtHn82qaukmgI,1175
|
|
115
116
|
tests/test_stabilizer.py,sha256=MivuZ5pY7GOcEPTanhtrflXostyLBToHyjfPqCU0tG0,5450
|
|
116
117
|
tests/test_templates.py,sha256=Xm9otFFaaBWG9TZpgJ-nNh9MBfRipTzFWL8fBOnie2k,7192
|
|
118
|
+
tests/test_timeevol.py,sha256=b5fsUzW3kfLZV909jnJpH9vCBz5fefONOUSrm2N6Nxg,7886
|
|
117
119
|
tests/test_torchnn.py,sha256=CHLTfWkF7Ses5_XnGFN_uv_JddfgenFEFzaDtSH8XYU,2848
|
|
118
120
|
tests/test_van.py,sha256=kAWz860ivlb5zAJuYpzuBe27qccT-Yf0jatf5uXtTo4,3163
|
|
119
|
-
tensorcircuit_nightly-1.3.0.
|
|
120
|
-
tensorcircuit_nightly-1.3.0.
|
|
121
|
-
tensorcircuit_nightly-1.3.0.
|
|
122
|
-
tensorcircuit_nightly-1.3.0.
|
|
121
|
+
tensorcircuit_nightly-1.3.0.dev20250729.dist-info/METADATA,sha256=MijyspDQzOzMCKoGxFN50a89pOhICnhrpUeXdPASpQg,34922
|
|
122
|
+
tensorcircuit_nightly-1.3.0.dev20250729.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
123
|
+
tensorcircuit_nightly-1.3.0.dev20250729.dist-info/top_level.txt,sha256=O_Iqeh2x02lasEYMI9iyPNNNtMzcpg5qvwMOkZQ7n4A,20
|
|
124
|
+
tensorcircuit_nightly-1.3.0.dev20250729.dist-info/RECORD,,
|
tests/test_miscs.py
CHANGED
|
@@ -226,36 +226,6 @@ def test_finite_difference_tf(tfb):
|
|
|
226
226
|
np.testing.assert_allclose(g2, g4, atol=1e-5)
|
|
227
227
|
|
|
228
228
|
|
|
229
|
-
def test_evol(jaxb):
|
|
230
|
-
def h_square(t, b):
|
|
231
|
-
return (tc.backend.sign(t - 1.0) + 1) / 2 * b * tc.gates.x().tensor
|
|
232
|
-
|
|
233
|
-
c = tc.Circuit(3)
|
|
234
|
-
c.x(0)
|
|
235
|
-
c.cx(0, 1)
|
|
236
|
-
c.h(2)
|
|
237
|
-
c = experimental.evol_local(
|
|
238
|
-
c, [1], h_square, 2.0, tc.backend.convert_to_tensor(0.2)
|
|
239
|
-
)
|
|
240
|
-
c.rx(1, theta=np.pi - 0.4)
|
|
241
|
-
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
242
|
-
|
|
243
|
-
ixi = tc.quantum.PauliStringSum2COO([[0, 1, 0]])
|
|
244
|
-
|
|
245
|
-
def h_square_sparse(t, b):
|
|
246
|
-
return (tc.backend.sign(t - 1.0) + 1) / 2 * b * ixi
|
|
247
|
-
|
|
248
|
-
c = tc.Circuit(3)
|
|
249
|
-
c.x(0)
|
|
250
|
-
c.cx(0, 1)
|
|
251
|
-
c.h(2)
|
|
252
|
-
c = experimental.evol_global(
|
|
253
|
-
c, h_square_sparse, 2.0, tc.backend.convert_to_tensor(0.2)
|
|
254
|
-
)
|
|
255
|
-
c.rx(1, theta=np.pi - 0.4)
|
|
256
|
-
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
257
|
-
|
|
258
|
-
|
|
259
229
|
def test_energy_baseline():
|
|
260
230
|
print(TFIM1Denergy(10))
|
|
261
231
|
print(Heisenberg1Denergy(10))
|
tests/test_timeevol.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import pytest
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pytest_lazyfixture import lazy_fixture as lf
|
|
6
|
+
|
|
7
|
+
thisfile = os.path.abspath(__file__)
|
|
8
|
+
modulepath = os.path.dirname(os.path.dirname(thisfile))
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, modulepath)
|
|
11
|
+
import tensorcircuit as tc
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_ode_evol(jaxb):
|
|
15
|
+
def h_square(t, b):
|
|
16
|
+
return (tc.backend.sign(t - 1.0) + 1) / 2 * b * tc.gates.x().tensor
|
|
17
|
+
|
|
18
|
+
c = tc.Circuit(3)
|
|
19
|
+
c.x(0)
|
|
20
|
+
c.cx(0, 1)
|
|
21
|
+
c.h(2)
|
|
22
|
+
c = tc.timeevol.ode_evol_local(
|
|
23
|
+
c, [1], h_square, 2.0, tc.backend.convert_to_tensor(0.2)
|
|
24
|
+
)
|
|
25
|
+
c.rx(1, theta=np.pi - 0.4)
|
|
26
|
+
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
27
|
+
|
|
28
|
+
ixi = tc.quantum.PauliStringSum2COO([[0, 1, 0]])
|
|
29
|
+
|
|
30
|
+
def h_square_sparse(t, b):
|
|
31
|
+
return (tc.backend.sign(t - 1.0) + 1) / 2 * b * ixi
|
|
32
|
+
|
|
33
|
+
c = tc.Circuit(3)
|
|
34
|
+
c.x(0)
|
|
35
|
+
c.cx(0, 1)
|
|
36
|
+
c.h(2)
|
|
37
|
+
c = tc.timeevol.ode_evol_global(
|
|
38
|
+
c, h_square_sparse, 2.0, tc.backend.convert_to_tensor(0.2)
|
|
39
|
+
)
|
|
40
|
+
c.rx(1, theta=np.pi - 0.4)
|
|
41
|
+
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
45
|
+
def test_hamiltonian_evol_basic(backend):
|
|
46
|
+
"""Test basic functionality of hamiltonian_evol with a simple 2-qubit Hamiltonian"""
|
|
47
|
+
# Define a simple 2-qubit Hamiltonian
|
|
48
|
+
h = tc.backend.convert_to_tensor(
|
|
49
|
+
[
|
|
50
|
+
[1.0, 0.0, 0.0, 0.0],
|
|
51
|
+
[0.0, -1.0, 2.0, 0.0],
|
|
52
|
+
[0.0, 2.0, -1.0, 0.0],
|
|
53
|
+
[0.0, 0.0, 0.0, 1.0],
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Initial state |00⟩
|
|
58
|
+
psi0 = tc.backend.convert_to_tensor([1.0, 0.0, 0.0, 0.0])
|
|
59
|
+
|
|
60
|
+
# Evolution times
|
|
61
|
+
times = 1.0j * tc.backend.cast(
|
|
62
|
+
tc.backend.convert_to_tensor([0.0, 0.5, 1.0]), tc.dtypestr
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Evolve and get states
|
|
66
|
+
states = tc.timeevol.hamiltonian_evol(times, h, psi0)
|
|
67
|
+
|
|
68
|
+
# Check output shape
|
|
69
|
+
assert states.shape == (3, 4)
|
|
70
|
+
|
|
71
|
+
# At t=0, state should be the initial state (normalized)
|
|
72
|
+
np.testing.assert_allclose(states[0], psi0, atol=1e-5)
|
|
73
|
+
|
|
74
|
+
# All states should be normalized
|
|
75
|
+
for state in states:
|
|
76
|
+
norm = tc.backend.norm(state)
|
|
77
|
+
np.testing.assert_allclose(norm, 1.0, atol=1e-5)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
81
|
+
def test_hamiltonian_evol_with_callback(backend):
|
|
82
|
+
"""Test hamiltonian_evol with a callback function"""
|
|
83
|
+
# Define a simple Hamiltonian
|
|
84
|
+
h = tc.backend.convert_to_tensor([[1.0, 0.0], [0.0, -1.0]])
|
|
85
|
+
|
|
86
|
+
# Initial state
|
|
87
|
+
psi0 = tc.backend.convert_to_tensor([1.0, 1.0]) / np.sqrt(2)
|
|
88
|
+
|
|
89
|
+
# Evolution times
|
|
90
|
+
times = 1.0j * tc.backend.cast(
|
|
91
|
+
tc.backend.convert_to_tensor([0.0, 1.0, 2.0]), tc.dtypestr
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Define callback to compute expectation value of Pauli Z
|
|
95
|
+
def callback(state):
|
|
96
|
+
# Z operator
|
|
97
|
+
c = tc.Circuit(1, inputs=state)
|
|
98
|
+
# Compute <psi|Z|psi>
|
|
99
|
+
return tc.backend.real(c.expectation_ps(z=[0]))
|
|
100
|
+
|
|
101
|
+
# Evolve with callback
|
|
102
|
+
results = tc.timeevol.hamiltonian_evol(times, h, psi0, callback)
|
|
103
|
+
|
|
104
|
+
# Check output shape - should be scalar for each time point
|
|
105
|
+
assert results.shape == (3,)
|
|
106
|
+
|
|
107
|
+
# At t=0, for |+⟩ state, <Z> should be 0
|
|
108
|
+
np.testing.assert_allclose(results[0], 0.0, atol=1e-5)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
112
|
+
def test_hamiltonian_evol_imaginary_time(backend):
|
|
113
|
+
"""Test that hamiltonian_evol performs imaginary time evolution by default"""
|
|
114
|
+
# Define a Hamiltonian
|
|
115
|
+
h = tc.backend.convert_to_tensor([[2.0, 0.0], [0.0, 1.0]])
|
|
116
|
+
|
|
117
|
+
# Initial state with equal superposition
|
|
118
|
+
psi0 = tc.backend.convert_to_tensor([1.0, 1.0]) / np.sqrt(2)
|
|
119
|
+
|
|
120
|
+
# Large time to see ground state dominance
|
|
121
|
+
times = tc.backend.convert_to_tensor([0.0, 10.0])
|
|
122
|
+
|
|
123
|
+
# Evolve
|
|
124
|
+
states = tc.timeevol.hamiltonian_evol(times, h, psi0)
|
|
125
|
+
|
|
126
|
+
# Ground state is |1⟩ (eigenvalue 1.0), so after long imaginary time
|
|
127
|
+
# evolution, we should approach this state
|
|
128
|
+
expected_ground_state = tc.backend.convert_to_tensor([0.0, 1.0])
|
|
129
|
+
np.testing.assert_allclose(states[-1], expected_ground_state, atol=1e-3)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
133
|
+
def test_krylov_evol_heisenberg_6_sites(backend):
|
|
134
|
+
"""Test krylov_evol with Heisenberg Hamiltonian on 6 sites"""
|
|
135
|
+
n = 6
|
|
136
|
+
# Create a 1D chain graph
|
|
137
|
+
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
138
|
+
|
|
139
|
+
# Generate Heisenberg Hamiltonian
|
|
140
|
+
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
|
|
141
|
+
print(h.dtype)
|
|
142
|
+
# Initial state - all spins up except last one down
|
|
143
|
+
psi0 = np.zeros((2**n,))
|
|
144
|
+
psi0[62] = 1.0
|
|
145
|
+
# State with pattern: up, up, up, up, up, down (111110 in binary = 62 in decimal)
|
|
146
|
+
|
|
147
|
+
# Normalize initial state
|
|
148
|
+
psi0 = psi0 / tc.backend.norm(psi0)
|
|
149
|
+
|
|
150
|
+
# Evolution times
|
|
151
|
+
times = tc.backend.convert_to_tensor([0.0, 0.5, 1.0])
|
|
152
|
+
|
|
153
|
+
# Perform Krylov evolution
|
|
154
|
+
states = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=10)
|
|
155
|
+
|
|
156
|
+
# Check output shape
|
|
157
|
+
assert states.shape == (3, 2**n)
|
|
158
|
+
|
|
159
|
+
# At t=0, state should be the initial state
|
|
160
|
+
np.testing.assert_allclose(states[0], psi0, atol=1e-5)
|
|
161
|
+
|
|
162
|
+
# All states should be normalized
|
|
163
|
+
for state in states:
|
|
164
|
+
norm = tc.backend.norm(state)
|
|
165
|
+
np.testing.assert_allclose(norm, 1.0, atol=1e-5)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
169
|
+
def test_krylov_evol_heisenberg_8_sites(backend):
|
|
170
|
+
"""Test krylov_evol with Heisenberg Hamiltonian on 8 sites with callback"""
|
|
171
|
+
n = 8
|
|
172
|
+
# Create a 1D chain graph
|
|
173
|
+
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
174
|
+
|
|
175
|
+
# Generate Heisenberg Hamiltonian
|
|
176
|
+
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
177
|
+
|
|
178
|
+
# Initial Neel state (alternating up and down spins)
|
|
179
|
+
c = tc.Circuit(n)
|
|
180
|
+
c.x([i for i in range(n // 2)])
|
|
181
|
+
psi0 = c.state()
|
|
182
|
+
|
|
183
|
+
# Evolution times
|
|
184
|
+
times = tc.backend.convert_to_tensor([0.0, 0.2, 0.4])
|
|
185
|
+
|
|
186
|
+
# Define callback to compute total magnetization
|
|
187
|
+
def total_magnetization(state):
|
|
188
|
+
# Calculate sum of <Z_i> for all sites
|
|
189
|
+
c = tc.Circuit(n, inputs=state)
|
|
190
|
+
total_z = 0.0
|
|
191
|
+
for i in range(n):
|
|
192
|
+
total_z += c.expectation_ps(z=[i])
|
|
193
|
+
return tc.backend.real(total_z)
|
|
194
|
+
|
|
195
|
+
# Perform Krylov evolution with callback
|
|
196
|
+
results = tc.timeevol.krylov_evol(
|
|
197
|
+
h, psi0, times, subspace_dimension=12, callback=total_magnetization
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Check output shape - should be scalar for each time point
|
|
201
|
+
assert results.shape == (3,)
|
|
202
|
+
|
|
203
|
+
# At t=0, for Neel state, total magnetization should be 0
|
|
204
|
+
np.testing.assert_allclose(results[0], 0.0, atol=1e-5)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
208
|
+
def test_krylov_evol_subspace_accuracy(backend):
|
|
209
|
+
"""Test accuracy of krylov_evol with different subspace dimensions"""
|
|
210
|
+
n = 6
|
|
211
|
+
# Create a 1D chain graph
|
|
212
|
+
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
213
|
+
|
|
214
|
+
# Generate Heisenberg Hamiltonian
|
|
215
|
+
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
216
|
+
|
|
217
|
+
# Initial domain wall state
|
|
218
|
+
c = tc.Circuit(n)
|
|
219
|
+
c.x([i + n // 2 for i in range(n // 2)])
|
|
220
|
+
psi0 = c.state()
|
|
221
|
+
|
|
222
|
+
# Evolution time
|
|
223
|
+
times = tc.backend.convert_to_tensor([1.0])
|
|
224
|
+
|
|
225
|
+
# Compare different subspace dimensions
|
|
226
|
+
state_small = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=6)
|
|
227
|
+
state_large = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=16)
|
|
228
|
+
|
|
229
|
+
# Both should be normalized
|
|
230
|
+
norm_small = tc.backend.norm(state_small[0])
|
|
231
|
+
norm_large = tc.backend.norm(state_large[0])
|
|
232
|
+
np.testing.assert_allclose(norm_small, 1.0, atol=1e-5)
|
|
233
|
+
np.testing.assert_allclose(norm_large, 1.0, atol=1e-5)
|
|
234
|
+
|
|
235
|
+
# Larger subspace should be more accurate (but we can't easily test that without exact solution)
|
|
236
|
+
# At least verify they have the correct shape
|
|
237
|
+
assert state_small.shape == (1, 2**n)
|
|
238
|
+
assert state_large.shape == (1, 2**n)
|
|
File without changes
|
|
File without changes
|