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.
Files changed (122) hide show
  1. trajectree/__init__.py +3 -0
  2. trajectree/fock_optics/devices.py +1 -1
  3. trajectree/fock_optics/light_sources.py +2 -2
  4. trajectree/fock_optics/measurement.py +3 -3
  5. trajectree/fock_optics/utils.py +6 -6
  6. trajectree/quimb/docs/_pygments/_pygments_dark.py +118 -0
  7. trajectree/quimb/docs/_pygments/_pygments_light.py +118 -0
  8. trajectree/quimb/docs/conf.py +158 -0
  9. trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +62 -0
  10. trajectree/quimb/quimb/__init__.py +507 -0
  11. trajectree/quimb/quimb/calc.py +1491 -0
  12. trajectree/quimb/quimb/core.py +2279 -0
  13. trajectree/quimb/quimb/evo.py +712 -0
  14. trajectree/quimb/quimb/experimental/__init__.py +0 -0
  15. trajectree/quimb/quimb/experimental/autojittn.py +129 -0
  16. trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +109 -0
  17. trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +397 -0
  18. trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +316 -0
  19. trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +653 -0
  20. trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +571 -0
  21. trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +775 -0
  22. trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +316 -0
  23. trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +537 -0
  24. trajectree/quimb/quimb/experimental/belief_propagation/regions.py +194 -0
  25. trajectree/quimb/quimb/experimental/cluster_update.py +286 -0
  26. trajectree/quimb/quimb/experimental/merabuilder.py +865 -0
  27. trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +15 -0
  28. trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +1631 -0
  29. trajectree/quimb/quimb/experimental/schematic.py +7 -0
  30. trajectree/quimb/quimb/experimental/tn_marginals.py +130 -0
  31. trajectree/quimb/quimb/experimental/tnvmc.py +1483 -0
  32. trajectree/quimb/quimb/gates.py +36 -0
  33. trajectree/quimb/quimb/gen/__init__.py +2 -0
  34. trajectree/quimb/quimb/gen/operators.py +1167 -0
  35. trajectree/quimb/quimb/gen/rand.py +713 -0
  36. trajectree/quimb/quimb/gen/states.py +479 -0
  37. trajectree/quimb/quimb/linalg/__init__.py +6 -0
  38. trajectree/quimb/quimb/linalg/approx_spectral.py +1109 -0
  39. trajectree/quimb/quimb/linalg/autoblock.py +258 -0
  40. trajectree/quimb/quimb/linalg/base_linalg.py +719 -0
  41. trajectree/quimb/quimb/linalg/mpi_launcher.py +397 -0
  42. trajectree/quimb/quimb/linalg/numpy_linalg.py +244 -0
  43. trajectree/quimb/quimb/linalg/rand_linalg.py +514 -0
  44. trajectree/quimb/quimb/linalg/scipy_linalg.py +293 -0
  45. trajectree/quimb/quimb/linalg/slepc_linalg.py +892 -0
  46. trajectree/quimb/quimb/schematic.py +1518 -0
  47. trajectree/quimb/quimb/tensor/__init__.py +401 -0
  48. trajectree/quimb/quimb/tensor/array_ops.py +610 -0
  49. trajectree/quimb/quimb/tensor/circuit.py +4824 -0
  50. trajectree/quimb/quimb/tensor/circuit_gen.py +411 -0
  51. trajectree/quimb/quimb/tensor/contraction.py +336 -0
  52. trajectree/quimb/quimb/tensor/decomp.py +1255 -0
  53. trajectree/quimb/quimb/tensor/drawing.py +1646 -0
  54. trajectree/quimb/quimb/tensor/fitting.py +385 -0
  55. trajectree/quimb/quimb/tensor/geometry.py +583 -0
  56. trajectree/quimb/quimb/tensor/interface.py +114 -0
  57. trajectree/quimb/quimb/tensor/networking.py +1058 -0
  58. trajectree/quimb/quimb/tensor/optimize.py +1818 -0
  59. trajectree/quimb/quimb/tensor/tensor_1d.py +4778 -0
  60. trajectree/quimb/quimb/tensor/tensor_1d_compress.py +1854 -0
  61. trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +662 -0
  62. trajectree/quimb/quimb/tensor/tensor_2d.py +5954 -0
  63. trajectree/quimb/quimb/tensor/tensor_2d_compress.py +96 -0
  64. trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +1230 -0
  65. trajectree/quimb/quimb/tensor/tensor_3d.py +2869 -0
  66. trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +46 -0
  67. trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +60 -0
  68. trajectree/quimb/quimb/tensor/tensor_arbgeom.py +3237 -0
  69. trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +565 -0
  70. trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +1138 -0
  71. trajectree/quimb/quimb/tensor/tensor_builder.py +5411 -0
  72. trajectree/quimb/quimb/tensor/tensor_core.py +11179 -0
  73. trajectree/quimb/quimb/tensor/tensor_dmrg.py +1472 -0
  74. trajectree/quimb/quimb/tensor/tensor_mera.py +204 -0
  75. trajectree/quimb/quimb/utils.py +892 -0
  76. trajectree/quimb/tests/__init__.py +0 -0
  77. trajectree/quimb/tests/test_accel.py +501 -0
  78. trajectree/quimb/tests/test_calc.py +788 -0
  79. trajectree/quimb/tests/test_core.py +847 -0
  80. trajectree/quimb/tests/test_evo.py +565 -0
  81. trajectree/quimb/tests/test_gen/__init__.py +0 -0
  82. trajectree/quimb/tests/test_gen/test_operators.py +361 -0
  83. trajectree/quimb/tests/test_gen/test_rand.py +296 -0
  84. trajectree/quimb/tests/test_gen/test_states.py +261 -0
  85. trajectree/quimb/tests/test_linalg/__init__.py +0 -0
  86. trajectree/quimb/tests/test_linalg/test_approx_spectral.py +368 -0
  87. trajectree/quimb/tests/test_linalg/test_base_linalg.py +351 -0
  88. trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +127 -0
  89. trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +84 -0
  90. trajectree/quimb/tests/test_linalg/test_rand_linalg.py +134 -0
  91. trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +283 -0
  92. trajectree/quimb/tests/test_tensor/__init__.py +0 -0
  93. trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
  94. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +39 -0
  95. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +67 -0
  96. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +64 -0
  97. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +51 -0
  98. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +142 -0
  99. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +101 -0
  100. trajectree/quimb/tests/test_tensor/test_circuit.py +816 -0
  101. trajectree/quimb/tests/test_tensor/test_contract.py +67 -0
  102. trajectree/quimb/tests/test_tensor/test_decomp.py +40 -0
  103. trajectree/quimb/tests/test_tensor/test_mera.py +52 -0
  104. trajectree/quimb/tests/test_tensor/test_optimizers.py +488 -0
  105. trajectree/quimb/tests/test_tensor/test_tensor_1d.py +1171 -0
  106. trajectree/quimb/tests/test_tensor/test_tensor_2d.py +606 -0
  107. trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +144 -0
  108. trajectree/quimb/tests/test_tensor/test_tensor_3d.py +123 -0
  109. trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +226 -0
  110. trajectree/quimb/tests/test_tensor/test_tensor_builder.py +441 -0
  111. trajectree/quimb/tests/test_tensor/test_tensor_core.py +2066 -0
  112. trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +388 -0
  113. trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +63 -0
  114. trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +270 -0
  115. trajectree/quimb/tests/test_utils.py +85 -0
  116. trajectree/trajectory.py +2 -2
  117. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/METADATA +2 -2
  118. trajectree-0.0.1.dist-info/RECORD +126 -0
  119. trajectree-0.0.0.dist-info/RECORD +0 -16
  120. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/WHEEL +0 -0
  121. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/licenses/LICENSE +0 -0
  122. {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)