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.
- tensorcircuit/__init__.py +2 -1
- tensorcircuit/backends/abstract_backend.py +17 -2
- tensorcircuit/backends/numpy_backend.py +1 -0
- tensorcircuit/backends/tensorflow_backend.py +12 -2
- tensorcircuit/experimental.py +7 -152
- tensorcircuit/timeevol.py +594 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250730.dist-info}/METADATA +2 -2
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250730.dist-info}/RECORD +13 -11
- tests/test_miscs.py +0 -30
- tests/test_timeevol.py +454 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250730.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250730.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250728.dist-info → tensorcircuit_nightly-1.3.0.dev20250730.dist-info}/top_level.txt +0 -0
tensorcircuit/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "1.3.0.
|
|
1
|
+
__version__ = "1.3.0.dev20250730"
|
|
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``.
|
|
@@ -1386,9 +1389,21 @@ class ExtendedBackend:
|
|
|
1386
1389
|
:rtype: Tensor
|
|
1387
1390
|
"""
|
|
1388
1391
|
carry = init
|
|
1389
|
-
|
|
1390
|
-
|
|
1392
|
+
# Check if `xs` is a PyTree (tuple or list) of arrays.
|
|
1393
|
+
if isinstance(xs, (tuple, list)):
|
|
1394
|
+
for x_slice_tuple in zip(*xs):
|
|
1395
|
+
# x_slice_tuple will be (k_elems[i], j_elems[i]) at each step.
|
|
1396
|
+
carry = f(carry, x_slice_tuple)
|
|
1397
|
+
else:
|
|
1398
|
+
# If xs is a single array, iterate normally.
|
|
1399
|
+
for x in xs:
|
|
1400
|
+
carry = f(carry, x)
|
|
1401
|
+
|
|
1391
1402
|
return carry
|
|
1403
|
+
# carry = init
|
|
1404
|
+
# for x in xs:
|
|
1405
|
+
# carry = f(carry, x)
|
|
1406
|
+
# return carry
|
|
1392
1407
|
|
|
1393
1408
|
def stop_gradient(self: Any, a: Tensor) -> Tensor:
|
|
1394
1409
|
"""
|
|
@@ -200,6 +200,7 @@ class NumpyBackend(numpy_backend.NumPyBackend, ExtendedBackend): # type: ignore
|
|
|
200
200
|
return softmax(a, axis=axis)
|
|
201
201
|
|
|
202
202
|
def onehot(self, a: Tensor, num: int) -> Tensor:
|
|
203
|
+
a = np.asarray(a)
|
|
203
204
|
res = np.eye(num)[a.reshape([-1])]
|
|
204
205
|
return res.reshape(list(a.shape) + [num])
|
|
205
206
|
# https://stackoverflow.com/questions/38592324/one-hot-encoding-using-numpy
|
|
@@ -678,7 +678,14 @@ class TensorFlowBackend(tensorflow_backend.TensorFlowBackend, ExtendedBackend):
|
|
|
678
678
|
sp_a: Tensor,
|
|
679
679
|
b: Tensor,
|
|
680
680
|
) -> Tensor:
|
|
681
|
-
|
|
681
|
+
is_vec = False
|
|
682
|
+
if len(b.shape) == 1:
|
|
683
|
+
b = self.reshape(b, [-1, 1])
|
|
684
|
+
is_vec = True
|
|
685
|
+
r = tf.sparse.sparse_dense_matmul(sp_a, b)
|
|
686
|
+
if is_vec:
|
|
687
|
+
return self.reshape(r, [-1])
|
|
688
|
+
return r
|
|
682
689
|
|
|
683
690
|
def _densify(self) -> Tensor:
|
|
684
691
|
@partial(self.jit, jit_compile=True)
|
|
@@ -712,7 +719,10 @@ class TensorFlowBackend(tensorflow_backend.TensorFlowBackend, ExtendedBackend):
|
|
|
712
719
|
def scan(
|
|
713
720
|
self, f: Callable[[Tensor, Tensor], Tensor], xs: Tensor, init: Tensor
|
|
714
721
|
) -> Tensor:
|
|
715
|
-
|
|
722
|
+
stacked_results = tf.scan(f, xs, init)
|
|
723
|
+
final_state = tf.nest.map_structure(lambda x: x[-1], stacked_results)
|
|
724
|
+
return final_state
|
|
725
|
+
# return tf.scan(f, xs, init)[-1]
|
|
716
726
|
|
|
717
727
|
def device(self, a: Tensor) -> str:
|
|
718
728
|
dev = a.device
|
tensorcircuit/experimental.py
CHANGED
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
Experimental features
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
# pylint: disable=unused-import
|
|
6
|
+
|
|
5
7
|
from functools import partial
|
|
6
8
|
import logging
|
|
7
9
|
from typing import Any, Callable, Dict, Optional, Tuple, List, Sequence, Union
|
|
8
10
|
|
|
9
11
|
import numpy as np
|
|
10
12
|
|
|
11
|
-
from .cons import backend, dtypestr,
|
|
13
|
+
from .cons import backend, dtypestr, rdtypestr, get_tn_info
|
|
12
14
|
from .gates import Gate
|
|
15
|
+
from .timeevol import hamiltonian_evol, evol_global, evol_local
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# for backward compatibility
|
|
13
19
|
|
|
14
20
|
Tensor = Any
|
|
15
21
|
Circuit = Any
|
|
@@ -435,157 +441,6 @@ def finite_difference_differentiator(
|
|
|
435
441
|
return tf_function # type: ignore
|
|
436
442
|
|
|
437
443
|
|
|
438
|
-
def hamiltonian_evol(
|
|
439
|
-
tlist: Tensor,
|
|
440
|
-
h: Tensor,
|
|
441
|
-
psi0: Tensor,
|
|
442
|
-
callback: Optional[Callable[..., Any]] = None,
|
|
443
|
-
) -> Tensor:
|
|
444
|
-
"""
|
|
445
|
-
Fast implementation of time independent Hamiltonian evolution using eigendecomposition.
|
|
446
|
-
By default, performs imaginary time evolution.
|
|
447
|
-
|
|
448
|
-
:param tlist: Time points for evolution
|
|
449
|
-
:type tlist: Tensor
|
|
450
|
-
:param h: Time-independent Hamiltonian matrix
|
|
451
|
-
:type h: Tensor
|
|
452
|
-
:param psi0: Initial state vector
|
|
453
|
-
:type psi0: Tensor
|
|
454
|
-
:param callback: Optional function to process state at each time point
|
|
455
|
-
:type callback: Optional[Callable[..., Any]], optional
|
|
456
|
-
:return: Evolution results at each time point. If callback is None, returns state vectors;
|
|
457
|
-
otherwise returns callback results
|
|
458
|
-
:rtype: Tensor
|
|
459
|
-
|
|
460
|
-
:Example:
|
|
461
|
-
|
|
462
|
-
>>> import tensorcircuit as tc
|
|
463
|
-
>>> import numpy as np
|
|
464
|
-
>>> # Define a simple 2-qubit Hamiltonian
|
|
465
|
-
>>> h = tc.array_to_tensor([
|
|
466
|
-
... [1.0, 0.0, 0.0, 0.0],
|
|
467
|
-
... [0.0, -1.0, 2.0, 0.0],
|
|
468
|
-
... [0.0, 2.0, -1.0, 0.0],
|
|
469
|
-
... [0.0, 0.0, 0.0, 1.0]
|
|
470
|
-
... ])
|
|
471
|
-
>>> # Initial state |00⟩
|
|
472
|
-
>>> psi0 = tc.array_to_tensor([1.0, 0.0, 0.0, 0.0])
|
|
473
|
-
>>> # Evolution times
|
|
474
|
-
>>> times = tc.array_to_tensor([0.0, 0.5, 1.0])
|
|
475
|
-
>>> # Evolve and get states
|
|
476
|
-
>>> states = tc.experimental.hamiltonian_evol(times, h, psi0)
|
|
477
|
-
>>> print(states.shape) # (3, 4)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
Note:
|
|
481
|
-
1. The Hamiltonian must be time-independent
|
|
482
|
-
2. For time-dependent Hamiltonians, use ``evol_local`` or ``evol_global`` instead
|
|
483
|
-
3. The evolution is performed in imaginary time by default (factor -t in exponential)
|
|
484
|
-
4. The state is automatically normalized at each time point
|
|
485
|
-
"""
|
|
486
|
-
es, u = backend.eigh(h)
|
|
487
|
-
utpsi0 = backend.reshape(
|
|
488
|
-
backend.transpose(u) @ backend.reshape(psi0, [-1, 1]), [-1]
|
|
489
|
-
)
|
|
490
|
-
|
|
491
|
-
@backend.jit
|
|
492
|
-
def _evol(t: Tensor) -> Tensor:
|
|
493
|
-
ebetah_utpsi0 = backend.exp(-t * es) * utpsi0
|
|
494
|
-
psi_exact = backend.conj(u) @ backend.reshape(ebetah_utpsi0, [-1, 1])
|
|
495
|
-
psi_exact = backend.reshape(psi_exact, [-1])
|
|
496
|
-
psi_exact = psi_exact / backend.norm(psi_exact)
|
|
497
|
-
if callback is None:
|
|
498
|
-
return psi_exact
|
|
499
|
-
return callback(psi_exact)
|
|
500
|
-
|
|
501
|
-
return backend.stack([_evol(t) for t in tlist])
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
def evol_local(
|
|
505
|
-
c: Circuit,
|
|
506
|
-
index: Sequence[int],
|
|
507
|
-
h_fun: Callable[..., Tensor],
|
|
508
|
-
t: float,
|
|
509
|
-
*args: Any,
|
|
510
|
-
**solver_kws: Any,
|
|
511
|
-
) -> Circuit:
|
|
512
|
-
"""
|
|
513
|
-
ode evolution of time dependent Hamiltonian on circuit of given indices
|
|
514
|
-
[only jax backend support for now]
|
|
515
|
-
|
|
516
|
-
:param c: _description_
|
|
517
|
-
:type c: Circuit
|
|
518
|
-
:param index: qubit sites to evolve
|
|
519
|
-
:type index: Sequence[int]
|
|
520
|
-
:param h_fun: h_fun should return a dense Hamiltonian matrix
|
|
521
|
-
with input arguments time and *args
|
|
522
|
-
:type h_fun: Callable[..., Tensor]
|
|
523
|
-
:param t: evolution time
|
|
524
|
-
:type t: float
|
|
525
|
-
:return: _description_
|
|
526
|
-
:rtype: Circuit
|
|
527
|
-
"""
|
|
528
|
-
from jax.experimental.ode import odeint
|
|
529
|
-
|
|
530
|
-
s = c.state()
|
|
531
|
-
n = c._nqubits
|
|
532
|
-
l = len(index)
|
|
533
|
-
|
|
534
|
-
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
535
|
-
y = backend.reshape2(y)
|
|
536
|
-
y = Gate(y)
|
|
537
|
-
h = -1.0j * h_fun(t, *args)
|
|
538
|
-
h = backend.reshape2(h)
|
|
539
|
-
h = Gate(h)
|
|
540
|
-
edges = []
|
|
541
|
-
for i in range(n):
|
|
542
|
-
if i not in index:
|
|
543
|
-
edges.append(y[i])
|
|
544
|
-
else:
|
|
545
|
-
j = index.index(i)
|
|
546
|
-
edges.append(h[j])
|
|
547
|
-
h[j + l] ^ y[i]
|
|
548
|
-
y = contractor([y, h], output_edge_order=edges)
|
|
549
|
-
return backend.reshape(y.tensor, [-1])
|
|
550
|
-
|
|
551
|
-
ts = backend.stack([0.0, t])
|
|
552
|
-
ts = backend.cast(ts, dtype=rdtypestr)
|
|
553
|
-
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
554
|
-
return type(c)(n, inputs=s1[-1])
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def evol_global(
|
|
558
|
-
c: Circuit, h_fun: Callable[..., Tensor], t: float, *args: Any, **solver_kws: Any
|
|
559
|
-
) -> Circuit:
|
|
560
|
-
"""
|
|
561
|
-
ode evolution of time dependent Hamiltonian on circuit of all qubits
|
|
562
|
-
[only jax backend support for now]
|
|
563
|
-
|
|
564
|
-
:param c: _description_
|
|
565
|
-
:type c: Circuit
|
|
566
|
-
:param h_fun: h_fun should return a **SPARSE** Hamiltonian matrix
|
|
567
|
-
with input arguments time and *args
|
|
568
|
-
:type h_fun: Callable[..., Tensor]
|
|
569
|
-
:param t: _description_
|
|
570
|
-
:type t: float
|
|
571
|
-
:return: _description_
|
|
572
|
-
:rtype: Circuit
|
|
573
|
-
"""
|
|
574
|
-
from jax.experimental.ode import odeint
|
|
575
|
-
|
|
576
|
-
s = c.state()
|
|
577
|
-
n = c._nqubits
|
|
578
|
-
|
|
579
|
-
def f(y: Tensor, t: Tensor, *args: Any) -> Tensor:
|
|
580
|
-
h = -1.0j * h_fun(t, *args)
|
|
581
|
-
return backend.sparse_dense_matmul(h, y)
|
|
582
|
-
|
|
583
|
-
ts = backend.stack([0.0, t])
|
|
584
|
-
ts = backend.cast(ts, dtype=rdtypestr)
|
|
585
|
-
s1 = odeint(f, s, ts, *args, **solver_kws)
|
|
586
|
-
return type(c)(n, inputs=s1[-1])
|
|
587
|
-
|
|
588
|
-
|
|
589
444
|
def jax_jitted_function_save(filename: str, f: Callable[..., Any], *args: Any) -> None:
|
|
590
445
|
"""
|
|
591
446
|
save a jitted jax function as a file
|