tensorcircuit-nightly 1.3.0.dev20250727__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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.3.0.dev20250727"
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
- return tf.sparse.sparse_dense_matmul(sp_a, b)
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)
@@ -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, contractor, rdtypestr, get_tn_info
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.dev20250727
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 [deepwiki](https://deepwiki.com/tensorcircuit/tensorcircuit-ng) or [Context7 MCP](https://context7.com/tensorcircuit/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=OVdReKvSMFFosvmCxCnQlOZK_hVKpavJKAdDoChQ-is,2032
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=RW97ncitCfO1QJLAUbKBvm2Tsc0hzKhqkC65ShA9-Q0,34456
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=Ob8zp-AgovoY3uYFNEUA_WlJz9YtgnaJvugKUXWttAA,59018
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=eTGVN6OyQgxvzj4C_CRvFIHv3v8tvZyb7Tt8B-kLbOo,36250
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.dev20250727.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
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=mm6kv5LqLkwHxWrGzLxajyOp1RaaKoHxq2OT1J3DpIM,9741
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.dev20250727.dist-info/METADATA,sha256=z1m8jG1FkT2VOYqt_udwFhII6IlJR9WzFWQb8m67NNs,34895
120
- tensorcircuit_nightly-1.3.0.dev20250727.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
- tensorcircuit_nightly-1.3.0.dev20250727.dist-info/top_level.txt,sha256=O_Iqeh2x02lasEYMI9iyPNNNtMzcpg5qvwMOkZQ7n4A,20
122
- tensorcircuit_nightly-1.3.0.dev20250727.dist-info/RECORD,,
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)