tensorcircuit-nightly 1.3.0.dev20250728__py3-none-any.whl → 1.3.0.dev20250730__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.

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,454 @@
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
+ print(h.dtype)
283
+ # Initial state - all spins up except last one down
284
+ psi0 = np.zeros((2**n,))
285
+ psi0[62] = 1.0
286
+ # State with pattern: up, up, up, up, up, down (111110 in binary = 62 in decimal)
287
+
288
+ # Normalize initial state
289
+ psi0 = psi0 / tc.backend.norm(psi0)
290
+
291
+ # Evolution times
292
+ times = tc.backend.convert_to_tensor([0.0, 0.5, 1.0])
293
+
294
+ # Perform Krylov evolution
295
+ states = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=10)
296
+
297
+ # Check output shape
298
+ assert states.shape == (3, 2**n)
299
+
300
+ # At t=0, state should be the initial state
301
+ np.testing.assert_allclose(states[0], psi0, atol=1e-5)
302
+
303
+ # All states should be normalized
304
+ for state in states:
305
+ norm = tc.backend.norm(state)
306
+ np.testing.assert_allclose(norm, 1.0, atol=1e-5)
307
+
308
+
309
+ @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
310
+ def test_krylov_evol_heisenberg_8_sites(backend):
311
+ """Test krylov_evol with Heisenberg Hamiltonian on 8 sites with callback"""
312
+ n = 8
313
+ # Create a 1D chain graph
314
+ g = tc.templates.graphs.Line1D(n, pbc=False)
315
+
316
+ # Generate Heisenberg Hamiltonian
317
+ h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
318
+
319
+ # Initial Neel state (alternating up and down spins)
320
+ c = tc.Circuit(n)
321
+ c.x([i for i in range(n // 2)])
322
+ psi0 = c.state()
323
+
324
+ # Evolution times
325
+ times = tc.backend.convert_to_tensor([0.0, 0.2, 0.4])
326
+
327
+ # Define callback to compute total magnetization
328
+ def total_magnetization(state):
329
+ # Calculate sum of <Z_i> for all sites
330
+ c = tc.Circuit(n, inputs=state)
331
+ total_z = 0.0
332
+ for i in range(n):
333
+ total_z += c.expectation_ps(z=[i])
334
+ return tc.backend.real(total_z)
335
+
336
+ # Perform Krylov evolution with callback
337
+ results = tc.timeevol.krylov_evol(
338
+ h, psi0, times, subspace_dimension=12, callback=total_magnetization
339
+ )
340
+
341
+ # Check output shape - should be scalar for each time point
342
+ assert results.shape == (3,)
343
+
344
+ # At t=0, for Neel state, total magnetization should be 0
345
+ np.testing.assert_allclose(results[0], 0.0, atol=1e-5)
346
+
347
+
348
+ @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
349
+ def test_krylov_evol_subspace_accuracy(backend):
350
+ """Test accuracy of krylov_evol with different subspace dimensions"""
351
+ n = 6
352
+ # Create a 1D chain graph
353
+ g = tc.templates.graphs.Line1D(n, pbc=False)
354
+
355
+ # Generate Heisenberg Hamiltonian
356
+ h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
357
+
358
+ # Initial domain wall state
359
+ c = tc.Circuit(n)
360
+ c.x([i + n // 2 for i in range(n // 2)])
361
+ psi0 = c.state()
362
+
363
+ # Evolution time
364
+ times = tc.backend.convert_to_tensor([1.0])
365
+
366
+ # Compare different subspace dimensions
367
+ state_small = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=6)
368
+ state_large = tc.timeevol.krylov_evol(h, psi0, times, subspace_dimension=16)
369
+
370
+ # Both should be normalized
371
+ norm_small = tc.backend.norm(state_small[0])
372
+ norm_large = tc.backend.norm(state_large[0])
373
+ np.testing.assert_allclose(norm_small, 1.0, atol=1e-5)
374
+ np.testing.assert_allclose(norm_large, 1.0, atol=1e-5)
375
+
376
+ # Larger subspace should be more accurate (but we can't easily test that without exact solution)
377
+ # At least verify they have the correct shape
378
+ assert state_small.shape == (1, 2**n)
379
+ assert state_large.shape == (1, 2**n)
380
+
381
+
382
+ @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
383
+ def test_krylov_evol_scan_impl(backend):
384
+ """Test krylov_evol with scan_impl=True"""
385
+ n = 4
386
+ # Create a 1D chain graph
387
+ g = tc.templates.graphs.Line1D(n, pbc=False)
388
+
389
+ # Generate Heisenberg Hamiltonian
390
+ h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=True)
391
+
392
+ c = tc.Circuit(n)
393
+ c.x([1, 2])
394
+ psi0 = c.state()
395
+
396
+ # Evolution times
397
+ times = tc.backend.convert_to_tensor([0.0, 0.5])
398
+
399
+ # Perform Krylov evolution with scan implementation
400
+ states_scan = tc.timeevol.krylov_evol(
401
+ h, psi0, times, subspace_dimension=8, scan_impl=True
402
+ )
403
+
404
+ states_scan_dense = tc.timeevol.krylov_evol(
405
+ tc.backend.to_dense(h), psi0, times, subspace_dimension=8, scan_impl=True
406
+ )
407
+
408
+ # Perform Krylov evolution with regular implementation
409
+ states_regular = tc.timeevol.krylov_evol(
410
+ h, psi0, times, subspace_dimension=8, scan_impl=False
411
+ )
412
+
413
+ # Check output shapes
414
+ assert states_scan.shape == (2, 2**n)
415
+ assert states_regular.shape == (2, 2**n)
416
+
417
+ # Results should be the same (up to numerical precision)
418
+ np.testing.assert_allclose(states_scan, states_regular, atol=1e-5)
419
+ np.testing.assert_allclose(states_scan_dense, states_regular, atol=1e-5)
420
+
421
+ # All states should be normalized
422
+ for state in states_scan:
423
+ norm = tc.backend.norm(state)
424
+ np.testing.assert_allclose(norm, 1.0, atol=1e-5)
425
+
426
+
427
+ @pytest.mark.parametrize("backend", [lf("jaxb")])
428
+ def test_krylov_evol_gradient(backend):
429
+ """Test gradient computation with krylov_evol"""
430
+ n = 5
431
+ # Create a 1D chain graph
432
+ g = tc.templates.graphs.Line1D(n, pbc=False)
433
+
434
+ # Generate Heisenberg Hamiltonian
435
+ h = tc.quantum.heisenberg_hamiltonian(g, hzz=1.0, hxx=1.0, hyy=1.0, sparse=False)
436
+
437
+ c = tc.Circuit(n)
438
+ c.x([1, 2])
439
+ psi0 = c.state()
440
+
441
+ # Evolution time
442
+ t = tc.backend.convert_to_tensor([1.0])
443
+
444
+ # Define a simple loss function based on the evolved state
445
+ def loss_function(t):
446
+ states = tc.timeevol.krylov_evol(
447
+ h, psi0, t, subspace_dimension=8, scan_impl=True
448
+ )
449
+ # Compute the sum of absolute values of the final state as a simple loss
450
+ return tc.backend.sum(tc.backend.abs(states[0]))
451
+
452
+ grad_fn = tc.backend.jit(tc.backend.grad(loss_function))
453
+ gradient = grad_fn(t)
454
+ print(gradient)