tensorcircuit-nightly 1.3.0.dev20250902__py3-none-any.whl → 1.3.0.dev20250904__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/abstractcircuit.py +1 -0
- tensorcircuit/backends/abstract_backend.py +33 -5
- tensorcircuit/backends/jax_backend.py +3 -0
- tensorcircuit/backends/numpy_backend.py +3 -0
- tensorcircuit/backends/pytorch_backend.py +3 -0
- tensorcircuit/backends/tensorflow_backend.py +3 -0
- tensorcircuit/basecircuit.py +127 -75
- tensorcircuit/circuit.py +54 -52
- tensorcircuit/cons.py +1 -0
- tensorcircuit/densitymatrix.py +13 -8
- tensorcircuit/gates.py +28 -22
- tensorcircuit/mpscircuit.py +89 -59
- tensorcircuit/quantum.py +274 -105
- tensorcircuit/results/counts.py +45 -31
- tensorcircuit/simplify.py +3 -1
- tensorcircuit/timeevol.py +42 -33
- {tensorcircuit_nightly-1.3.0.dev20250902.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/METADATA +1 -1
- {tensorcircuit_nightly-1.3.0.dev20250902.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/RECORD +22 -22
- {tensorcircuit_nightly-1.3.0.dev20250902.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250902.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250902.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/top_level.txt +0 -0
tensorcircuit/mpscircuit.py
CHANGED
|
@@ -8,13 +8,14 @@ from functools import reduce, partial
|
|
|
8
8
|
from typing import Any, List, Optional, Sequence, Tuple, Dict, Union
|
|
9
9
|
from copy import copy
|
|
10
10
|
import logging
|
|
11
|
+
import types
|
|
11
12
|
|
|
12
13
|
import numpy as np
|
|
13
14
|
import tensornetwork as tn
|
|
14
15
|
|
|
15
16
|
from . import gates
|
|
16
17
|
from .cons import backend, npdtype, contractor, rdtypestr, dtypestr
|
|
17
|
-
from .quantum import QuOperator, QuVector, extract_tensors_from_qop
|
|
18
|
+
from .quantum import QuOperator, QuVector, extract_tensors_from_qop, _decode_basis_label
|
|
18
19
|
from .mps_base import FiniteMPS
|
|
19
20
|
from .abstractcircuit import AbstractCircuit
|
|
20
21
|
from .utils import arg_alias
|
|
@@ -91,12 +92,16 @@ class MPSCircuit(AbstractCircuit):
|
|
|
91
92
|
tensors: Optional[Sequence[Tensor]] = None,
|
|
92
93
|
wavefunction: Optional[Union[QuVector, Tensor]] = None,
|
|
93
94
|
split: Optional[Dict[str, Any]] = None,
|
|
95
|
+
dim: Optional[int] = None,
|
|
94
96
|
) -> None:
|
|
95
97
|
"""
|
|
96
98
|
MPSCircuit object based on state simulator.
|
|
99
|
+
Do not use this class with d!=2 directly
|
|
97
100
|
|
|
98
101
|
:param nqubits: The number of qubits in the circuit.
|
|
99
102
|
:type nqubits: int
|
|
103
|
+
:param dim: The local Hilbert space dimension per site. Qudit is supported for 2 <= d <= 36.
|
|
104
|
+
:type dim: If None, the dimension of the circuit will be `2`, which is a qubit system.
|
|
100
105
|
:param center_position: The center position of MPS, default to 0
|
|
101
106
|
:type center_position: int, optional
|
|
102
107
|
:param tensors: If not None, the initial state of the circuit is taken as ``tensors``
|
|
@@ -109,6 +114,7 @@ class MPSCircuit(AbstractCircuit):
|
|
|
109
114
|
:param split: Split rules
|
|
110
115
|
:type split: Any
|
|
111
116
|
"""
|
|
117
|
+
self._d = 2 if dim is None else dim
|
|
112
118
|
self.circuit_param = {
|
|
113
119
|
"nqubits": nqubits,
|
|
114
120
|
"center_position": center_position,
|
|
@@ -137,7 +143,9 @@ class MPSCircuit(AbstractCircuit):
|
|
|
137
143
|
wavefunction, split=self.split
|
|
138
144
|
)
|
|
139
145
|
else: # full wavefunction
|
|
140
|
-
tensors = self.wavefunction_to_tensors(
|
|
146
|
+
tensors = self.wavefunction_to_tensors(
|
|
147
|
+
wavefunction, dim_phys=self._d, split=self.split
|
|
148
|
+
)
|
|
141
149
|
assert len(tensors) == nqubits
|
|
142
150
|
self._mps = FiniteMPS(tensors, canonicalize=False)
|
|
143
151
|
self._mps.center_position = 0
|
|
@@ -151,8 +159,13 @@ class MPSCircuit(AbstractCircuit):
|
|
|
151
159
|
self._mps = FiniteMPS(tensors, canonicalize=True, center_position=0)
|
|
152
160
|
else:
|
|
153
161
|
tensors = [
|
|
154
|
-
np.
|
|
155
|
-
|
|
162
|
+
np.concatenate(
|
|
163
|
+
[
|
|
164
|
+
np.array([1.0], dtype=npdtype),
|
|
165
|
+
np.zeros((self._d - 1,), dtype=npdtype),
|
|
166
|
+
]
|
|
167
|
+
)[None, :, None]
|
|
168
|
+
for _ in range(nqubits)
|
|
156
169
|
]
|
|
157
170
|
self._mps = FiniteMPS(tensors, canonicalize=False)
|
|
158
171
|
if center_position is not None:
|
|
@@ -370,42 +383,51 @@ class MPSCircuit(AbstractCircuit):
|
|
|
370
383
|
# b
|
|
371
384
|
|
|
372
385
|
# index must be ordered
|
|
373
|
-
|
|
374
|
-
|
|
386
|
+
if len(index) == 0:
|
|
387
|
+
raise ValueError("`index` must contain at least one site.")
|
|
388
|
+
if not all(index[i] < index[i + 1] for i in range(len(index) - 1)):
|
|
389
|
+
raise ValueError("`index` must be strictly increasing.")
|
|
390
|
+
|
|
391
|
+
index_left = int(np.min(index))
|
|
375
392
|
if isinstance(gate, tn.Node):
|
|
376
393
|
gate = backend.copy(gate.tensor)
|
|
377
|
-
|
|
394
|
+
|
|
378
395
|
nindex = len(index)
|
|
396
|
+
in_dims = tuple(backend.shape_tuple(gate))[:nindex]
|
|
397
|
+
dim = int(in_dims[0])
|
|
398
|
+
dim_phys_mpo = dim * dim
|
|
399
|
+
gate = backend.reshape(gate, (dim,) * nindex + (dim,) * nindex)
|
|
379
400
|
# transform gate from (in1, in2, ..., out1, out2 ...) to
|
|
380
401
|
# (in1, out1, in2, out2, ...)
|
|
381
|
-
order = tuple(np.arange(2 * nindex).reshape(
|
|
382
|
-
|
|
383
|
-
gate = backend.reshape(backend.transpose(gate, order), shape)
|
|
384
|
-
argsort = np.argsort(index)
|
|
402
|
+
order = tuple(np.arange(2 * nindex).reshape(2, nindex).T.flatten().tolist())
|
|
403
|
+
gate = backend.transpose(gate, order)
|
|
385
404
|
# reorder the gate according to the site positions
|
|
386
|
-
gate = backend.
|
|
387
|
-
index = index[argsort] # type: ignore
|
|
405
|
+
gate = backend.reshape(gate, (dim_phys_mpo,) * nindex)
|
|
388
406
|
# split the gate into tensors assuming they are adjacent
|
|
389
|
-
main_tensors = cls.wavefunction_to_tensors(
|
|
407
|
+
main_tensors = cls.wavefunction_to_tensors(
|
|
408
|
+
gate, dim_phys=dim_phys_mpo, norm=False
|
|
409
|
+
)
|
|
390
410
|
# each tensor is in shape of (i, a, b, j)
|
|
391
|
-
tensors = []
|
|
392
|
-
previous_i = None
|
|
393
|
-
|
|
394
|
-
|
|
411
|
+
tensors: list[Tensor] = []
|
|
412
|
+
previous_i: Optional[int] = None
|
|
413
|
+
index_arr = np.array(index, dtype=int) - index_left
|
|
414
|
+
|
|
415
|
+
for i, main_tensor in zip(index_arr, main_tensors):
|
|
395
416
|
if previous_i is not None:
|
|
396
|
-
for
|
|
397
|
-
bond_dim = tensors[-1]
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
.reshape((bond_dim, 2, bond_dim, 2))
|
|
401
|
-
.transpose((0, 1, 3, 2))
|
|
402
|
-
.astype(dtypestr)
|
|
417
|
+
for _gap_site in range(int(previous_i) + 1, int(i)):
|
|
418
|
+
bond_dim = int(backend.shape_tuple(tensors[-1])[-1])
|
|
419
|
+
eye2d = backend.eye(
|
|
420
|
+
bond_dim * dim, dtype=backend.dtype(tensors[-1])
|
|
403
421
|
)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
422
|
+
I4 = backend.reshape(eye2d, (bond_dim, dim, bond_dim, dim))
|
|
423
|
+
I4 = backend.transpose(I4, (0, 1, 3, 2))
|
|
424
|
+
tensors.append(I4)
|
|
425
|
+
|
|
426
|
+
nleft, _, nright = backend.shape_tuple(main_tensor)
|
|
427
|
+
tensor = backend.reshape(main_tensor, (int(nleft), dim, dim, int(nright)))
|
|
407
428
|
tensors.append(tensor)
|
|
408
|
-
previous_i = i
|
|
429
|
+
previous_i = int(i)
|
|
430
|
+
|
|
409
431
|
return tensors, index_left
|
|
410
432
|
|
|
411
433
|
@classmethod
|
|
@@ -448,15 +470,15 @@ class MPSCircuit(AbstractCircuit):
|
|
|
448
470
|
"""
|
|
449
471
|
if split is None:
|
|
450
472
|
split = {}
|
|
451
|
-
ni = tensor_left.shape[0]
|
|
452
|
-
nk = tensor_right.shape[-1]
|
|
473
|
+
ni, di = tensor_left.shape[0], tensor_right.shape[1]
|
|
474
|
+
nk, dk = tensor_right.shape[-1], tensor_right.shape[-2]
|
|
453
475
|
T = backend.einsum("iaj,jbk->iabk", tensor_left, tensor_right)
|
|
454
|
-
T = backend.reshape(T, (ni *
|
|
476
|
+
T = backend.reshape(T, (ni * di, nk * dk))
|
|
455
477
|
new_tensor_left, new_tensor_right = split_tensor(
|
|
456
478
|
T, center_left=center_left, split=split
|
|
457
479
|
)
|
|
458
|
-
new_tensor_left = backend.reshape(new_tensor_left, (ni,
|
|
459
|
-
new_tensor_right = backend.reshape(new_tensor_right, (-1,
|
|
480
|
+
new_tensor_left = backend.reshape(new_tensor_left, (ni, di, -1))
|
|
481
|
+
new_tensor_right = backend.reshape(new_tensor_right, (-1, dk, nk))
|
|
460
482
|
return new_tensor_left, new_tensor_right
|
|
461
483
|
|
|
462
484
|
def reduce_dimension(
|
|
@@ -550,10 +572,11 @@ class MPSCircuit(AbstractCircuit):
|
|
|
550
572
|
for i, idx in zip(i_list, idx_list):
|
|
551
573
|
O = tensors[i]
|
|
552
574
|
T = self._mps.tensors[idx]
|
|
553
|
-
ni,
|
|
575
|
+
ni, d_in, _, nj = O.shape
|
|
554
576
|
nk, _, nl = T.shape
|
|
555
577
|
OT = backend.einsum("iabj,kbl->ikajl", O, T)
|
|
556
|
-
OT = backend.reshape(OT, (ni * nk,
|
|
578
|
+
OT = backend.reshape(OT, (ni * nk, d_in, nj * nl))
|
|
579
|
+
|
|
557
580
|
self._mps.tensors[idx] = OT
|
|
558
581
|
|
|
559
582
|
# canonicalize
|
|
@@ -660,8 +683,7 @@ class MPSCircuit(AbstractCircuit):
|
|
|
660
683
|
:type keep: int, optional
|
|
661
684
|
"""
|
|
662
685
|
# normalization not guaranteed
|
|
663
|
-
|
|
664
|
-
gate = backend.zeros((2, 2), dtype=dtypestr)
|
|
686
|
+
gate = backend.zeros((self._d, self._d), dtype=dtypestr)
|
|
665
687
|
gate = backend.scatter(
|
|
666
688
|
gate,
|
|
667
689
|
backend.convert_to_tensor([[keep, keep]]),
|
|
@@ -692,7 +714,7 @@ class MPSCircuit(AbstractCircuit):
|
|
|
692
714
|
def wavefunction_to_tensors(
|
|
693
715
|
cls,
|
|
694
716
|
wavefunction: Tensor,
|
|
695
|
-
dim_phys: int =
|
|
717
|
+
dim_phys: Optional[int] = None,
|
|
696
718
|
norm: bool = True,
|
|
697
719
|
split: Optional[Dict[str, Any]] = None,
|
|
698
720
|
) -> List[Tensor]:
|
|
@@ -710,6 +732,7 @@ class MPSCircuit(AbstractCircuit):
|
|
|
710
732
|
:return: The tensors
|
|
711
733
|
:rtype: List[Tensor]
|
|
712
734
|
"""
|
|
735
|
+
dim_phys = dim_phys if dim_phys is not None else 2
|
|
713
736
|
if split is None:
|
|
714
737
|
split = {}
|
|
715
738
|
wavefunction = backend.reshape(wavefunction, (-1, 1))
|
|
@@ -768,10 +791,16 @@ class MPSCircuit(AbstractCircuit):
|
|
|
768
791
|
for key in vars(self):
|
|
769
792
|
if key == "_mps":
|
|
770
793
|
continue
|
|
771
|
-
|
|
772
|
-
|
|
794
|
+
val = info[key]
|
|
795
|
+
if backend.is_tensor(val):
|
|
796
|
+
copied_value = backend.copy(val)
|
|
797
|
+
elif isinstance(val, types.ModuleType):
|
|
798
|
+
copied_value = val
|
|
773
799
|
else:
|
|
774
|
-
|
|
800
|
+
try:
|
|
801
|
+
copied_value = copy(val)
|
|
802
|
+
except TypeError:
|
|
803
|
+
copied_value = val
|
|
775
804
|
setattr(result, key, copied_value)
|
|
776
805
|
return result
|
|
777
806
|
|
|
@@ -815,7 +844,8 @@ class MPSCircuit(AbstractCircuit):
|
|
|
815
844
|
|
|
816
845
|
def amplitude(self, l: str) -> Tensor:
|
|
817
846
|
assert len(l) == self._nqubits
|
|
818
|
-
|
|
847
|
+
idx_list = _decode_basis_label(l, n=self._nqubits, dim=self._d)
|
|
848
|
+
tensors = [self._mps.tensors[i][:, idx, :] for i, idx in enumerate(idx_list)]
|
|
819
849
|
return reduce(backend.matmul, tensors)[0, 0]
|
|
820
850
|
|
|
821
851
|
def proj_with_mps(self, other: "MPSCircuit", conj: bool = True) -> Tensor:
|
|
@@ -873,6 +903,7 @@ class MPSCircuit(AbstractCircuit):
|
|
|
873
903
|
|
|
874
904
|
mps = self.__class__(
|
|
875
905
|
nqubits,
|
|
906
|
+
dim=self._d,
|
|
876
907
|
tensors=tensors,
|
|
877
908
|
center_position=center_position,
|
|
878
909
|
split=self.split.copy(),
|
|
@@ -1000,36 +1031,35 @@ class MPSCircuit(AbstractCircuit):
|
|
|
1000
1031
|
# set the center to the left side, then gradually move to the right and do measurement at sites
|
|
1001
1032
|
"""
|
|
1002
1033
|
mps = self.copy()
|
|
1003
|
-
up = backend.convert_to_tensor(np.array([1, 0]).astype(dtypestr))
|
|
1004
|
-
down = backend.convert_to_tensor(np.array([0, 1]).astype(dtypestr))
|
|
1005
1034
|
|
|
1006
1035
|
p = 1.0
|
|
1007
1036
|
p = backend.convert_to_tensor(p)
|
|
1008
1037
|
p = backend.cast(p, dtype=rdtypestr)
|
|
1009
|
-
sample = []
|
|
1038
|
+
sample: Tensor = []
|
|
1010
1039
|
for k, site in enumerate(index):
|
|
1011
1040
|
mps.position(site)
|
|
1012
|
-
# do measurement
|
|
1013
1041
|
tensor = mps._mps.tensors[site]
|
|
1014
1042
|
ps = backend.real(
|
|
1015
1043
|
backend.einsum("iaj,iaj->a", tensor, backend.conj(tensor))
|
|
1016
1044
|
)
|
|
1017
1045
|
ps /= backend.sum(ps)
|
|
1018
|
-
pu = ps[0]
|
|
1019
1046
|
if status is None:
|
|
1020
|
-
|
|
1047
|
+
outcome = backend.implicit_randc(
|
|
1048
|
+
self._d, shape=1, p=backend.cast(ps, rdtypestr)
|
|
1049
|
+
)[0]
|
|
1021
1050
|
else:
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
m =
|
|
1051
|
+
one_r = backend.cast(backend.convert_to_tensor(1.0), rdtypestr)
|
|
1052
|
+
st = backend.cast(status[k : k + 1], rdtypestr)
|
|
1053
|
+
ind = backend.probability_sample(
|
|
1054
|
+
shots=1, p=backend.cast(ps, rdtypestr), status=one_r - st
|
|
1055
|
+
)
|
|
1056
|
+
outcome = backend.cast(ind[0], "int32")
|
|
1057
|
+
|
|
1058
|
+
p = p * ps[outcome]
|
|
1059
|
+
basis = backend.convert_to_tensor(np.eye(self._d).astype(dtypestr))
|
|
1060
|
+
m = basis[outcome]
|
|
1032
1061
|
mps._mps.tensors[site] = backend.einsum("iaj,a->ij", tensor, m)[:, None, :]
|
|
1062
|
+
sample.append(outcome)
|
|
1033
1063
|
sample = backend.stack(sample)
|
|
1034
1064
|
sample = backend.real(sample)
|
|
1035
1065
|
if with_prob:
|