Trajectree 0.0.0__py3-none-any.whl → 0.0.1__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.
- trajectree/__init__.py +3 -0
- trajectree/fock_optics/devices.py +1 -1
- trajectree/fock_optics/light_sources.py +2 -2
- trajectree/fock_optics/measurement.py +3 -3
- trajectree/fock_optics/utils.py +6 -6
- trajectree/quimb/docs/_pygments/_pygments_dark.py +118 -0
- trajectree/quimb/docs/_pygments/_pygments_light.py +118 -0
- trajectree/quimb/docs/conf.py +158 -0
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +62 -0
- trajectree/quimb/quimb/__init__.py +507 -0
- trajectree/quimb/quimb/calc.py +1491 -0
- trajectree/quimb/quimb/core.py +2279 -0
- trajectree/quimb/quimb/evo.py +712 -0
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +129 -0
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +109 -0
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +397 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +653 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +571 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +775 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +537 -0
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +194 -0
- trajectree/quimb/quimb/experimental/cluster_update.py +286 -0
- trajectree/quimb/quimb/experimental/merabuilder.py +865 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +15 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +1631 -0
- trajectree/quimb/quimb/experimental/schematic.py +7 -0
- trajectree/quimb/quimb/experimental/tn_marginals.py +130 -0
- trajectree/quimb/quimb/experimental/tnvmc.py +1483 -0
- trajectree/quimb/quimb/gates.py +36 -0
- trajectree/quimb/quimb/gen/__init__.py +2 -0
- trajectree/quimb/quimb/gen/operators.py +1167 -0
- trajectree/quimb/quimb/gen/rand.py +713 -0
- trajectree/quimb/quimb/gen/states.py +479 -0
- trajectree/quimb/quimb/linalg/__init__.py +6 -0
- trajectree/quimb/quimb/linalg/approx_spectral.py +1109 -0
- trajectree/quimb/quimb/linalg/autoblock.py +258 -0
- trajectree/quimb/quimb/linalg/base_linalg.py +719 -0
- trajectree/quimb/quimb/linalg/mpi_launcher.py +397 -0
- trajectree/quimb/quimb/linalg/numpy_linalg.py +244 -0
- trajectree/quimb/quimb/linalg/rand_linalg.py +514 -0
- trajectree/quimb/quimb/linalg/scipy_linalg.py +293 -0
- trajectree/quimb/quimb/linalg/slepc_linalg.py +892 -0
- trajectree/quimb/quimb/schematic.py +1518 -0
- trajectree/quimb/quimb/tensor/__init__.py +401 -0
- trajectree/quimb/quimb/tensor/array_ops.py +610 -0
- trajectree/quimb/quimb/tensor/circuit.py +4824 -0
- trajectree/quimb/quimb/tensor/circuit_gen.py +411 -0
- trajectree/quimb/quimb/tensor/contraction.py +336 -0
- trajectree/quimb/quimb/tensor/decomp.py +1255 -0
- trajectree/quimb/quimb/tensor/drawing.py +1646 -0
- trajectree/quimb/quimb/tensor/fitting.py +385 -0
- trajectree/quimb/quimb/tensor/geometry.py +583 -0
- trajectree/quimb/quimb/tensor/interface.py +114 -0
- trajectree/quimb/quimb/tensor/networking.py +1058 -0
- trajectree/quimb/quimb/tensor/optimize.py +1818 -0
- trajectree/quimb/quimb/tensor/tensor_1d.py +4778 -0
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +1854 -0
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +662 -0
- trajectree/quimb/quimb/tensor/tensor_2d.py +5954 -0
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +96 -0
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +1230 -0
- trajectree/quimb/quimb/tensor/tensor_3d.py +2869 -0
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +46 -0
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +60 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +3237 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +565 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +1138 -0
- trajectree/quimb/quimb/tensor/tensor_builder.py +5411 -0
- trajectree/quimb/quimb/tensor/tensor_core.py +11179 -0
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +1472 -0
- trajectree/quimb/quimb/tensor/tensor_mera.py +204 -0
- trajectree/quimb/quimb/utils.py +892 -0
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +501 -0
- trajectree/quimb/tests/test_calc.py +788 -0
- trajectree/quimb/tests/test_core.py +847 -0
- trajectree/quimb/tests/test_evo.py +565 -0
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +361 -0
- trajectree/quimb/tests/test_gen/test_rand.py +296 -0
- trajectree/quimb/tests/test_gen/test_states.py +261 -0
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +368 -0
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +351 -0
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +127 -0
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +84 -0
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +134 -0
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +283 -0
- trajectree/quimb/tests/test_tensor/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +39 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +67 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +64 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +51 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +142 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +101 -0
- trajectree/quimb/tests/test_tensor/test_circuit.py +816 -0
- trajectree/quimb/tests/test_tensor/test_contract.py +67 -0
- trajectree/quimb/tests/test_tensor/test_decomp.py +40 -0
- trajectree/quimb/tests/test_tensor/test_mera.py +52 -0
- trajectree/quimb/tests/test_tensor/test_optimizers.py +488 -0
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +1171 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +606 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +144 -0
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +123 -0
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +226 -0
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +441 -0
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +2066 -0
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +388 -0
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +63 -0
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +270 -0
- trajectree/quimb/tests/test_utils.py +85 -0
- trajectree/trajectory.py +2 -2
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/METADATA +2 -2
- trajectree-0.0.1.dist-info/RECORD +126 -0
- trajectree-0.0.0.dist-info/RECORD +0 -16
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/WHEEL +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
"""Tools for performing TEBD like algorithms in 1D."""
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from autoray import do
|
|
7
|
+
|
|
8
|
+
from ..utils import ensure_dict, continuous_progbar, deprecated
|
|
9
|
+
from ..utils import progbar as Progbar
|
|
10
|
+
from .array_ops import norm_fro
|
|
11
|
+
from .tensor_arbgeom_tebd import LocalHamGen
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LocalHam1D(LocalHamGen):
|
|
15
|
+
"""An simple interacting hamiltonian object used, for instance, in TEBD.
|
|
16
|
+
Once instantiated, the ``LocalHam1D`` hamiltonian stores a single term
|
|
17
|
+
per pair of sites, cached versions of which can be retrieved like
|
|
18
|
+
``H.get_gate_expm((i, i + 1), -1j * 0.5)`` etc.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
L : int
|
|
23
|
+
The size of the hamiltonian.
|
|
24
|
+
H2 : array_like or dict[tuple[int], array_like]
|
|
25
|
+
The sum of interaction terms. If a dict is given, the keys should be
|
|
26
|
+
nearest neighbours like ``(10, 11)``, apart from any default term which
|
|
27
|
+
should have the key ``None``, and the values should be the sum of
|
|
28
|
+
interaction terms for that interaction.
|
|
29
|
+
H1 : array_like or dict[int, array_like], optional
|
|
30
|
+
The sum of single site terms. If a dict is given, the keys should be
|
|
31
|
+
integer sites, apart from any default term which should have the key
|
|
32
|
+
``None``, and the values should be the sum of single site terms for
|
|
33
|
+
that site.
|
|
34
|
+
cyclic : bool, optional
|
|
35
|
+
Whether the hamiltonian has periodic boundary conditions or not.
|
|
36
|
+
|
|
37
|
+
Attributes
|
|
38
|
+
----------
|
|
39
|
+
terms : dict[tuple[int], array]
|
|
40
|
+
The terms in the hamiltonian, combined from the inputs such that there
|
|
41
|
+
is a single term per pair.
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
A simple, translationally invariant, interaction-only ``LocalHam1D``::
|
|
46
|
+
|
|
47
|
+
>>> XX = pauli('X') & pauli('X')
|
|
48
|
+
>>> YY = pauli('Y') & pauli('Y')
|
|
49
|
+
>>> ham = LocalHam1D(L=100, H2=XX + YY)
|
|
50
|
+
|
|
51
|
+
The same, but with a translationally invariant field as well::
|
|
52
|
+
|
|
53
|
+
>>> Z = pauli('Z')
|
|
54
|
+
>>> ham = LocalHam1D(L=100, H2=XX + YY, H1=Z)
|
|
55
|
+
|
|
56
|
+
Specifying a default interaction and field, with custom values set for some
|
|
57
|
+
sites::
|
|
58
|
+
|
|
59
|
+
>>> H2 = {None: XX + YY, (49, 50): (XX + YY) / 2}
|
|
60
|
+
>>> H1 = {None: Z, 49: 2 * Z, 50: 2 * Z}
|
|
61
|
+
>>> ham = LocalHam1D(L=100, H2=H2, H1=H1)
|
|
62
|
+
|
|
63
|
+
Specifying the hamiltonian entirely through site specific interactions and
|
|
64
|
+
fields::
|
|
65
|
+
|
|
66
|
+
>>> H2 = {(i, i + 1): XX + YY for i in range(99)}
|
|
67
|
+
>>> H1 = {i: Z for i in range(100)}
|
|
68
|
+
>>> ham = LocalHam1D(L=100, H2=H2, H1=H1)
|
|
69
|
+
|
|
70
|
+
See Also
|
|
71
|
+
--------
|
|
72
|
+
SpinHam1D
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, L, H2, H1=None, cyclic=False):
|
|
76
|
+
self.L = int(L)
|
|
77
|
+
self.cyclic = cyclic
|
|
78
|
+
|
|
79
|
+
# parse two site terms
|
|
80
|
+
if hasattr(H2, "shape"):
|
|
81
|
+
# use as default nearest neighbour term
|
|
82
|
+
H2 = {None: H2}
|
|
83
|
+
else:
|
|
84
|
+
H2 = dict(H2)
|
|
85
|
+
|
|
86
|
+
default_H2 = H2.pop(None, None)
|
|
87
|
+
if default_H2 is not None:
|
|
88
|
+
for i in range(self.L + int(self.cyclic) - 1):
|
|
89
|
+
coo_a = i
|
|
90
|
+
coo_b = (i + 1) % self.L
|
|
91
|
+
if (coo_a, coo_b) not in H2 and (coo_b, coo_a) not in H2:
|
|
92
|
+
H2[coo_a, coo_b] = default_H2
|
|
93
|
+
|
|
94
|
+
super().__init__(H2=H2, H1=H1)
|
|
95
|
+
|
|
96
|
+
def mean_norm(self):
|
|
97
|
+
"""Computes the average frobenius norm of local terms."""
|
|
98
|
+
return sum(norm_fro(h) for h in self.terms.values()) / len(self.terms)
|
|
99
|
+
|
|
100
|
+
def build_mpo_propagator_trotterized(
|
|
101
|
+
self,
|
|
102
|
+
x,
|
|
103
|
+
site_tag_id="I{}",
|
|
104
|
+
tags=None,
|
|
105
|
+
upper_ind_id="k{}",
|
|
106
|
+
lower_ind_id="b{}",
|
|
107
|
+
shape="lrud",
|
|
108
|
+
contract_sites=True,
|
|
109
|
+
**split_opts,
|
|
110
|
+
):
|
|
111
|
+
"""Build an MPO representation of ``expm(H * x)``, i.e. the imaginary
|
|
112
|
+
or real time propagator of this local 1D hamiltonian, using a first
|
|
113
|
+
order trotterized decomposition.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
x : float
|
|
118
|
+
The time to evolve for. Note this does **not** include the
|
|
119
|
+
imaginary prefactor of the Schrodinger equation, so real ``x``
|
|
120
|
+
corresponds to imaginary time evolution, and vice versa.
|
|
121
|
+
site_tag_id : str
|
|
122
|
+
A string specifiying how to tag the tensors at each site. Should
|
|
123
|
+
contain a ``'{}'`` placeholder. It is used to generate the actual tags
|
|
124
|
+
like: ``map(site_tag_id.format, range(len(arrays)))``.
|
|
125
|
+
tags : str or sequence of str, optional
|
|
126
|
+
Global tags to attach to all tensors.
|
|
127
|
+
upper_ind_id : str
|
|
128
|
+
A string specifiying how to label the upper physical site indices.
|
|
129
|
+
Should contain a ``'{}'`` placeholder. It is used to generate the
|
|
130
|
+
actual indices like:
|
|
131
|
+
``map(upper_ind_id.format, range(len(arrays)))``.
|
|
132
|
+
lower_ind_id : str
|
|
133
|
+
A string specifiying how to label the lower physical site indices.
|
|
134
|
+
Should contain a ``'{}'`` placeholder. It is used to generate the
|
|
135
|
+
actual indices like:
|
|
136
|
+
``map(lower_ind_id.format, range(len(arrays)))``.
|
|
137
|
+
shape : str, optional
|
|
138
|
+
String specifying layout of the tensors. E.g. 'lrud' (the default)
|
|
139
|
+
indicates the shape corresponds left-bond, right-bond, 'up'
|
|
140
|
+
physical index, 'down' physical index.
|
|
141
|
+
End tensors have either 'l' or 'r' dropped from the string if not
|
|
142
|
+
periodic.
|
|
143
|
+
contract_sites : bool, optional
|
|
144
|
+
Whether to contract all the decomposed factors at each site to
|
|
145
|
+
yield a single tensor per site, by default True.
|
|
146
|
+
split_opts
|
|
147
|
+
Supplied to :func:`~quimb.tensor.tensor_core.tensor_split`.
|
|
148
|
+
"""
|
|
149
|
+
from .tensor_core import Tensor
|
|
150
|
+
from .tensor_1d import MatrixProductOperator
|
|
151
|
+
|
|
152
|
+
mpo = MatrixProductOperator.new(
|
|
153
|
+
L=self.L,
|
|
154
|
+
site_tag_id=site_tag_id,
|
|
155
|
+
upper_ind_id=upper_ind_id,
|
|
156
|
+
lower_ind_id=lower_ind_id,
|
|
157
|
+
cyclic=self.cyclic,
|
|
158
|
+
)
|
|
159
|
+
imax = self.L - (not self.cyclic)
|
|
160
|
+
|
|
161
|
+
# process even bonds then odd bonds
|
|
162
|
+
layered_sites = itertools.chain(range(0, imax, 2), range(1, imax, 2))
|
|
163
|
+
|
|
164
|
+
# process even bonds
|
|
165
|
+
for i in layered_sites:
|
|
166
|
+
j = (i + 1) % self.L
|
|
167
|
+
|
|
168
|
+
# get a tensor of the local exponentiated term
|
|
169
|
+
U = self.get_gate_expm((i, j), x)
|
|
170
|
+
U = do("reshape", U, (2, 2, 2, 2))
|
|
171
|
+
|
|
172
|
+
ki = upper_ind_id.format(i)
|
|
173
|
+
kj = upper_ind_id.format(j)
|
|
174
|
+
bi = lower_ind_id.format(i)
|
|
175
|
+
bj = lower_ind_id.format(j)
|
|
176
|
+
|
|
177
|
+
# split spatially
|
|
178
|
+
tnU = Tensor(
|
|
179
|
+
data=U,
|
|
180
|
+
inds=(ki, kj, bi, bj),
|
|
181
|
+
).split(
|
|
182
|
+
left_inds=(ki, bi),
|
|
183
|
+
ltags=site_tag_id.format(i),
|
|
184
|
+
rtags=site_tag_id.format(j),
|
|
185
|
+
**split_opts,
|
|
186
|
+
)
|
|
187
|
+
# add tensors to mpo
|
|
188
|
+
mpo.gate_inds_with_tn_(
|
|
189
|
+
inds=(ki, kj),
|
|
190
|
+
gate=tnU,
|
|
191
|
+
gate_inds_inner=(bi, bj),
|
|
192
|
+
gate_inds_outer=(ki, kj),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if contract_sites:
|
|
196
|
+
# combine site groups into single tensors
|
|
197
|
+
for st in mpo.site_tags:
|
|
198
|
+
mpo ^= st
|
|
199
|
+
|
|
200
|
+
if tags is not None:
|
|
201
|
+
# global tags
|
|
202
|
+
mpo.add_tag(tags)
|
|
203
|
+
|
|
204
|
+
if shape is not None:
|
|
205
|
+
# enforce a canonical ordering of indices within each tensor
|
|
206
|
+
mpo.permute_arrays(shape)
|
|
207
|
+
|
|
208
|
+
return mpo
|
|
209
|
+
|
|
210
|
+
def __repr__(self):
|
|
211
|
+
return f"<LocalHam1D(L={self.L}, cyclic={self.cyclic})>"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
NNI = deprecated(LocalHam1D, "NNI", "LocalHam1D")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class TEBD:
|
|
218
|
+
"""Class implementing Time Evolving Block Decimation (TEBD) [1].
|
|
219
|
+
|
|
220
|
+
[1] Guifré Vidal, Efficient Classical Simulation of Slightly Entangled
|
|
221
|
+
Quantum Computations, PRL 91, 147902 (2003)
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
p0 : MatrixProductState
|
|
226
|
+
Initial state.
|
|
227
|
+
H : LocalHam1D or array_like
|
|
228
|
+
Dense hamiltonian representing the two body interaction. Should have
|
|
229
|
+
shape ``(d * d, d * d)``, where ``d`` is the physical dimension of
|
|
230
|
+
``p0``.
|
|
231
|
+
dt : float, optional
|
|
232
|
+
Default time step, cannot be set as well as ``tol``.
|
|
233
|
+
tol : float, optional
|
|
234
|
+
Default target error for each evolution, cannot be set as well as
|
|
235
|
+
``dt``, which will instead be calculated from the trotter orderm length
|
|
236
|
+
of time, and hamiltonian norm.
|
|
237
|
+
t0 : float, optional
|
|
238
|
+
Initial time. Defaults to 0.0.
|
|
239
|
+
split_opts : dict, optional
|
|
240
|
+
Compression options applied for splitting after gate application, see
|
|
241
|
+
:func:`~quimb.tensor.tensor_core.tensor_split`.
|
|
242
|
+
imag : bool, optional
|
|
243
|
+
Enable imaginary time evolution. Defaults to false.
|
|
244
|
+
|
|
245
|
+
See Also
|
|
246
|
+
--------
|
|
247
|
+
quimb.Evolution
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
def __init__(
|
|
251
|
+
self,
|
|
252
|
+
p0,
|
|
253
|
+
H,
|
|
254
|
+
dt=None,
|
|
255
|
+
tol=None,
|
|
256
|
+
t0=0.0,
|
|
257
|
+
split_opts=None,
|
|
258
|
+
progbar=True,
|
|
259
|
+
imag=False,
|
|
260
|
+
):
|
|
261
|
+
# prepare initial state
|
|
262
|
+
self._pt = p0.canonicalize(0)
|
|
263
|
+
self.L = self._pt.L
|
|
264
|
+
|
|
265
|
+
# handle hamiltonian -> convert array to LocalHam1D
|
|
266
|
+
if isinstance(H, np.ndarray):
|
|
267
|
+
H = LocalHam1D(L=self.L, H2=H, cyclic=p0.cyclic)
|
|
268
|
+
|
|
269
|
+
if not isinstance(H, LocalHam1D):
|
|
270
|
+
raise TypeError(
|
|
271
|
+
"``H`` should be a ``LocalHam1D`` or 2-site "
|
|
272
|
+
"array, not a TensorNetwork of any form."
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if p0.cyclic != H.cyclic:
|
|
276
|
+
raise ValueError(
|
|
277
|
+
"Both ``p0`` and ``H`` should have matching OBC " "or PBC."
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
self.H = H
|
|
281
|
+
self.cyclic = H.cyclic
|
|
282
|
+
self._ham_norm = H.mean_norm()
|
|
283
|
+
self._err = 0.0
|
|
284
|
+
|
|
285
|
+
# set time and tolerance defaults
|
|
286
|
+
self.t0 = self.t = t0
|
|
287
|
+
if dt and tol:
|
|
288
|
+
raise ValueError("Can't set default for both ``dt`` and ``tol``.")
|
|
289
|
+
self.dt = self._dt = dt
|
|
290
|
+
self.tol = tol
|
|
291
|
+
self.imag = imag
|
|
292
|
+
|
|
293
|
+
# misc other options
|
|
294
|
+
self.progbar = progbar
|
|
295
|
+
self.split_opts = ensure_dict(split_opts)
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def pt(self):
|
|
299
|
+
"""The MPS state of the system at the current time."""
|
|
300
|
+
return self._pt.copy()
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def err(self):
|
|
304
|
+
return self._err
|
|
305
|
+
|
|
306
|
+
def choose_time_step(self, tol, T, order):
|
|
307
|
+
"""Trotter error is ``~ (T / dt) * dt^(order + 1)``. Invert to
|
|
308
|
+
find desired time step, and scale by norm of interaction term.
|
|
309
|
+
"""
|
|
310
|
+
return (tol / (T * self._ham_norm)) ** (1 / order)
|
|
311
|
+
|
|
312
|
+
def _get_gate_from_ham(self, dt_frac, sites):
|
|
313
|
+
"""Get the unitary (exponentiated) gate for fraction of timestep
|
|
314
|
+
``dt_frac`` and sites ``sites``, cached.
|
|
315
|
+
"""
|
|
316
|
+
imag_factor = 1.0 if self.imag else 1.0j
|
|
317
|
+
return self.H.get_gate_expm(sites, -imag_factor * self._dt * dt_frac)
|
|
318
|
+
|
|
319
|
+
def sweep(self, direction, dt_frac, dt=None, queue=False):
|
|
320
|
+
"""Perform a single sweep of gates and compression. This shifts the
|
|
321
|
+
orthonognality centre along with the gates as they are applied and
|
|
322
|
+
split.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
direction : {'right', 'left'}
|
|
327
|
+
Which direction to sweep. Right is even bonds, left is odd.
|
|
328
|
+
dt_frac : float
|
|
329
|
+
What fraction of dt substep to take.
|
|
330
|
+
dt : float, optional
|
|
331
|
+
Overide the current ``dt`` with a custom value.
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
# if custom dt set, scale the dt fraction
|
|
335
|
+
if dt is not None:
|
|
336
|
+
dt_frac *= dt / self._dt
|
|
337
|
+
|
|
338
|
+
# ------ automatically combine consecutive sweeps of same time ------ #
|
|
339
|
+
|
|
340
|
+
if not hasattr(self, "_queued_sweep"):
|
|
341
|
+
self._queued_sweep = None
|
|
342
|
+
|
|
343
|
+
if queue:
|
|
344
|
+
# check for queued sweep
|
|
345
|
+
if self._queued_sweep:
|
|
346
|
+
# if matches, combine and continue
|
|
347
|
+
if direction == self._queued_sweep[0]:
|
|
348
|
+
self._queued_sweep[1] += dt_frac
|
|
349
|
+
return
|
|
350
|
+
# else perform the old, queue the new
|
|
351
|
+
else:
|
|
352
|
+
new_queued_sweep = [direction, dt_frac]
|
|
353
|
+
direction, dt_frac = self._queued_sweep
|
|
354
|
+
self._queued_sweep = new_queued_sweep
|
|
355
|
+
|
|
356
|
+
# just queue the new sweep
|
|
357
|
+
else:
|
|
358
|
+
self._queued_sweep = [direction, dt_frac]
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
# check if need to drain the queue first
|
|
362
|
+
elif self._queued_sweep:
|
|
363
|
+
queued_direction, queued_dt_frac = self._queued_sweep
|
|
364
|
+
self._queued_sweep = None
|
|
365
|
+
self.sweep(queued_direction, queued_dt_frac, queue=False)
|
|
366
|
+
|
|
367
|
+
# ------------------------------------------------------------------- #
|
|
368
|
+
|
|
369
|
+
if direction == "right":
|
|
370
|
+
start_site_ind = 0
|
|
371
|
+
final_site_ind = self.L - 1
|
|
372
|
+
# Apply even gates:
|
|
373
|
+
#
|
|
374
|
+
# o-<-<-<-<-<-<-<-<-<- -<-<
|
|
375
|
+
# | | | | | | | | | | | | >~>~>~>~>~>~>~>~>~>~>~o
|
|
376
|
+
# UUU UUU UUU UUU UUU ... UUU --> | | | | | | | | | | | |
|
|
377
|
+
# | | | | | | | | | | | |
|
|
378
|
+
# 1 2 3 4 5 ==>
|
|
379
|
+
#
|
|
380
|
+
for i in range(start_site_ind, final_site_ind, 2):
|
|
381
|
+
sites = (i, (i + 1) % self.L)
|
|
382
|
+
U = self._get_gate_from_ham(dt_frac, sites)
|
|
383
|
+
self._pt.left_canonize(start=max(0, i - 1), stop=i)
|
|
384
|
+
self._pt.gate_split_(
|
|
385
|
+
U, where=sites, absorb="right", **self.split_opts
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if self.L % 2 == 1:
|
|
389
|
+
self._pt.left_canonize_site(self.L - 2)
|
|
390
|
+
if self.cyclic:
|
|
391
|
+
sites = (self.L - 1, 0)
|
|
392
|
+
U = self._get_gate_from_ham(dt_frac, sites)
|
|
393
|
+
self._pt.right_canonize_site(1)
|
|
394
|
+
self._pt.gate_split_(
|
|
395
|
+
U, where=sites, absorb="left", **self.split_opts
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
elif direction == "left":
|
|
399
|
+
if self.cyclic and (self.L % 2 == 0):
|
|
400
|
+
sites = (self.L - 1, 0)
|
|
401
|
+
U = self._get_gate_from_ham(dt_frac, sites)
|
|
402
|
+
self._pt.right_canonize_site(1)
|
|
403
|
+
self._pt.gate_split_(
|
|
404
|
+
U, where=sites, absorb="left", **self.split_opts
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
final_site_ind = 1
|
|
408
|
+
# Apply odd gates:
|
|
409
|
+
#
|
|
410
|
+
# >->->- ->->->->->->->->-o
|
|
411
|
+
# | | | | | | | | | | | | o~<~<~<~<~<~<~<~<~<~<~<
|
|
412
|
+
# | UUU ... UUU UUU UUU UUU | --> | | | | | | | | | | | |
|
|
413
|
+
# | | | | | | | | | | | |
|
|
414
|
+
# <== 4 3 2 1
|
|
415
|
+
#
|
|
416
|
+
for i in reversed(range(final_site_ind, self.L - 1, 2)):
|
|
417
|
+
sites = (i, (i + 1) % self.L)
|
|
418
|
+
U = self._get_gate_from_ham(dt_frac, sites)
|
|
419
|
+
self._pt.right_canonize(
|
|
420
|
+
start=min(self.L - 1, i + 2), stop=i + 1
|
|
421
|
+
)
|
|
422
|
+
self._pt.gate_split_(
|
|
423
|
+
U, where=sites, absorb="left", **self.split_opts
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
# one extra canonicalization not included in last split
|
|
427
|
+
self._pt.right_canonize_site(1)
|
|
428
|
+
|
|
429
|
+
# Renormalise after imaginary time evolution
|
|
430
|
+
if self.imag:
|
|
431
|
+
factor = self._pt[final_site_ind].norm()
|
|
432
|
+
self._pt[final_site_ind] /= factor
|
|
433
|
+
|
|
434
|
+
def _step_order2(self, tau=1, **sweep_opts):
|
|
435
|
+
"""Perform a single, second order step."""
|
|
436
|
+
self.sweep("right", tau / 2, **sweep_opts)
|
|
437
|
+
self.sweep("left", tau, **sweep_opts)
|
|
438
|
+
self.sweep("right", tau / 2, **sweep_opts)
|
|
439
|
+
|
|
440
|
+
def _step_order4(self, **sweep_opts):
|
|
441
|
+
"""Perform a single, fourth order step."""
|
|
442
|
+
tau1 = tau2 = 1 / (4 * 4 ** (1 / 3))
|
|
443
|
+
tau3 = 1 - 2 * tau1 - 2 * tau2
|
|
444
|
+
self._step_order2(tau1, **sweep_opts)
|
|
445
|
+
self._step_order2(tau2, **sweep_opts)
|
|
446
|
+
self._step_order2(tau3, **sweep_opts)
|
|
447
|
+
self._step_order2(tau2, **sweep_opts)
|
|
448
|
+
self._step_order2(tau1, **sweep_opts)
|
|
449
|
+
|
|
450
|
+
def step(self, order=2, dt=None, progbar=None, **sweep_opts):
|
|
451
|
+
"""Perform a single step of time ``self.dt``."""
|
|
452
|
+
{2: self._step_order2, 4: self._step_order4}[order](
|
|
453
|
+
dt=dt, **sweep_opts
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
dt = self._dt if dt is None else dt
|
|
457
|
+
self.t += dt
|
|
458
|
+
self._err += self._ham_norm * dt ** (order + 1)
|
|
459
|
+
|
|
460
|
+
if progbar is not None:
|
|
461
|
+
progbar.cupdate(self.t)
|
|
462
|
+
self._set_progbar_desc(progbar)
|
|
463
|
+
|
|
464
|
+
def _compute_sweep_dt_tol(self, T, dt, tol, order):
|
|
465
|
+
# Work out timestep, possibly from target tol, and checking defaults
|
|
466
|
+
dt = self.dt if (dt is None) else dt
|
|
467
|
+
tol = self.tol if (tol is None) else tol
|
|
468
|
+
|
|
469
|
+
if not (dt or tol):
|
|
470
|
+
raise ValueError("Must set one of ``dt`` and ``tol``.")
|
|
471
|
+
if dt and tol:
|
|
472
|
+
raise ValueError("Can't set both ``dt`` and ``tol``.")
|
|
473
|
+
|
|
474
|
+
if dt is None:
|
|
475
|
+
self._dt = self.choose_time_step(tol, T - self.t, order)
|
|
476
|
+
else:
|
|
477
|
+
self._dt = dt
|
|
478
|
+
|
|
479
|
+
return self._dt
|
|
480
|
+
|
|
481
|
+
TARGET_TOL = 1e-13 # tolerance to have 'reached' target time
|
|
482
|
+
|
|
483
|
+
def update_to(self, T, dt=None, tol=None, order=4, progbar=None):
|
|
484
|
+
"""Update the state to time ``T``.
|
|
485
|
+
|
|
486
|
+
Parameters
|
|
487
|
+
----------
|
|
488
|
+
T : float
|
|
489
|
+
The time to evolve to.
|
|
490
|
+
dt : float, optional
|
|
491
|
+
Time step to use. Can't be set as well as ``tol``.
|
|
492
|
+
tol : float, optional
|
|
493
|
+
Tolerance for whole evolution. Can't be set as well as ``dt``.
|
|
494
|
+
order : int, optional
|
|
495
|
+
Trotter order to use.
|
|
496
|
+
progbar : bool, optional
|
|
497
|
+
Manually turn the progress bar off.
|
|
498
|
+
"""
|
|
499
|
+
if T < self.t - self.TARGET_TOL:
|
|
500
|
+
# can't go backwards yet
|
|
501
|
+
raise NotImplementedError
|
|
502
|
+
|
|
503
|
+
self._compute_sweep_dt_tol(T, dt, tol, order)
|
|
504
|
+
|
|
505
|
+
# set up progress bar and start evolution
|
|
506
|
+
progbar = self.progbar if (progbar is None) else progbar
|
|
507
|
+
progbar = continuous_progbar(self.t, T) if progbar else None
|
|
508
|
+
|
|
509
|
+
while self.t < T - self._dt:
|
|
510
|
+
# get closer until we can reach in a single step
|
|
511
|
+
self.step(order=order, progbar=progbar, dt=None, queue=True)
|
|
512
|
+
|
|
513
|
+
# always perform final sweep with queue draining
|
|
514
|
+
self.step(order=order, progbar=progbar, dt=T - self.t, queue=False)
|
|
515
|
+
|
|
516
|
+
if progbar:
|
|
517
|
+
progbar.close()
|
|
518
|
+
|
|
519
|
+
def _set_progbar_desc(self, progbar):
|
|
520
|
+
msg = f"t={self.t:.4g}, max-bond={self._pt.max_bond()}"
|
|
521
|
+
progbar.set_description(msg)
|
|
522
|
+
|
|
523
|
+
def at_times(self, ts, dt=None, tol=None, order=4, progbar=None):
|
|
524
|
+
"""Generate the time evolved state at each time in ``ts``.
|
|
525
|
+
|
|
526
|
+
Parameters
|
|
527
|
+
----------
|
|
528
|
+
ts : sequence of float
|
|
529
|
+
The times to evolve to and yield the state at.
|
|
530
|
+
dt : float, optional
|
|
531
|
+
Time step to use. Can't be set as well as ``tol``.
|
|
532
|
+
tol : float, optional
|
|
533
|
+
Tolerance for whole evolution. Can't be set as well as ``dt``.
|
|
534
|
+
order : int, optional
|
|
535
|
+
Trotter order to use.
|
|
536
|
+
progbar : bool, optional
|
|
537
|
+
Manually turn the progress bar off.
|
|
538
|
+
|
|
539
|
+
Yields
|
|
540
|
+
------
|
|
541
|
+
pt : MatrixProductState
|
|
542
|
+
The state at each of the times in ``ts``. This is a copy of
|
|
543
|
+
internal state used, so inplace changes can be made to it.
|
|
544
|
+
"""
|
|
545
|
+
# convert ts to list, to to calc range and use progress bar
|
|
546
|
+
ts = sorted(ts)
|
|
547
|
+
T = ts[-1]
|
|
548
|
+
|
|
549
|
+
# need to use dt always so tol applies over whole T sweep
|
|
550
|
+
dt = self._compute_sweep_dt_tol(T, dt, tol, order)
|
|
551
|
+
|
|
552
|
+
# set up progress bar
|
|
553
|
+
progbar = self.progbar if (progbar is None) else progbar
|
|
554
|
+
if progbar:
|
|
555
|
+
ts = Progbar(ts)
|
|
556
|
+
|
|
557
|
+
for t in ts:
|
|
558
|
+
self.update_to(t, dt=dt, tol=False, order=order, progbar=False)
|
|
559
|
+
|
|
560
|
+
if progbar:
|
|
561
|
+
self._set_progbar_desc(ts)
|
|
562
|
+
|
|
563
|
+
yield self.pt
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def OTOC_local(
|
|
567
|
+
psi0,
|
|
568
|
+
H,
|
|
569
|
+
H_back,
|
|
570
|
+
ts,
|
|
571
|
+
i,
|
|
572
|
+
A,
|
|
573
|
+
j=None,
|
|
574
|
+
B=None,
|
|
575
|
+
initial_eigenstate="check",
|
|
576
|
+
**tebd_opts,
|
|
577
|
+
):
|
|
578
|
+
"""The out-of-time-ordered correlator (OTOC) generating by two local
|
|
579
|
+
operator A and B acting on site 'i', note it's a function of time.
|
|
580
|
+
|
|
581
|
+
Parameters
|
|
582
|
+
----------
|
|
583
|
+
psi0 : MatrixProductState
|
|
584
|
+
The initial state in MPS form.
|
|
585
|
+
H : LocalHam1D
|
|
586
|
+
The Hamiltonian for forward time-evolution.
|
|
587
|
+
H_back : LocalHam1D
|
|
588
|
+
The Hamiltonian for backward time-evolution, should have only
|
|
589
|
+
sign difference with 'H'.
|
|
590
|
+
ts : sequence of float
|
|
591
|
+
The time to evolve to.
|
|
592
|
+
i : int
|
|
593
|
+
The site where the local operators acting on.
|
|
594
|
+
A : array
|
|
595
|
+
The operator to act with.
|
|
596
|
+
initial_eigenstate: {'check', Flase, True}
|
|
597
|
+
To check the psi0 is or not eigenstate of operator B. If psi0 is the
|
|
598
|
+
eigenstate of B, it will run a simpler version of OTOC calculation
|
|
599
|
+
automatically.
|
|
600
|
+
|
|
601
|
+
Returns
|
|
602
|
+
----------
|
|
603
|
+
The OTOC <A(t)B(0)A(t)B(0)>
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
if B is None:
|
|
607
|
+
B = A
|
|
608
|
+
if j is None:
|
|
609
|
+
j = i
|
|
610
|
+
|
|
611
|
+
if initial_eigenstate == "check":
|
|
612
|
+
psi = psi0.gate(B, j, contract=True)
|
|
613
|
+
x = psi0.H.expec(psi)
|
|
614
|
+
y = psi.H.expec(psi)
|
|
615
|
+
if abs(x**2 - y) < 1e-10:
|
|
616
|
+
initial_eigenstate = True
|
|
617
|
+
else:
|
|
618
|
+
initial_eigenstate = False
|
|
619
|
+
|
|
620
|
+
if initial_eigenstate is True:
|
|
621
|
+
tebd1 = TEBD(psi0, H, **tebd_opts)
|
|
622
|
+
x = psi0.H.expec(psi0.gate(B, j, contract=True))
|
|
623
|
+
for t in ts:
|
|
624
|
+
# evolve forward
|
|
625
|
+
tebd1.update_to(t)
|
|
626
|
+
# apply first A-gate
|
|
627
|
+
psi_t_A = tebd1.pt.gate(A, i, contract=True)
|
|
628
|
+
# evolve backwards
|
|
629
|
+
tebd2 = TEBD(psi_t_A, H_back, **tebd_opts)
|
|
630
|
+
tebd2.update_to(t)
|
|
631
|
+
# compute expectation with second B-gate
|
|
632
|
+
psi_f = tebd2.pt
|
|
633
|
+
yield x * psi_f.H.expec(psi_f.gate(B, j, contract=True))
|
|
634
|
+
else:
|
|
635
|
+
# set the initial TEBD and apply the first operator A to right
|
|
636
|
+
psi0_L = psi0
|
|
637
|
+
tebd1_L = TEBD(psi0_L, H, **tebd_opts)
|
|
638
|
+
|
|
639
|
+
psi0_R = psi0.gate(B, j, contract=True)
|
|
640
|
+
tebd1_R = TEBD(psi0_R, H, **tebd_opts)
|
|
641
|
+
|
|
642
|
+
for t in ts:
|
|
643
|
+
# evolve forward
|
|
644
|
+
tebd1_L.update_to(t)
|
|
645
|
+
tebd1_R.update_to(t)
|
|
646
|
+
|
|
647
|
+
# apply the opertor A to both left and right states
|
|
648
|
+
psi_t_L_A = tebd1_L.pt.gate(A, i, contract=True)
|
|
649
|
+
psi_t_R_A = tebd1_R.pt.gate(A.H, i, contract=True)
|
|
650
|
+
|
|
651
|
+
# set the second left and right TEBD
|
|
652
|
+
tebd2_L = TEBD(psi_t_L_A, H_back, **tebd_opts)
|
|
653
|
+
tebd2_R = TEBD(psi_t_R_A, H_back, **tebd_opts)
|
|
654
|
+
|
|
655
|
+
# evolve backwards
|
|
656
|
+
tebd2_L.update_to(t)
|
|
657
|
+
tebd2_R.update_to(t)
|
|
658
|
+
|
|
659
|
+
# apply the laste operator B to left and compute overlap
|
|
660
|
+
psi_f_L = tebd2_L.pt.gate(B.H, j, contract=True)
|
|
661
|
+
psi_f_R = tebd2_R.pt
|
|
662
|
+
yield psi_f_L.H.expec(psi_f_R)
|