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.

Files changed (49) hide show
  1. tensorcircuit/__init__.py +1 -1
  2. tensorcircuit/applications/van.py +1 -1
  3. tensorcircuit/backends/abstract_backend.py +1 -1
  4. tensorcircuit/backends/numpy_backend.py +1 -1
  5. tensorcircuit/backends/pytorch_backend.py +1 -1
  6. tensorcircuit/backends/tensorflow_backend.py +1 -1
  7. tensorcircuit/cloud/local.py +1 -1
  8. tensorcircuit/cloud/quafu_provider.py +1 -1
  9. tensorcircuit/cloud/tencent.py +1 -1
  10. tensorcircuit/compiler/simple_compiler.py +2 -2
  11. tensorcircuit/densitymatrix.py +1 -1
  12. tensorcircuit/keras.py +3 -3
  13. tensorcircuit/templates/blocks.py +2 -2
  14. {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/METADATA +15 -15
  15. {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/RECORD +18 -49
  16. {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/top_level.txt +0 -1
  17. tests/__init__.py +0 -0
  18. tests/conftest.py +0 -67
  19. tests/test_backends.py +0 -1156
  20. tests/test_calibrating.py +0 -149
  21. tests/test_channels.py +0 -409
  22. tests/test_circuit.py +0 -1713
  23. tests/test_cloud.py +0 -219
  24. tests/test_compiler.py +0 -147
  25. tests/test_dmcircuit.py +0 -555
  26. tests/test_ensemble.py +0 -72
  27. tests/test_fgs.py +0 -318
  28. tests/test_gates.py +0 -156
  29. tests/test_hamiltonians.py +0 -159
  30. tests/test_interfaces.py +0 -557
  31. tests/test_keras.py +0 -160
  32. tests/test_lattice.py +0 -1750
  33. tests/test_miscs.py +0 -304
  34. tests/test_mpscircuit.py +0 -341
  35. tests/test_noisemodel.py +0 -156
  36. tests/test_qaoa.py +0 -86
  37. tests/test_qem.py +0 -152
  38. tests/test_quantum.py +0 -549
  39. tests/test_quantum_attr.py +0 -42
  40. tests/test_results.py +0 -379
  41. tests/test_shadows.py +0 -160
  42. tests/test_simplify.py +0 -46
  43. tests/test_stabilizer.py +0 -226
  44. tests/test_templates.py +0 -218
  45. tests/test_timeevol.py +0 -641
  46. tests/test_torchnn.py +0 -99
  47. tests/test_van.py +0 -102
  48. {tensorcircuit_nightly-1.3.0.dev20250807.dist-info → tensorcircuit_nightly-1.3.0.dev20250810.dist-info}/WHEEL +0 -0
  49. {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 == ()