tensorcircuit-nightly 1.3.0.dev20250807__py3-none-any.whl → 1.3.0.dev20250810__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/applications/van.py +1 -1
- tensorcircuit/backends/abstract_backend.py +1 -1
- tensorcircuit/backends/numpy_backend.py +1 -1
- tensorcircuit/backends/pytorch_backend.py +1 -1
- tensorcircuit/backends/tensorflow_backend.py +1 -1
- tensorcircuit/cloud/local.py +1 -1
- tensorcircuit/cloud/quafu_provider.py +1 -1
- tensorcircuit/cloud/tencent.py +1 -1
- tensorcircuit/compiler/simple_compiler.py +2 -2
- tensorcircuit/densitymatrix.py +1 -1
- tensorcircuit/keras.py +3 -3
- tensorcircuit/templates/blocks.py +2 -2
- {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/METADATA +15 -15
- {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/RECORD +18 -49
- {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/top_level.txt +0 -1
- tests/__init__.py +0 -0
- tests/conftest.py +0 -67
- tests/test_backends.py +0 -1156
- tests/test_calibrating.py +0 -149
- tests/test_channels.py +0 -409
- tests/test_circuit.py +0 -1713
- tests/test_cloud.py +0 -219
- tests/test_compiler.py +0 -147
- tests/test_dmcircuit.py +0 -555
- tests/test_ensemble.py +0 -72
- tests/test_fgs.py +0 -318
- tests/test_gates.py +0 -156
- tests/test_hamiltonians.py +0 -159
- tests/test_interfaces.py +0 -557
- tests/test_keras.py +0 -160
- tests/test_lattice.py +0 -1750
- tests/test_miscs.py +0 -304
- tests/test_mpscircuit.py +0 -341
- tests/test_noisemodel.py +0 -156
- tests/test_qaoa.py +0 -86
- tests/test_qem.py +0 -152
- tests/test_quantum.py +0 -549
- tests/test_quantum_attr.py +0 -42
- tests/test_results.py +0 -379
- tests/test_shadows.py +0 -160
- tests/test_simplify.py +0 -46
- tests/test_stabilizer.py +0 -226
- tests/test_templates.py +0 -218
- tests/test_timeevol.py +0 -641
- tests/test_torchnn.py +0 -99
- tests/test_van.py +0 -102
- {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/licenses/LICENSE +0 -0
tests/test_timeevol.py
DELETED
|
@@ -1,641 +0,0 @@
|
|
|
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_circuit_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.evol_local(c, [1], h_square, 2.0, tc.backend.convert_to_tensor(0.2))
|
|
23
|
-
c.rx(1, theta=np.pi - 0.4)
|
|
24
|
-
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
25
|
-
|
|
26
|
-
ixi = tc.quantum.PauliStringSum2COO([[0, 1, 0]])
|
|
27
|
-
|
|
28
|
-
def h_square_sparse(t, b):
|
|
29
|
-
return (tc.backend.sign(t - 1.0) + 1) / 2 * b * ixi
|
|
30
|
-
|
|
31
|
-
c = tc.Circuit(3)
|
|
32
|
-
c.x(0)
|
|
33
|
-
c.cx(0, 1)
|
|
34
|
-
c.h(2)
|
|
35
|
-
c = tc.timeevol.evol_global(
|
|
36
|
-
c, h_square_sparse, 2.0, tc.backend.convert_to_tensor(0.2)
|
|
37
|
-
)
|
|
38
|
-
c.rx(1, theta=np.pi - 0.4)
|
|
39
|
-
np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_ode_evol_local(jaxb):
|
|
43
|
-
def local_hamiltonian(t, Omega, phi):
|
|
44
|
-
angle = phi * t
|
|
45
|
-
coeff = Omega * tc.backend.cos(2.0 * t) # Amplitude modulation
|
|
46
|
-
|
|
47
|
-
# Single-qubit Rabi Hamiltonian (2x2 matrix)
|
|
48
|
-
hx = coeff * tc.backend.cos(angle) * tc.gates.x().tensor
|
|
49
|
-
hy = coeff * tc.backend.sin(angle) * tc.gates.y().tensor
|
|
50
|
-
return hx + hy
|
|
51
|
-
|
|
52
|
-
# Initial state: GHZ state |0000⟩ + |1111⟩
|
|
53
|
-
c = tc.Circuit(4)
|
|
54
|
-
c.h(0)
|
|
55
|
-
for i in range(3):
|
|
56
|
-
c.cnot(i, i + 1)
|
|
57
|
-
psi0 = c.state()
|
|
58
|
-
|
|
59
|
-
# Time points
|
|
60
|
-
times = tc.backend.arange(0.0, 3.0, 0.1)
|
|
61
|
-
|
|
62
|
-
# Evolve with local Hamiltonian acting on qubit 1
|
|
63
|
-
states = tc.timeevol.ode_evol_local(
|
|
64
|
-
local_hamiltonian,
|
|
65
|
-
psi0,
|
|
66
|
-
times,
|
|
67
|
-
[1], # Apply to qubit 1
|
|
68
|
-
None,
|
|
69
|
-
1.0,
|
|
70
|
-
2.0, # Omega=1.0, phi=2.0
|
|
71
|
-
)
|
|
72
|
-
assert tc.backend.shape_tuple(states) == (30, 16)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def test_ode_evol_global(jaxb):
|
|
76
|
-
# Create a time-dependent transverse field Hamiltonian
|
|
77
|
-
# H(t) = -∑ᵢ Jᵢ(t) ZᵢZᵢ₊₁ - ∑ᵢ hᵢ(t) Xᵢ
|
|
78
|
-
|
|
79
|
-
# Time-dependent coefficients
|
|
80
|
-
def time_dep_J(t):
|
|
81
|
-
return 1.0 + 0.5 * tc.backend.sin(2.0 * t)
|
|
82
|
-
|
|
83
|
-
def time_dep_h(t):
|
|
84
|
-
return 0.5 * tc.backend.cos(1.5 * t)
|
|
85
|
-
|
|
86
|
-
zz_ham = tc.quantum.PauliStringSum2COO(
|
|
87
|
-
[[3, 3, 0, 0], [0, 3, 3, 0], [0, 0, 3, 3]], [1, 1, 1]
|
|
88
|
-
)
|
|
89
|
-
x_ham = tc.quantum.PauliStringSum2COO(
|
|
90
|
-
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [1, 1, 1, 1]
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
# Hamiltonian construction function
|
|
94
|
-
def hamiltonian_func(t):
|
|
95
|
-
# Create time-dependent ZZ terms
|
|
96
|
-
zz_coeff = time_dep_J(t)
|
|
97
|
-
|
|
98
|
-
# Create time-dependent X terms
|
|
99
|
-
x_coeff = time_dep_h(t)
|
|
100
|
-
|
|
101
|
-
return zz_coeff * zz_ham + x_coeff * x_ham
|
|
102
|
-
|
|
103
|
-
# Initial state: |↑↓↑↓⟩
|
|
104
|
-
c = tc.Circuit(4)
|
|
105
|
-
c.x([1, 3])
|
|
106
|
-
psi0 = c.state()
|
|
107
|
-
|
|
108
|
-
# Time points for evolution
|
|
109
|
-
times = tc.backend.arange(0, 5, 0.5)
|
|
110
|
-
|
|
111
|
-
def zobs(state):
|
|
112
|
-
n = int(np.log2(state.shape[-1]))
|
|
113
|
-
c = tc.Circuit(n, inputs=state)
|
|
114
|
-
return tc.backend.real(c.expectation_ps(z=[0]))
|
|
115
|
-
|
|
116
|
-
# Perform global ODE evolution
|
|
117
|
-
states = tc.timeevol.ode_evol_global(hamiltonian_func, psi0, times, zobs)
|
|
118
|
-
assert tc.backend.shape_tuple(states) == (10,)
|
|
119
|
-
|
|
120
|
-
zz_ham = tc.quantum.PauliStringSum2COO([[3, 3, 0, 0], [0, 3, 3, 0]], [1, 1])
|
|
121
|
-
x_ham = tc.quantum.PauliStringSum2COO([[1, 0, 0, 0], [0, 1, 0, 0]], [1, 1])
|
|
122
|
-
|
|
123
|
-
# Example with parameterized Hamiltonian and optimization
|
|
124
|
-
def parametrized_hamiltonian(t, params):
|
|
125
|
-
# params = [J0, J1, h0, h1] - parameters to optimize
|
|
126
|
-
J_t = params[0] + params[1] * tc.backend.sin(2.0 * t)
|
|
127
|
-
h_t = params[2] + params[3] * tc.backend.cos(1.5 * t)
|
|
128
|
-
|
|
129
|
-
return J_t * zz_ham + h_t * x_ham
|
|
130
|
-
|
|
131
|
-
# Observable function: measure ZZ correlation
|
|
132
|
-
def zz_correlation(state):
|
|
133
|
-
n = int(np.log2(state.shape[0]))
|
|
134
|
-
circuit = tc.Circuit(n, inputs=state)
|
|
135
|
-
return circuit.expectation_ps(z=[0, 1])
|
|
136
|
-
|
|
137
|
-
@tc.backend.jit
|
|
138
|
-
@tc.backend.value_and_grad
|
|
139
|
-
def objective_function(params):
|
|
140
|
-
states = tc.timeevol.ode_evol_global(
|
|
141
|
-
parametrized_hamiltonian,
|
|
142
|
-
psi0,
|
|
143
|
-
tc.backend.convert_to_tensor([0, 1.0]),
|
|
144
|
-
None,
|
|
145
|
-
params,
|
|
146
|
-
)
|
|
147
|
-
# Measure ZZ correlation at final time
|
|
148
|
-
final_state = states[-1]
|
|
149
|
-
return tc.backend.real(zz_correlation(final_state))
|
|
150
|
-
|
|
151
|
-
print(objective_function(tc.backend.ones([4])))
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
155
|
-
def test_ed_evol(backend):
|
|
156
|
-
n = 4
|
|
157
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
158
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
|
|
159
|
-
|
|
160
|
-
# Initial Neel state: |↑↓↑↓⟩
|
|
161
|
-
c = tc.Circuit(n)
|
|
162
|
-
c.x([1, 3]) # Apply X gates to qubits 1 and 3
|
|
163
|
-
psi0 = c.state()
|
|
164
|
-
|
|
165
|
-
# Imaginary time evolution times
|
|
166
|
-
times = tc.backend.convert_to_tensor([0.0, 0.5, 1.0, 2.0])
|
|
167
|
-
|
|
168
|
-
# Evolve and get states
|
|
169
|
-
states = tc.timeevol.ed_evol(h, psi0, times)
|
|
170
|
-
print(states)
|
|
171
|
-
|
|
172
|
-
def evolve_and_measure(params):
|
|
173
|
-
# Parametrized Hamiltonian
|
|
174
|
-
h_param = tc.quantum.heisenberg_hamiltonian(
|
|
175
|
-
g, hzz=params[0], hxx=params[1], hyy=params[2], sparse=False
|
|
176
|
-
)
|
|
177
|
-
states = tc.timeevol.ed_evol(h_param, psi0, times)
|
|
178
|
-
# Measure observable on final state
|
|
179
|
-
circuit = tc.Circuit(n, inputs=states[-1])
|
|
180
|
-
return tc.backend.real(circuit.expectation_ps(z=[0]))
|
|
181
|
-
|
|
182
|
-
evolve_and_measure(tc.backend.ones([3]))
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
186
|
-
def test_hamiltonian_evol_basic(backend):
|
|
187
|
-
"""Test basic functionality of hamiltonian_evol with a simple 2-qubit Hamiltonian"""
|
|
188
|
-
# Define a simple 2-qubit Hamiltonian
|
|
189
|
-
h = tc.backend.convert_to_tensor(
|
|
190
|
-
[
|
|
191
|
-
[1.0, 0.0, 0.0, 0.0],
|
|
192
|
-
[0.0, -1.0, 2.0, 0.0],
|
|
193
|
-
[0.0, 2.0, -1.0, 0.0],
|
|
194
|
-
[0.0, 0.0, 0.0, 1.0],
|
|
195
|
-
]
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
# Initial state |00⟩
|
|
199
|
-
psi0 = tc.backend.convert_to_tensor([1.0, 0.0, 0.0, 0.0])
|
|
200
|
-
|
|
201
|
-
# Evolution times
|
|
202
|
-
times = 1.0j * tc.backend.cast(
|
|
203
|
-
tc.backend.convert_to_tensor([0.0, 0.5, 1.0]), tc.dtypestr
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
# Evolve and get states
|
|
207
|
-
states = tc.timeevol.hamiltonian_evol(h, psi0, times)
|
|
208
|
-
|
|
209
|
-
# Check output shape
|
|
210
|
-
assert states.shape == (3, 4)
|
|
211
|
-
|
|
212
|
-
# At t=0, state should be the initial state (normalized)
|
|
213
|
-
np.testing.assert_allclose(states[0], psi0, atol=1e-5)
|
|
214
|
-
|
|
215
|
-
# All states should be normalized
|
|
216
|
-
for state in states:
|
|
217
|
-
norm = tc.backend.norm(state)
|
|
218
|
-
np.testing.assert_allclose(norm, 1.0, atol=1e-5)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
222
|
-
def test_hamiltonian_evol_with_callback(backend):
|
|
223
|
-
"""Test hamiltonian_evol with a callback function"""
|
|
224
|
-
# Define a simple Hamiltonian
|
|
225
|
-
h = tc.backend.convert_to_tensor([[1.0, 0.0], [0.0, -1.0]])
|
|
226
|
-
|
|
227
|
-
# Initial state
|
|
228
|
-
psi0 = tc.backend.convert_to_tensor([1.0, 1.0]) / np.sqrt(2)
|
|
229
|
-
|
|
230
|
-
# Evolution times
|
|
231
|
-
times = 1.0j * tc.backend.cast(
|
|
232
|
-
tc.backend.convert_to_tensor([0.0, 1.0, 2.0]), tc.dtypestr
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
# Define callback to compute expectation value of Pauli Z
|
|
236
|
-
def callback(state):
|
|
237
|
-
# Z operator
|
|
238
|
-
c = tc.Circuit(1, inputs=state)
|
|
239
|
-
# Compute <psi|Z|psi>
|
|
240
|
-
return tc.backend.real(c.expectation_ps(z=[0]))
|
|
241
|
-
|
|
242
|
-
# Evolve with callback
|
|
243
|
-
results = tc.timeevol.hamiltonian_evol(h, psi0, times, callback)
|
|
244
|
-
|
|
245
|
-
# Check output shape - should be scalar for each time point
|
|
246
|
-
assert results.shape == (3,)
|
|
247
|
-
|
|
248
|
-
# At t=0, for |+⟩ state, <Z> should be 0
|
|
249
|
-
np.testing.assert_allclose(results[0], 0.0, atol=1e-5)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
253
|
-
def test_hamiltonian_evol_imaginary_time(backend):
|
|
254
|
-
"""Test that hamiltonian_evol performs imaginary time evolution by default"""
|
|
255
|
-
# Define a Hamiltonian
|
|
256
|
-
h = tc.backend.convert_to_tensor([[2.0, 0.0], [0.0, 1.0]])
|
|
257
|
-
|
|
258
|
-
# Initial state with equal superposition
|
|
259
|
-
psi0 = tc.backend.convert_to_tensor([1.0, 1.0]) / np.sqrt(2)
|
|
260
|
-
|
|
261
|
-
# Large time to see ground state dominance
|
|
262
|
-
times = tc.backend.convert_to_tensor([0.0, 10.0])
|
|
263
|
-
|
|
264
|
-
# Evolve
|
|
265
|
-
states = tc.timeevol.hamiltonian_evol(h, psi0, times)
|
|
266
|
-
|
|
267
|
-
# Ground state is |1⟩ (eigenvalue 1.0), so after long imaginary time
|
|
268
|
-
# evolution, we should approach this state
|
|
269
|
-
expected_ground_state = tc.backend.convert_to_tensor([0.0, 1.0])
|
|
270
|
-
np.testing.assert_allclose(states[-1], expected_ground_state, atol=1e-3)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
274
|
-
def test_krylov_evol_heisenberg_6_sites(backend):
|
|
275
|
-
"""Test krylov_evol with Heisenberg Hamiltonian on 6 sites"""
|
|
276
|
-
n = 6
|
|
277
|
-
# Create a 1D chain graph
|
|
278
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
279
|
-
|
|
280
|
-
# Generate Heisenberg Hamiltonian
|
|
281
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
|
|
282
|
-
# Initial state - all spins up except last one down
|
|
283
|
-
psi0 = np.zeros((2**n,))
|
|
284
|
-
psi0[62] = 1.0
|
|
285
|
-
# State with pattern: up, up, up, up, up, down (111110 in binary = 62 in decimal)
|
|
286
|
-
|
|
287
|
-
# Normalize initial state
|
|
288
|
-
psi0 = psi0 / tc.backend.norm(psi0)
|
|
289
|
-
|
|
290
|
-
# Evolution times
|
|
291
|
-
times = tc.backend.convert_to_tensor([0.0, 0.5, 1.0])
|
|
292
|
-
|
|
293
|
-
# Perform Krylov evolution
|
|
294
|
-
states = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=10)
|
|
295
|
-
|
|
296
|
-
# Check output shape
|
|
297
|
-
assert states.shape == (3, 2**n)
|
|
298
|
-
|
|
299
|
-
# At t=0, state should be the initial state
|
|
300
|
-
np.testing.assert_allclose(states[0], psi0, atol=1e-5)
|
|
301
|
-
|
|
302
|
-
# All states should be normalized
|
|
303
|
-
for state in states:
|
|
304
|
-
norm = tc.backend.norm(state)
|
|
305
|
-
np.testing.assert_allclose(norm, 1.0, atol=1e-5)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
309
|
-
def test_krylov_evol_heisenberg_8_sites(backend):
|
|
310
|
-
"""Test krylov_evol with Heisenberg Hamiltonian on 8 sites with callback"""
|
|
311
|
-
n = 8
|
|
312
|
-
# Create a 1D chain graph
|
|
313
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
314
|
-
|
|
315
|
-
# Generate Heisenberg Hamiltonian
|
|
316
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
317
|
-
|
|
318
|
-
# Initial Neel state (alternating up and down spins)
|
|
319
|
-
c = tc.Circuit(n)
|
|
320
|
-
c.x([i for i in range(n // 2)])
|
|
321
|
-
psi0 = c.state()
|
|
322
|
-
|
|
323
|
-
# Evolution times
|
|
324
|
-
times = tc.backend.convert_to_tensor([0.0, 0.2, 0.4])
|
|
325
|
-
|
|
326
|
-
# Define callback to compute total magnetization
|
|
327
|
-
def total_magnetization(state):
|
|
328
|
-
# Calculate sum of <Z_i> for all sites
|
|
329
|
-
c = tc.Circuit(n, inputs=state)
|
|
330
|
-
total_z = 0.0
|
|
331
|
-
for i in range(n):
|
|
332
|
-
total_z += c.expectation_ps(z=[i])
|
|
333
|
-
return tc.backend.real(total_z)
|
|
334
|
-
|
|
335
|
-
# Perform Krylov evolution with callback
|
|
336
|
-
results = tc.timeevol.krylov_evol(
|
|
337
|
-
h, psi0, times, subspace_dimension=12, callback=total_magnetization
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
# Check output shape - should be scalar for each time point
|
|
341
|
-
assert results.shape == (3,)
|
|
342
|
-
|
|
343
|
-
# At t=0, for Neel state, total magnetization should be 0
|
|
344
|
-
np.testing.assert_allclose(results[0], 0.0, atol=1e-5)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
348
|
-
def test_krylov_evol_subspace_accuracy(backend):
|
|
349
|
-
"""Test accuracy of krylov_evol with different subspace dimensions"""
|
|
350
|
-
n = 6
|
|
351
|
-
# Create a 1D chain graph
|
|
352
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
353
|
-
|
|
354
|
-
# Generate Heisenberg Hamiltonian
|
|
355
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
356
|
-
|
|
357
|
-
# Initial domain wall state
|
|
358
|
-
c = tc.Circuit(n)
|
|
359
|
-
c.x([i + n // 2 for i in range(n // 2)])
|
|
360
|
-
psi0 = c.state()
|
|
361
|
-
|
|
362
|
-
# Evolution time
|
|
363
|
-
times = tc.backend.convert_to_tensor([1.0])
|
|
364
|
-
|
|
365
|
-
# Compare different subspace dimensions
|
|
366
|
-
state_small = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=6)
|
|
367
|
-
state_large = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=16)
|
|
368
|
-
|
|
369
|
-
# Both should be normalized
|
|
370
|
-
norm_small = tc.backend.norm(state_small[0])
|
|
371
|
-
norm_large = tc.backend.norm(state_large[0])
|
|
372
|
-
np.testing.assert_allclose(norm_small, 1.0, atol=1e-5)
|
|
373
|
-
np.testing.assert_allclose(norm_large, 1.0, atol=1e-5)
|
|
374
|
-
|
|
375
|
-
# Larger subspace should be more accurate (but we can't easily test that without exact solution)
|
|
376
|
-
# At least verify they have the correct shape
|
|
377
|
-
assert state_small.shape == (1, 2**n)
|
|
378
|
-
assert state_large.shape == (1, 2**n)
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
|
|
382
|
-
def test_krylov_evol_scan_impl(backend):
|
|
383
|
-
"""Test krylov_evol with scan_impl=True"""
|
|
384
|
-
n = 4
|
|
385
|
-
# Create a 1D chain graph
|
|
386
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
387
|
-
|
|
388
|
-
# Generate Heisenberg Hamiltonian
|
|
389
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
390
|
-
|
|
391
|
-
c = tc.Circuit(n)
|
|
392
|
-
c.x([1, 2])
|
|
393
|
-
psi0 = c.state()
|
|
394
|
-
|
|
395
|
-
# Evolution times
|
|
396
|
-
times = tc.backend.convert_to_tensor([0.0, 0.5])
|
|
397
|
-
|
|
398
|
-
# Perform Krylov evolution with scan implementation
|
|
399
|
-
states_scan = tc.timeevol.krylov_evol(
|
|
400
|
-
h, psi0, times, subspace_dimension=8, scan_impl=True
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
states_scan_dense = tc.timeevol.krylov_evol(
|
|
404
|
-
tc.backend.to_dense(h), psi0, times, subspace_dimension=8, scan_impl=True
|
|
405
|
-
)
|
|
406
|
-
|
|
407
|
-
# Perform Krylov evolution with regular implementation
|
|
408
|
-
states_regular = tc.timeevol.krylov_evol(
|
|
409
|
-
h, psi0, times, subspace_dimension=8, scan_impl=False
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
# Check output shapes
|
|
413
|
-
assert states_scan.shape == (2, 2**n)
|
|
414
|
-
assert states_regular.shape == (2, 2**n)
|
|
415
|
-
|
|
416
|
-
# Results should be the same (up to numerical precision)
|
|
417
|
-
np.testing.assert_allclose(states_scan, states_regular, atol=1e-5)
|
|
418
|
-
np.testing.assert_allclose(states_scan_dense, states_regular, atol=1e-5)
|
|
419
|
-
|
|
420
|
-
# All states should be normalized
|
|
421
|
-
for state in states_scan:
|
|
422
|
-
norm = tc.backend.norm(state)
|
|
423
|
-
np.testing.assert_allclose(norm, 1.0, atol=1e-5)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
@pytest.mark.parametrize("backend", [lf("jaxb")])
|
|
427
|
-
def test_krylov_evol_gradient(backend):
|
|
428
|
-
"""Test gradient computation with krylov_evol"""
|
|
429
|
-
n = 5
|
|
430
|
-
# Create a 1D chain graph
|
|
431
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
432
|
-
|
|
433
|
-
# Generate Heisenberg Hamiltonian
|
|
434
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
|
|
435
|
-
|
|
436
|
-
c = tc.Circuit(n)
|
|
437
|
-
c.x([1, 2])
|
|
438
|
-
psi0 = c.state()
|
|
439
|
-
|
|
440
|
-
# Evolution time
|
|
441
|
-
t = tc.backend.convert_to_tensor([1.0])
|
|
442
|
-
|
|
443
|
-
# Define a simple loss function based on the evolved state
|
|
444
|
-
def loss_function(t):
|
|
445
|
-
states = tc.timeevol.krylov_evol(
|
|
446
|
-
h, psi0, t, subspace_dimension=8, scan_impl=True
|
|
447
|
-
)
|
|
448
|
-
# Compute the sum of absolute values of the final state as a simple loss
|
|
449
|
-
return tc.backend.sum(tc.backend.abs(states[0]))
|
|
450
|
-
|
|
451
|
-
grad_fn = tc.backend.jit(tc.backend.grad(loss_function))
|
|
452
|
-
gradient = grad_fn(t)
|
|
453
|
-
print(gradient)
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
@pytest.mark.parametrize(
|
|
457
|
-
"backend, sparse",
|
|
458
|
-
[[lf("npb"), True], [lf("npb"), False], [lf("jaxb"), True], [lf("jaxb"), False]],
|
|
459
|
-
)
|
|
460
|
-
def test_chebyshev_evol_basic(backend, highp, sparse):
|
|
461
|
-
n = 6
|
|
462
|
-
# Create a 1D chain graph
|
|
463
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
464
|
-
|
|
465
|
-
# Generate Heisenberg Hamiltonian (dense for better compatibility)
|
|
466
|
-
h = tc.quantum.heisenberg_hamiltonian(
|
|
467
|
-
g, hzz=1.0, hxx=1.0, hyy=1.0, hx=0.2, sparse=sparse
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
# Initial Neel state: |↑↓↑↓⟩
|
|
471
|
-
c = tc.Circuit(n)
|
|
472
|
-
c.x([1, 3, 5]) # Apply X gates to qubits 1 and 3
|
|
473
|
-
psi0 = c.state()
|
|
474
|
-
|
|
475
|
-
# Evolution time
|
|
476
|
-
t = 2.0
|
|
477
|
-
|
|
478
|
-
# Estimate spectral bounds
|
|
479
|
-
e_max, e_min = tc.timeevol.estimate_spectral_bounds(h, n_iter=30)
|
|
480
|
-
|
|
481
|
-
# Estimate parameters
|
|
482
|
-
k = tc.timeevol.estimate_k(t, (e_max, e_min))
|
|
483
|
-
m = tc.timeevol.estimate_M(t, (e_max, e_min), k)
|
|
484
|
-
|
|
485
|
-
# Evolve using Chebyshev method
|
|
486
|
-
psi_chebyshev = tc.timeevol.chebyshev_evol(
|
|
487
|
-
h, psi0, t, (float(e_max) + 0.1, float(e_min) - 0.1), k, m
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
# Check that state is normalized (or close to it)
|
|
491
|
-
norm = tc.backend.norm(psi_chebyshev)
|
|
492
|
-
np.testing.assert_allclose(norm, 1.0, atol=1e-3)
|
|
493
|
-
|
|
494
|
-
# Compare with exact evolution for small system
|
|
495
|
-
if sparse is True:
|
|
496
|
-
h = tc.backend.to_dense(h)
|
|
497
|
-
psi_exact = tc.timeevol.ed_evol(h, psi0, 1.0j * tc.backend.convert_to_tensor([t]))[
|
|
498
|
-
0
|
|
499
|
-
]
|
|
500
|
-
|
|
501
|
-
# States should be close (up to global phase)
|
|
502
|
-
fidelity = np.abs(np.vdot(np.asarray(psi_exact), np.asarray(psi_chebyshev))) ** 2
|
|
503
|
-
assert fidelity > 0.95 # Should be close, but not exact due to approximations
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
def test_chebyshev_evol_vmap_on_t(jaxb, highp):
|
|
507
|
-
n = 4
|
|
508
|
-
# Create a 1D chain graph
|
|
509
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
510
|
-
|
|
511
|
-
# Generate Heisenberg Hamiltonian
|
|
512
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
|
|
513
|
-
|
|
514
|
-
# Initial Neel state
|
|
515
|
-
c = tc.Circuit(n)
|
|
516
|
-
c.x([1, 3])
|
|
517
|
-
psi0 = c.state()
|
|
518
|
-
|
|
519
|
-
# Estimate spectral bounds
|
|
520
|
-
e_max, e_min = tc.timeevol.estimate_spectral_bounds(h, n_iter=20)
|
|
521
|
-
|
|
522
|
-
# Fixed parameters
|
|
523
|
-
k = 50
|
|
524
|
-
m = 150
|
|
525
|
-
|
|
526
|
-
# Define vectorized evolution function
|
|
527
|
-
def evolve_single_time(t):
|
|
528
|
-
return tc.timeevol.chebyshev_evol(
|
|
529
|
-
h, psi0, t, (float(e_max) + 0.1, float(e_min) - 0.1), k, m
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
# Vectorize over times
|
|
533
|
-
times = tc.backend.convert_to_tensor([0.5, 1.0, 1.5])
|
|
534
|
-
vmap_evolve = tc.backend.jit(tc.backend.vmap(evolve_single_time))
|
|
535
|
-
states_vmap = vmap_evolve(times)
|
|
536
|
-
|
|
537
|
-
# Check output shape
|
|
538
|
-
assert states_vmap.shape == (3, 2**n)
|
|
539
|
-
|
|
540
|
-
# Compare with sequential execution
|
|
541
|
-
states_sequential = []
|
|
542
|
-
for t in times:
|
|
543
|
-
state = tc.timeevol.chebyshev_evol(
|
|
544
|
-
h, psi0, float(t), (e_max + 0.1, e_min - 0.1), k, m
|
|
545
|
-
)
|
|
546
|
-
states_sequential.append(state)
|
|
547
|
-
|
|
548
|
-
states_sequential = tc.backend.stack(states_sequential)
|
|
549
|
-
|
|
550
|
-
# Results should be the same
|
|
551
|
-
np.testing.assert_allclose(states_vmap, states_sequential, atol=1e-5)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
def test_chebyshev_evol_jit_on_psi(jaxb, highp):
|
|
555
|
-
"""Test JIT compilation capability of chebyshev_evol on psi parameter"""
|
|
556
|
-
n = 4
|
|
557
|
-
# Create a 1D chain graph
|
|
558
|
-
g = tc.templates.graphs.Line1D(n, pbc=False)
|
|
559
|
-
|
|
560
|
-
# Generate Heisenberg Hamiltonian
|
|
561
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=0.6, hyy=1.0, sparse=True)
|
|
562
|
-
|
|
563
|
-
# Estimate spectral bounds
|
|
564
|
-
e_max, e_min = tc.timeevol.estimate_spectral_bounds(h, n_iter=20)
|
|
565
|
-
|
|
566
|
-
# Fixed parameters
|
|
567
|
-
t = 1.0
|
|
568
|
-
k = 50
|
|
569
|
-
m = 150
|
|
570
|
-
|
|
571
|
-
# Define JIT-compiled evolution function with psi as argument
|
|
572
|
-
def evolve_state(psi):
|
|
573
|
-
return tc.timeevol.chebyshev_evol(
|
|
574
|
-
h, psi, t, (float(e_max) + 0.1, float(e_min) - 0.1), k, m
|
|
575
|
-
)
|
|
576
|
-
|
|
577
|
-
jit_evolve = tc.backend.jit(evolve_state)
|
|
578
|
-
|
|
579
|
-
# Test with different initial states
|
|
580
|
-
c1 = tc.Circuit(n)
|
|
581
|
-
c1.x([0, 2])
|
|
582
|
-
psi1 = c1.state()
|
|
583
|
-
|
|
584
|
-
c2 = tc.Circuit(n)
|
|
585
|
-
c2.h(0)
|
|
586
|
-
for i in range(n - 1):
|
|
587
|
-
c2.cnot(i, i + 1)
|
|
588
|
-
psi2 = c2.state()
|
|
589
|
-
|
|
590
|
-
# Run JIT-compiled evolution
|
|
591
|
-
result1_jit = jit_evolve(psi1)
|
|
592
|
-
result2_jit = jit_evolve(psi2)
|
|
593
|
-
|
|
594
|
-
# Run regular evolution for comparison
|
|
595
|
-
result1_regular = tc.timeevol.chebyshev_evol(
|
|
596
|
-
h, psi1, t, (e_max + 0.1, e_min - 0.1), k, m
|
|
597
|
-
)
|
|
598
|
-
result2_regular = tc.timeevol.chebyshev_evol(
|
|
599
|
-
h, psi2, t, (e_max + 0.1, e_min - 0.1), k, m
|
|
600
|
-
)
|
|
601
|
-
print(result1_jit)
|
|
602
|
-
# Results should be the same
|
|
603
|
-
np.testing.assert_allclose(result1_jit, result1_regular, atol=1e-5)
|
|
604
|
-
np.testing.assert_allclose(result2_jit, result2_regular, atol=1e-5)
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
def test_chebyshev_evol_ad_on_t(jaxb, highp):
|
|
608
|
-
n = 5
|
|
609
|
-
# Create a 1D chain graph
|
|
610
|
-
g = tc.templates.graphs.Line1D(n, pbc=True)
|
|
611
|
-
|
|
612
|
-
# Generate Heisenberg Hamiltonian
|
|
613
|
-
h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
|
|
614
|
-
|
|
615
|
-
# Initial state
|
|
616
|
-
c = tc.Circuit(n)
|
|
617
|
-
c.x([1, 3])
|
|
618
|
-
psi0 = c.state()
|
|
619
|
-
|
|
620
|
-
# Estimate spectral bounds
|
|
621
|
-
e_max, e_min = tc.timeevol.estimate_spectral_bounds(h, n_iter=20)
|
|
622
|
-
|
|
623
|
-
# Fixed parameters
|
|
624
|
-
k = 50
|
|
625
|
-
m = 100
|
|
626
|
-
|
|
627
|
-
# Define loss function for gradient computation
|
|
628
|
-
def loss_function(t):
|
|
629
|
-
psi_t = tc.timeevol.chebyshev_evol(
|
|
630
|
-
h, psi0, t, (float(e_max) + 0.1, float(e_min) - 0.1), k, m
|
|
631
|
-
)
|
|
632
|
-
c = tc.Circuit(5, inputs=psi_t)
|
|
633
|
-
return tc.backend.real(c.expectation_ps(z=[2]))
|
|
634
|
-
|
|
635
|
-
# Compute gradient
|
|
636
|
-
grad_fn = tc.backend.jit(tc.backend.grad(loss_function))
|
|
637
|
-
t_test = tc.backend.convert_to_tensor(1.0)
|
|
638
|
-
gradient = grad_fn(t_test)
|
|
639
|
-
print(gradient)
|
|
640
|
-
# Gradient should be a scalar
|
|
641
|
-
assert gradient.shape == ()
|