Trajectree 0.0.1__py3-none-any.whl → 0.0.2__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 +0 -3
  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/trajectory.py +2 -2
  7. {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/METADATA +2 -3
  8. trajectree-0.0.2.dist-info/RECORD +16 -0
  9. trajectree/quimb/docs/_pygments/_pygments_dark.py +0 -118
  10. trajectree/quimb/docs/_pygments/_pygments_light.py +0 -118
  11. trajectree/quimb/docs/conf.py +0 -158
  12. trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +0 -62
  13. trajectree/quimb/quimb/__init__.py +0 -507
  14. trajectree/quimb/quimb/calc.py +0 -1491
  15. trajectree/quimb/quimb/core.py +0 -2279
  16. trajectree/quimb/quimb/evo.py +0 -712
  17. trajectree/quimb/quimb/experimental/__init__.py +0 -0
  18. trajectree/quimb/quimb/experimental/autojittn.py +0 -129
  19. trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +0 -109
  20. trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +0 -397
  21. trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +0 -316
  22. trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +0 -653
  23. trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +0 -571
  24. trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +0 -775
  25. trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +0 -316
  26. trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +0 -537
  27. trajectree/quimb/quimb/experimental/belief_propagation/regions.py +0 -194
  28. trajectree/quimb/quimb/experimental/cluster_update.py +0 -286
  29. trajectree/quimb/quimb/experimental/merabuilder.py +0 -865
  30. trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +0 -15
  31. trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +0 -1631
  32. trajectree/quimb/quimb/experimental/schematic.py +0 -7
  33. trajectree/quimb/quimb/experimental/tn_marginals.py +0 -130
  34. trajectree/quimb/quimb/experimental/tnvmc.py +0 -1483
  35. trajectree/quimb/quimb/gates.py +0 -36
  36. trajectree/quimb/quimb/gen/__init__.py +0 -2
  37. trajectree/quimb/quimb/gen/operators.py +0 -1167
  38. trajectree/quimb/quimb/gen/rand.py +0 -713
  39. trajectree/quimb/quimb/gen/states.py +0 -479
  40. trajectree/quimb/quimb/linalg/__init__.py +0 -6
  41. trajectree/quimb/quimb/linalg/approx_spectral.py +0 -1109
  42. trajectree/quimb/quimb/linalg/autoblock.py +0 -258
  43. trajectree/quimb/quimb/linalg/base_linalg.py +0 -719
  44. trajectree/quimb/quimb/linalg/mpi_launcher.py +0 -397
  45. trajectree/quimb/quimb/linalg/numpy_linalg.py +0 -244
  46. trajectree/quimb/quimb/linalg/rand_linalg.py +0 -514
  47. trajectree/quimb/quimb/linalg/scipy_linalg.py +0 -293
  48. trajectree/quimb/quimb/linalg/slepc_linalg.py +0 -892
  49. trajectree/quimb/quimb/schematic.py +0 -1518
  50. trajectree/quimb/quimb/tensor/__init__.py +0 -401
  51. trajectree/quimb/quimb/tensor/array_ops.py +0 -610
  52. trajectree/quimb/quimb/tensor/circuit.py +0 -4824
  53. trajectree/quimb/quimb/tensor/circuit_gen.py +0 -411
  54. trajectree/quimb/quimb/tensor/contraction.py +0 -336
  55. trajectree/quimb/quimb/tensor/decomp.py +0 -1255
  56. trajectree/quimb/quimb/tensor/drawing.py +0 -1646
  57. trajectree/quimb/quimb/tensor/fitting.py +0 -385
  58. trajectree/quimb/quimb/tensor/geometry.py +0 -583
  59. trajectree/quimb/quimb/tensor/interface.py +0 -114
  60. trajectree/quimb/quimb/tensor/networking.py +0 -1058
  61. trajectree/quimb/quimb/tensor/optimize.py +0 -1818
  62. trajectree/quimb/quimb/tensor/tensor_1d.py +0 -4778
  63. trajectree/quimb/quimb/tensor/tensor_1d_compress.py +0 -1854
  64. trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +0 -662
  65. trajectree/quimb/quimb/tensor/tensor_2d.py +0 -5954
  66. trajectree/quimb/quimb/tensor/tensor_2d_compress.py +0 -96
  67. trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +0 -1230
  68. trajectree/quimb/quimb/tensor/tensor_3d.py +0 -2869
  69. trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +0 -46
  70. trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +0 -60
  71. trajectree/quimb/quimb/tensor/tensor_arbgeom.py +0 -3237
  72. trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +0 -565
  73. trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +0 -1138
  74. trajectree/quimb/quimb/tensor/tensor_builder.py +0 -5411
  75. trajectree/quimb/quimb/tensor/tensor_core.py +0 -11179
  76. trajectree/quimb/quimb/tensor/tensor_dmrg.py +0 -1472
  77. trajectree/quimb/quimb/tensor/tensor_mera.py +0 -204
  78. trajectree/quimb/quimb/utils.py +0 -892
  79. trajectree/quimb/tests/__init__.py +0 -0
  80. trajectree/quimb/tests/test_accel.py +0 -501
  81. trajectree/quimb/tests/test_calc.py +0 -788
  82. trajectree/quimb/tests/test_core.py +0 -847
  83. trajectree/quimb/tests/test_evo.py +0 -565
  84. trajectree/quimb/tests/test_gen/__init__.py +0 -0
  85. trajectree/quimb/tests/test_gen/test_operators.py +0 -361
  86. trajectree/quimb/tests/test_gen/test_rand.py +0 -296
  87. trajectree/quimb/tests/test_gen/test_states.py +0 -261
  88. trajectree/quimb/tests/test_linalg/__init__.py +0 -0
  89. trajectree/quimb/tests/test_linalg/test_approx_spectral.py +0 -368
  90. trajectree/quimb/tests/test_linalg/test_base_linalg.py +0 -351
  91. trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +0 -127
  92. trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +0 -84
  93. trajectree/quimb/tests/test_linalg/test_rand_linalg.py +0 -134
  94. trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +0 -283
  95. trajectree/quimb/tests/test_tensor/__init__.py +0 -0
  96. trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
  97. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +0 -39
  98. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +0 -67
  99. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +0 -64
  100. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +0 -51
  101. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +0 -142
  102. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +0 -101
  103. trajectree/quimb/tests/test_tensor/test_circuit.py +0 -816
  104. trajectree/quimb/tests/test_tensor/test_contract.py +0 -67
  105. trajectree/quimb/tests/test_tensor/test_decomp.py +0 -40
  106. trajectree/quimb/tests/test_tensor/test_mera.py +0 -52
  107. trajectree/quimb/tests/test_tensor/test_optimizers.py +0 -488
  108. trajectree/quimb/tests/test_tensor/test_tensor_1d.py +0 -1171
  109. trajectree/quimb/tests/test_tensor/test_tensor_2d.py +0 -606
  110. trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +0 -144
  111. trajectree/quimb/tests/test_tensor/test_tensor_3d.py +0 -123
  112. trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +0 -226
  113. trajectree/quimb/tests/test_tensor/test_tensor_builder.py +0 -441
  114. trajectree/quimb/tests/test_tensor/test_tensor_core.py +0 -2066
  115. trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +0 -388
  116. trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +0 -63
  117. trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +0 -270
  118. trajectree/quimb/tests/test_utils.py +0 -85
  119. trajectree-0.0.1.dist-info/RECORD +0 -126
  120. {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/WHEEL +0 -0
  121. {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/licenses/LICENSE +0 -0
  122. {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/top_level.txt +0 -0
@@ -1,1230 +0,0 @@
1
- """Tools for performing TEBD like algorithms on a 2D lattice.
2
- """
3
-
4
- from itertools import starmap
5
-
6
- import numpy as np
7
- import scipy.sparse.linalg as spla
8
- from autoray import conj, dag, do, reshape
9
-
10
- from ..utils import default_to_neutral_style, pairwise
11
- from .contraction import contract_strategy
12
- from .drawing import get_colors
13
- from .optimize import TNOptimizer
14
- from .tensor_2d import (
15
- calc_plaquette_map,
16
- calc_plaquette_sizes,
17
- gen_2d_bonds,
18
- gen_long_range_path,
19
- gen_long_range_swap_path,
20
- nearest_neighbors,
21
- plaquette_to_sites,
22
- swap_path_to_long_range_path,
23
- )
24
- from .tensor_arbgeom_tebd import LocalHamGen, TEBDGen
25
- from .tensor_core import Tensor
26
-
27
-
28
- class LocalHam2D(LocalHamGen):
29
- """A 2D Hamiltonian represented as local terms. This combines all two site
30
- and one site terms into a single interaction per lattice pair, and caches
31
- operations on the terms such as getting their exponential.
32
-
33
- Parameters
34
- ----------
35
- Lx : int
36
- The number of rows.
37
- Ly : int
38
- The number of columns.
39
- H2 : array_like or dict[tuple[tuple[int]], array_like]
40
- The two site term(s). If a single array is given, assume to be the
41
- default interaction for all nearest neighbours. If a dict is supplied,
42
- the keys should represent specific pairs of coordinates like
43
- ``((ia, ja), (ib, jb))`` with the values the array representing the
44
- interaction for that pair. A default term for all remaining nearest
45
- neighbours interactions can still be supplied with the key ``None``.
46
- H1 : array_like or dict[tuple[int], array_like], optional
47
- The one site term(s). If a single array is given, assume to be the
48
- default onsite term for all terms. If a dict is supplied,
49
- the keys should represent specific coordinates like
50
- ``(i, j)`` with the values the array representing the local term for
51
- that site. A default term for all remaining sites can still be supplied
52
- with the key ``None``.
53
-
54
- Attributes
55
- ----------
56
- terms : dict[tuple[tuple[int]], array_like]
57
- The total effective local term for each interaction (with single site
58
- terms appropriately absorbed). Each key is a pair of coordinates
59
- ``ija, ijb`` with ``ija < ijb``.
60
-
61
- """
62
-
63
- def __init__(self, Lx, Ly, H2, H1=None, cyclic=False):
64
- self.Lx = int(Lx)
65
- self.Ly = int(Ly)
66
-
67
- # parse two site terms
68
- if hasattr(H2, "shape"):
69
- # use as default nearest neighbour term
70
- H2 = {None: H2}
71
- else:
72
- H2 = dict(H2)
73
-
74
- # possibly fill in default gates
75
- default_H2 = H2.pop(None, None)
76
- if default_H2 is not None:
77
- for coo_a, coo_b in gen_2d_bonds(Lx, Ly, steppers=[
78
- lambda i, j: (i, j + 1),
79
- lambda i, j: (i + 1, j),
80
- ], cyclic=cyclic):
81
- if (coo_a, coo_b) not in H2 and (coo_b, coo_a) not in H2:
82
- H2[coo_a, coo_b] = default_H2
83
-
84
- super().__init__(H2=H2, H1=H1)
85
-
86
- @property
87
- def nsites(self):
88
- """The number of sites in the system.
89
- """
90
- return self.Lx * self.Ly
91
-
92
- def __repr__(self):
93
- s = "<LocalHam2D(Lx={}, Ly={}, num_terms={})>"
94
- return s.format(self.Lx, self.Ly, len(self.terms))
95
-
96
- @default_to_neutral_style
97
- def draw(
98
- self,
99
- ordering='sort',
100
- show_norm=True,
101
- figsize=None,
102
- fontsize=8,
103
- legend=True,
104
- ax=None,
105
- **kwargs,
106
- ):
107
- """Plot this Hamiltonian as a network.
108
-
109
- Parameters
110
- ----------
111
- ordering : {'sort', None, 'random'}, optional
112
- An ordering of the termns, or an argument to be supplied to
113
- :meth:`quimb.tensor.tensor_2d_tebd.LocalHam2D.get_auto_ordering`
114
- to generate this automatically.
115
- show_norm : bool, optional
116
- Show the norm of each term as edge labels.
117
- figsize : None or tuple[int], optional
118
- Size of the figure, defaults to size of Hamiltonian.
119
- fontsize : int, optional
120
- Font size for norm labels.
121
- legend : bool, optional
122
- Whether to show the legend of which terms are in which group.
123
- ax : None or matplotlib.Axes, optional
124
- Add to a existing set of axes.
125
- """
126
- import matplotlib.pyplot as plt
127
-
128
- if figsize is None:
129
- figsize = (self.Ly, self.Lx)
130
-
131
- ax_supplied = (ax is not None)
132
- if not ax_supplied:
133
- fig, ax = plt.subplots(figsize=figsize, constrained_layout=True)
134
- ax.axis('off')
135
- ax.set_aspect('equal')
136
- else:
137
- fig = None
138
-
139
- if ordering is None or isinstance(ordering, str):
140
- ordering = self.get_auto_ordering(ordering, **kwargs)
141
-
142
- data = []
143
- seen = set()
144
- n = 0
145
- for ij1, ij2 in ordering:
146
- if (ij1 in seen) or (ij2 in seen):
147
- # start a new group
148
- seen = {ij1, ij2}
149
- n += 1
150
- else:
151
- seen.add(ij1)
152
- seen.add(ij2)
153
-
154
- ys, xs = zip(ij1, ij2)
155
-
156
- d = ((xs[1] - xs[0])**2 + (ys[1] - ys[0])**2)**0.5
157
- # offset by the length of bond to distinguish NNN etc.
158
- # choose offset direction by parity of first site
159
-
160
- if d > 2**0.5:
161
- xs = [xi + (-1)**int(ys[0]) * 0.02 * d for xi in xs]
162
- ys = [yi + (-1)**int(xs[0]) * 0.02 * d for yi in ys]
163
-
164
- # set coordinates for label with some offset towards left
165
- if ij1[1] < ij2[1]:
166
- lbl_x0 = (3 * xs[0] + 2 * xs[1]) / 5
167
- lbl_y0 = (3 * ys[0] + 2 * ys[1]) / 5
168
- else:
169
- lbl_x0 = (2 * xs[0] + 3 * xs[1]) / 5
170
- lbl_y0 = (2 * ys[0] + 3 * ys[1]) / 5
171
-
172
- nrm = do('linalg.norm', self.terms[ij1, ij2])
173
-
174
- data.append((xs, ys, n, lbl_x0, lbl_y0, nrm))
175
-
176
- num_groups = n + 1
177
- colors = get_colors(range(num_groups))
178
-
179
- # do the plotting
180
- for xs, ys, n, lbl_x0, lbl_y0, nrm in data:
181
- ax.plot(xs, ys, c=colors[n], linewidth=2 * nrm**0.5)
182
- if show_norm:
183
- label = "{:.3f}".format(nrm)
184
- ax.text(lbl_x0, lbl_y0, label, c=colors[n], fontsize=fontsize)
185
-
186
- # create legend
187
- if legend:
188
- handles = []
189
- for color in colors.values():
190
- handles += [plt.Line2D([0], [0], marker='o', color=color,
191
- linestyle='', markersize=10)]
192
-
193
- lbls = [f"Group {i + 1}" for i in range(num_groups)]
194
-
195
- ax.legend(handles, lbls, ncol=max(round(len(handles) / 20), 1),
196
- loc='center left', bbox_to_anchor=(1, 0.5))
197
-
198
- return fig, ax
199
-
200
- graph = draw
201
-
202
-
203
- class TEBD2D(TEBDGen):
204
- """Generic class for performing two dimensional time evolving block
205
- decimation, i.e. applying the exponential of a Hamiltonian using
206
- a product formula that involves applying local exponentiated gates only.
207
-
208
- Parameters
209
- ----------
210
- psi0 : TensorNetwork2DVector
211
- The initial state.
212
- ham : LocalHam2D
213
- The Hamtiltonian consisting of local terms.
214
- tau : float, optional
215
- The default local exponent, if considered as time real values here
216
- imply imaginary time.
217
- max_bond : {'psi0', int, None}, optional
218
- The maximum bond dimension to keep when applying each gate.
219
- gate_opts : dict, optional
220
- Supplied to :meth:`quimb.tensor.tensor_2d.TensorNetwork2DVector.gate`,
221
- in addition to ``max_bond``. By default ``contract`` is set to
222
- 'reduce-split' and ``cutoff`` is set to ``0.0``.
223
- ordering : str, tuple[tuple[int]], callable, optional
224
- How to order the terms, if a string is given then use this as the
225
- strategy given to
226
- :meth:`~quimb.tensor.tensor_2d_tebd.LocalHam2D.get_auto_ordering`. An
227
- explicit list of coordinate pairs can also be given. The default is to
228
- greedily form an 'edge coloring' based on the sorted list of
229
- Hamiltonian pair coordinates. If a callable is supplied it will be used
230
- to generate the ordering before each sweep.
231
- second_order_reflect : bool, optional
232
- If ``True``, then apply each layer of gates in ``ordering`` forward
233
- with half the time step, then the same with reverse order.
234
- compute_energy_every : None or int, optional
235
- How often to compute and record the energy. If a positive integer 'n',
236
- the energy is computed *before* every nth sweep (i.e. including before
237
- the zeroth).
238
- compute_energy_final : bool, optional
239
- Whether to compute and record the energy at the end of the sweeps
240
- regardless of the value of ``compute_energy_every``. If you start
241
- sweeping again then this final energy is the same as the zeroth of the
242
- next set of sweeps and won't be recomputed.
243
- compute_energy_opts : dict, optional
244
- Supplied to
245
- :meth:`~quimb.tensor.tensor_2d.PEPS.compute_local_expectation`. By
246
- default ``max_bond`` is set to ``max(8, D**2)`` where ``D`` is the
247
- maximum bond to use for applying the gate, ``cutoff`` is set to ``0.0``
248
- and ``normalized`` is set to ``True``.
249
- compute_energy_fn : callable, optional
250
- Supply your own function to compute the energy, it should take the
251
- ``TEBD2D`` object as its only argument.
252
- callback : callable, optional
253
- A custom callback to run after every sweep, it should take the
254
- ``TEBD2D`` object as its only argument. If it returns any value
255
- that boolean evaluates to ``True`` then terminal the evolution.
256
- progbar : boolean, optional
257
- Whether to show a live progress bar during the evolution.
258
- kwargs
259
- Extra options for the specific ``TEBD2D`` subclass.
260
-
261
- Attributes
262
- ----------
263
- state : TensorNetwork2DVector
264
- The current state.
265
- ham : LocalHam2D
266
- The Hamiltonian being used to evolve.
267
- energy : float
268
- The current of the current state, this will trigger a computation if
269
- the energy at this iteration hasn't been computed yet.
270
- energies : list[float]
271
- The energies that have been computed, if any.
272
- its : list[int]
273
- The corresponding sequence of iteration numbers that energies have been
274
- computed at.
275
- taus : list[float]
276
- The corresponding sequence of time steps that energies have been
277
- computed at.
278
- best : dict
279
- If ``keep_best`` was set then the best recorded energy and the
280
- corresponding state that was computed - keys ``'energy'`` and
281
- ``'state'`` respectively.
282
- """
283
-
284
- def __init__(
285
- self,
286
- psi0,
287
- ham,
288
- tau=0.01,
289
- D=None,
290
- chi=None,
291
- imag=True,
292
- gate_opts=None,
293
- ordering=None,
294
- second_order_reflect=False,
295
- compute_energy_every=None,
296
- compute_energy_final=True,
297
- compute_energy_opts=None,
298
- compute_energy_fn=None,
299
- compute_energy_per_site=False,
300
- callback=None,
301
- keep_best=False,
302
- progbar=True,
303
- ):
304
- super().__init__(
305
- psi0=psi0,
306
- ham=ham,
307
- tau=tau,
308
- D=D,
309
- imag=imag,
310
- gate_opts=gate_opts,
311
- ordering=ordering,
312
- second_order_reflect=second_order_reflect,
313
- compute_energy_every=compute_energy_every,
314
- compute_energy_final=compute_energy_final,
315
- compute_energy_opts=compute_energy_opts,
316
- compute_energy_fn=compute_energy_fn,
317
- compute_energy_per_site=compute_energy_per_site,
318
- callback=callback,
319
- keep_best=keep_best,
320
- progbar=progbar,
321
- )
322
-
323
- # parse energy computation options
324
- if chi is None:
325
- chi = max(8, self.D**2)
326
- self.compute_energy_opts['max_bond'] = chi
327
- self.compute_energy_opts.setdefault('cutoff', 0.0)
328
- self.compute_energy_opts.setdefault('normalized', True)
329
-
330
- def compute_energy(self):
331
- """Compute and return the energy of the current state.
332
- """
333
- return self.state.compute_local_expectation(
334
- self.ham.terms,
335
- **self.compute_energy_opts
336
- )
337
-
338
- @property
339
- def chi(self):
340
- return self.compute_energy_opts['max_bond']
341
-
342
- @chi.setter
343
- def chi(self, value):
344
- self.compute_energy_opts['max_bond'] = round(value)
345
-
346
- def __repr__(self):
347
- s = "<{}(n={}, tau={}, D={}, chi={})>"
348
- return s.format(
349
- self.__class__.__name__, self.n, self.tau, self.D, self.chi)
350
-
351
-
352
- def conditioner(tn, value=None, sweeps=2, balance_bonds=True):
353
- """
354
- """
355
- if balance_bonds:
356
- for _ in range(sweeps - 1):
357
- tn.balance_bonds_()
358
- tn.equalize_norms_()
359
- tn.balance_bonds_()
360
- tn.equalize_norms_(value=value)
361
-
362
-
363
- class SimpleUpdate(TEBD2D):
364
- """A simple subclass of ``TEBD2D`` that overrides two key methods in
365
- order to keep 'diagonal gauges' living on the bonds of a PEPS. The gauges
366
- are stored separately from the main PEPS in the ``gauges`` attribute.
367
- Before and after a gate is applied they are absorbed and then extracted.
368
- When accessing the ``state`` attribute they are automatically inserted or
369
- you can call ``get_state(absorb_gauges=False)`` to lazily add them as
370
- hyperedge weights only. Reference: https://arxiv.org/abs/0806.3719.
371
-
372
- Parameters
373
- ----------
374
- psi0 : TensorNetwork2DVector
375
- The initial state.
376
- ham : LocalHam2D
377
- The Hamtiltonian consisting of local terms.
378
- tau : float, optional
379
- The default local exponent, if considered as time real values here
380
- imply imaginary time.
381
- max_bond : {'psi0', int, None}, optional
382
- The maximum bond dimension to keep when applying each gate.
383
- gate_opts : dict, optional
384
- Supplied to :meth:`quimb.tensor.tensor_2d.TensorNetwork2DVector.gate`,
385
- in addition to ``max_bond``. By default ``contract`` is set to
386
- 'reduce-split' and ``cutoff`` is set to ``0.0``.
387
- ordering : str, tuple[tuple[int]], callable, optional
388
- How to order the terms, if a string is given then use this as the
389
- strategy given to
390
- :meth:`~quimb.tensor.tensor_2d_tebd.LocalHam2D.get_auto_ordering`. An
391
- explicit list of coordinate pairs can also be given. The default is to
392
- greedily form an 'edge coloring' based on the sorted list of
393
- Hamiltonian pair coordinates. If a callable is supplied it will be used
394
- to generate the ordering before each sweep.
395
- second_order_reflect : bool, optional
396
- If ``True``, then apply each layer of gates in ``ordering`` forward
397
- with half the time step, then the same with reverse order.
398
- compute_energy_every : None or int, optional
399
- How often to compute and record the energy. If a positive integer 'n',
400
- the energy is computed *before* every nth sweep (i.e. including before
401
- the zeroth).
402
- compute_energy_final : bool, optional
403
- Whether to compute and record the energy at the end of the sweeps
404
- regardless of the value of ``compute_energy_every``. If you start
405
- sweeping again then this final energy is the same as the zeroth of the
406
- next set of sweeps and won't be recomputed.
407
- compute_energy_opts : dict, optional
408
- Supplied to
409
- :meth:`~quimb.tensor.tensor_2d.PEPS.compute_local_expectation`. By
410
- default ``max_bond`` is set to ``max(8, D**2)`` where ``D`` is the
411
- maximum bond to use for applying the gate, ``cutoff`` is set to ``0.0``
412
- and ``normalized`` is set to ``True``.
413
- compute_energy_fn : callable, optional
414
- Supply your own function to compute the energy, it should take the
415
- ``TEBD2D`` object as its only argument.
416
- callback : callable, optional
417
- A custom callback to run after every sweep, it should take the
418
- ``TEBD2D`` object as its only argument. If it returns any value
419
- that boolean evaluates to ``True`` then terminal the evolution.
420
- progbar : boolean, optional
421
- Whether to show a live progress bar during the evolution.
422
- gauge_renorm : bool, optional
423
- Whether to actively renormalize the singular value gauges.
424
- gauge_smudge : float, optional
425
- A small offset to use when applying the guage and its inverse to avoid
426
- numerical problems.
427
- condition_tensors : bool, optional
428
- Whether to actively equalize tensor norms for numerical stability.
429
- condition_balance_bonds : bool, optional
430
- If and when equalizing tensor norms, whether to also balance bonds as
431
- an additional conditioning.
432
- long_range_use_swaps : bool, optional
433
- If there are long range terms, whether to use swap gates to apply the
434
- terms. If ``False``, a long range blob tensor (which won't scale well
435
- for long distances) is formed instead.
436
- long_range_path_sequence : str or callable, optional
437
- If there are long range terms how to generate the path between the two
438
- coordinates. If callable, should take the two coordinates and return a
439
- sequence of coordinates that links them, else passed to
440
- ``gen_long_range_swap_path``.
441
-
442
- Attributes
443
- ----------
444
- state : TensorNetwork2DVector
445
- The current state.
446
- ham : LocalHam2D
447
- The Hamiltonian being used to evolve.
448
- energy : float
449
- The current of the current state, this will trigger a computation if
450
- the energy at this iteration hasn't been computed yet.
451
- energies : list[float]
452
- The energies that have been computed, if any.
453
- its : list[int]
454
- The corresponding sequence of iteration numbers that energies have been
455
- computed at.
456
- taus : list[float]
457
- The corresponding sequence of time steps that energies have been
458
- computed at.
459
- best : dict
460
- If ``keep_best`` was set then the best recorded energy and the
461
- corresponding state that was computed - keys ``'energy'`` and
462
- ``'state'`` respectively.
463
- """
464
-
465
- def __init__(
466
- self,
467
- psi0,
468
- ham,
469
- tau=0.01,
470
- D=None,
471
- chi=None,
472
- gauge_renorm=True,
473
- gauge_smudge=1e-6,
474
- condition_tensors=True,
475
- condition_balance_bonds=True,
476
- long_range_use_swaps=False,
477
- long_range_path_sequence='random',
478
- imag=True,
479
- gate_opts=None,
480
- ordering=None,
481
- second_order_reflect=False,
482
- compute_energy_every=None,
483
- compute_energy_final=True,
484
- compute_energy_opts=None,
485
- compute_energy_fn=None,
486
- compute_energy_per_site=False,
487
- callback=None,
488
- keep_best=False,
489
- progbar=True,
490
- ):
491
- super().__init__(
492
- psi0=psi0,
493
- ham=ham,
494
- tau=tau,
495
- D=D,
496
- chi=chi,
497
- imag=imag,
498
- gate_opts=gate_opts,
499
- ordering=ordering,
500
- second_order_reflect=second_order_reflect,
501
- compute_energy_every=compute_energy_every,
502
- compute_energy_final=compute_energy_final,
503
- compute_energy_opts=compute_energy_opts,
504
- compute_energy_fn=compute_energy_fn,
505
- compute_energy_per_site=compute_energy_per_site,
506
- callback=callback,
507
- keep_best=keep_best,
508
- progbar=progbar,
509
- )
510
- self.gauge_renorm = gauge_renorm
511
- self.gauge_smudge = gauge_smudge
512
- self.condition_tensors = condition_tensors
513
- self.condition_balance_bonds = condition_balance_bonds
514
- self.gate_opts['long_range_use_swaps'] = long_range_use_swaps
515
- self.long_range_path_sequence = long_range_path_sequence
516
-
517
- def _initialize_gauges(self):
518
- """Create unit singular values, stored as tensors.
519
- """
520
- # create the gauges like whatever data array is in the first site.
521
- data00 = next(iter(self._psi.tensor_map.values())).data
522
-
523
- self._gauges = dict()
524
- for ija, ijb in self._psi.gen_bond_coos():
525
- bnd = self._psi.bond(ija, ijb)
526
- d = self._psi.ind_size(bnd)
527
- Tsval = Tensor(
528
- do('ones', (d,), dtype=data00.dtype, like=data00),
529
- inds=[bnd],
530
- tags=[
531
- self._psi.site_tag(*ija),
532
- self._psi.site_tag(*ijb),
533
- 'SU_gauge',
534
- ]
535
- )
536
- self._gauges[tuple(sorted((ija, ijb)))] = Tsval
537
-
538
- @property
539
- def gauges(self):
540
- """The dictionary of bond pair coordinates to Tensors describing the
541
- weights (``t = gauges[pair]; t.data``) and index
542
- (``t = gauges[pair]; t.inds[0]``) of all the gauges.
543
- """
544
- return self._gauges
545
-
546
- @property
547
- def long_range_use_swaps(self):
548
- return self.gate_opts['long_range_use_swaps']
549
-
550
- @long_range_use_swaps.setter
551
- def long_range_use_swaps(self, b):
552
- self.gate_opts['long_range_use_swaps'] = bool(b)
553
-
554
- def gate(self, U, where):
555
- """Like ``TEBD2D.gate`` but absorb and extract the relevant gauges
556
- before and after each gate application.
557
- """
558
- ija, ijb = where
559
-
560
- if callable(self.long_range_path_sequence):
561
- long_range_path_sequence = self.long_range_path_sequence(ija, ijb)
562
- else:
563
- long_range_path_sequence = self.long_range_path_sequence
564
-
565
- if self.long_range_use_swaps:
566
- path = tuple(gen_long_range_swap_path(
567
- ija, ijb, sequence=long_range_path_sequence))
568
- string = swap_path_to_long_range_path(path, ija)
569
- else:
570
- # get the string linking the two sites
571
- string = path = tuple(gen_long_range_path(
572
- ija, ijb, sequence=long_range_path_sequence))
573
-
574
- def env_neighbours(i, j):
575
- return tuple(filter(
576
- lambda coo: self._psi.valid_coo((coo)) and coo not in string,
577
- nearest_neighbors((i, j))
578
- ))
579
-
580
- # get the relevant neighbours for string of sites
581
- neighbours = {site: env_neighbours(*site) for site in string}
582
-
583
- # absorb the 'outer' gauges from these neighbours
584
- for site in string:
585
- Tij = self._psi[site]
586
- for neighbour in neighbours[site]:
587
- Tsval = self.gauges[tuple(sorted((site, neighbour)))]
588
- Tij.multiply_index_diagonal_(
589
- ind=Tsval.inds[0], x=(Tsval.data + self.gauge_smudge))
590
-
591
- # absorb the inner bond gauges equally into both sites along string
592
- for site_a, site_b in pairwise(string):
593
- Ta, Tb = self._psi[site_a], self._psi[site_b]
594
- Tsval = self.gauges[tuple(sorted((site_a, site_b)))]
595
- bnd, = Tsval.inds
596
- Ta.multiply_index_diagonal_(ind=bnd, x=Tsval.data**0.5)
597
- Tb.multiply_index_diagonal_(ind=bnd, x=Tsval.data**0.5)
598
-
599
- # perform the gate, retrieving new bond singular values
600
- info = dict()
601
- self._psi.gate_(U, where, absorb=None, info=info,
602
- long_range_path_sequence=path, **self.gate_opts)
603
-
604
- # set the new singualar values all along the chain
605
- for site_a, site_b in pairwise(string):
606
- bond_pair = tuple(sorted((site_a, site_b)))
607
- s, = info.values()
608
- if self.gauge_renorm:
609
- # keep the singular values from blowing up
610
- s = s / do("max", s)
611
- Tsval = self.gauges[bond_pair]
612
- Tsval.modify(data=s)
613
-
614
- # absorb the 'outer' gauges from these neighbours
615
- for site in string:
616
- Tij = self._psi[site]
617
- for neighbour in neighbours[site]:
618
- Tsval = self.gauges[tuple(sorted((site, neighbour)))]
619
- Tij.multiply_index_diagonal_(
620
- ind=Tsval.inds[0], x=(Tsval.data + self.gauge_smudge)**-1)
621
-
622
- def get_state(self, absorb_gauges=True):
623
- """Return the state, with the diagonal bond gauges either absorbed
624
- equally into the tensors on either side of them
625
- (``absorb_gauges=True``, the default), or left lazily represented in
626
- the tensor network with hyperedges (``absorb_gauges=False``).
627
- """
628
- psi = self._psi.copy()
629
-
630
- if not absorb_gauges:
631
- for Tsval in self.gauges.values():
632
- psi &= Tsval
633
- else:
634
- for (ija, ijb), Tsval in self.gauges.items():
635
- bnd, = Tsval.inds
636
- Ta = psi[ija]
637
- Tb = psi[ijb]
638
- Ta.multiply_index_diagonal_(bnd, Tsval.data**0.5)
639
- Tb.multiply_index_diagonal_(bnd, Tsval.data**0.5)
640
-
641
- if self.condition_tensors:
642
- conditioner(psi, balance_bonds=self.condition_balance_bonds)
643
-
644
- return psi
645
-
646
- def set_state(self, psi):
647
- """Set the wavefunction state, this resets the environment gauges to
648
- unity.
649
- """
650
- self._psi = psi.copy()
651
- self._initialize_gauges()
652
-
653
-
654
- def gate_full_update_als(
655
- ket,
656
- env,
657
- bra,
658
- G,
659
- where,
660
- tags_plq,
661
- steps,
662
- tol,
663
- max_bond,
664
- optimize='auto-hq',
665
- solver='solve',
666
- dense=True,
667
- enforce_pos=False,
668
- pos_smudge=1e-6,
669
- init_simple_guess=True,
670
- condition_tensors=True,
671
- condition_maintain_norms=True,
672
- condition_balance_bonds=True,
673
- ):
674
- ket_plq = ket.select_any(tags_plq)
675
- bra_plq = bra.select_any(tags_plq)
676
-
677
- # this is the full target (copy - not virtual)
678
- target = ket_plq.gate(G, where, contract=False) | env
679
-
680
- if init_simple_guess:
681
- ket_plq.gate_(G, where, contract='reduce-split', max_bond=max_bond)
682
- for site in tags_plq:
683
- bra_plq[site].modify(data=conj(ket_plq[site].data))
684
-
685
- if condition_tensors:
686
- conditioner(ket_plq, balance_bonds=condition_balance_bonds)
687
- for site in tags_plq:
688
- bra_plq[site].modify(data=conj(ket_plq[site].data))
689
- if condition_maintain_norms:
690
- pre_norm = ket_plq[site].norm()
691
-
692
- overlap = bra_plq | target
693
- norm_plq = bra_plq | env | ket_plq
694
-
695
- xs = dict()
696
- x_previous = dict()
697
- previous_cost = None
698
-
699
- with contract_strategy(optimize):
700
- for i in range(steps):
701
-
702
- for site in tags_plq:
703
- lix = norm_plq[site, 'BRA'].inds[:-1]
704
- rix = norm_plq[site, 'KET'].inds[:-1]
705
- # remove site tensors and group their indices
706
- if dense:
707
- N = (norm_plq.select(site, which='!any')
708
- .to_dense(lix, rix))
709
-
710
- if enforce_pos:
711
- el, ev = do('linalg.eigh', (N + dag(N)) / 2)
712
- el = do('clip', el, pos_smudge, None)
713
- N = ev @ do('diag', el) @ dag(ev)
714
-
715
- else:
716
- N = (norm_plq.select(site, which='!any')
717
- .aslinearoperator(lix, rix))
718
-
719
- # target vector (remove lower site tensor and contract to vec)
720
- b = (overlap
721
- .select((site, 'BRA'), which='!all')
722
- .to_dense(overlap[site, 'BRA'].inds[:-1],
723
- overlap[site, 'BRA'].inds[-1:]))
724
-
725
- if solver == 'solve':
726
- x = do('linalg.solve', N, b)
727
- elif solver == 'lstsq':
728
- x = do('linalg.lstsq', N, b, rcond=tol * 1e-3)[0]
729
- else:
730
- # use scipy sparse linalg solvers
731
- if solver in ('lsqr', 'lsmr'):
732
- solver_opts = dict(atol=tol, btol=tol)
733
- else:
734
- solver_opts = dict(tol=tol)
735
-
736
- # use current site as initial guess (iterate over site ind)
737
- x0 = x_previous.get(site, b)
738
- x = np.stack([
739
- getattr(spla, solver)
740
- (N, b[..., k], x0=x0[..., k], **solver_opts)[0]
741
- for k in range(x0.shape[-1])
742
- ], axis=-1)
743
-
744
- # update the tensors (all 'virtual' TNs above also updated)
745
- Tk, Tb = ket[site], bra[site]
746
- Tk.modify(data=reshape(x, Tk.shape))
747
- Tb.modify(data=reshape(conj(x), Tb.shape))
748
-
749
- # store solution to check convergence
750
- xs[site] = x
751
-
752
- # after updating both sites check for convergence of tensor entries
753
- cost_fid = do('trace', do('real', dag(x) @ b))
754
- cost_norm = do('abs', do('trace', dag(x) @ (N @ x)))
755
- cost = - 2 * cost_fid + cost_norm
756
-
757
- converged = (
758
- (previous_cost is not None) and
759
- (abs(cost - previous_cost) < tol)
760
- )
761
- if converged:
762
- break
763
-
764
- previous_cost = cost
765
- for site in tags_plq:
766
- x_previous[site] = xs[site]
767
-
768
- if condition_tensors:
769
- if condition_maintain_norms:
770
- conditioner(
771
- ket_plq, value=pre_norm, balance_bonds=condition_balance_bonds)
772
- else:
773
- conditioner(
774
- ket_plq, balance_bonds=condition_balance_bonds)
775
- for site in tags_plq:
776
- bra_plq[site].modify(data=conj(ket_plq[site].data))
777
-
778
-
779
- def gate_full_update_autodiff_fidelity(
780
- ket,
781
- env,
782
- bra,
783
- G,
784
- where,
785
- tags_plq,
786
- steps,
787
- tol,
788
- max_bond,
789
- optimize='auto-hq',
790
- autodiff_backend='autograd',
791
- autodiff_optimizer='L-BFGS-B',
792
- init_simple_guess=True,
793
- condition_tensors=True,
794
- condition_maintain_norms=True,
795
- condition_balance_bonds=True,
796
- **kwargs,
797
- ):
798
- ket_plq = ket.select_any(tags_plq).view_like_(ket)
799
- bra_plq = bra.select_any(tags_plq).view_like_(bra)
800
-
801
- # the target sites + gate and also norm (copy - not virtual)
802
- target = ket_plq.gate(G, where, contract=False) | env
803
-
804
- # make initial guess the simple gate tensors
805
- if init_simple_guess:
806
- ket_plq.gate_(G, where, contract='reduce-split', max_bond=max_bond)
807
- for site in tags_plq:
808
- bra_plq[site].modify(data=conj(ket_plq[site].data))
809
-
810
- if condition_tensors:
811
- conditioner(ket_plq, balance_bonds=condition_balance_bonds)
812
- for site in tags_plq:
813
- bra_plq[site].modify(data=conj(ket_plq[site].data))
814
- if condition_maintain_norms:
815
- pre_norm = ket_plq[site].norm()
816
-
817
- def fidelity(bra_plq):
818
- for site in tags_plq:
819
- ket_plq[site].modify(data=conj(bra_plq[site].data))
820
-
821
- fid = (bra_plq | target).contract(all, optimize=optimize)
822
- norm = (bra_plq | env | ket_plq).contract(all, optimize=optimize)
823
-
824
- return - 2 * do('abs', fid) + do('abs', norm)
825
-
826
- tnopt = TNOptimizer(
827
- bra_plq,
828
- loss_fn=fidelity,
829
- tags=tags_plq,
830
- progbar=False,
831
- optimizer=autodiff_optimizer,
832
- autodiff_backend=autodiff_backend,
833
- **kwargs,
834
- )
835
- bra_plq_opt = tnopt.optimize(steps, tol=tol)
836
-
837
- for site in tags_plq:
838
- new_data = bra_plq_opt[site].data
839
- ket[site].modify(data=conj(new_data))
840
- bra[site].modify(data=new_data)
841
-
842
- if condition_tensors:
843
- if condition_maintain_norms:
844
- conditioner(
845
- ket_plq, value=pre_norm, balance_bonds=condition_balance_bonds)
846
- else:
847
- conditioner(
848
- ket_plq, balance_bonds=condition_balance_bonds)
849
- for site in tags_plq:
850
- bra_plq[site].modify(data=conj(ket_plq[site].data))
851
-
852
-
853
- def get_default_full_update_fit_opts():
854
- """The default options for the full update gate fitting procedure.
855
- """
856
- return {
857
- # general
858
- 'tol': 1e-10,
859
- 'steps': 20,
860
- 'init_simple_guess': True,
861
- 'condition_tensors': True,
862
- 'condition_maintain_norms': True,
863
- # alternative least squares
864
- 'als_dense': True,
865
- 'als_solver': 'solve',
866
- 'als_enforce_pos': False,
867
- 'als_enforce_pos_smudge': 1e-6,
868
- # automatic differentation optimizing
869
- 'autodiff_backend': 'autograd',
870
- 'autodiff_optimizer': 'L-BFGS-B',
871
- }
872
-
873
-
874
- def parse_specific_gate_opts(strategy, fit_opts):
875
- """Parse the options from ``fit_opts`` which are relevant for ``strategy``.
876
- """
877
- gate_opts = {
878
- 'tol': fit_opts['tol'],
879
- 'steps': fit_opts['steps'],
880
- 'init_simple_guess': fit_opts['init_simple_guess'],
881
- 'condition_tensors': fit_opts['condition_tensors'],
882
- 'condition_maintain_norms': fit_opts['condition_maintain_norms'],
883
- }
884
-
885
- if 'als' in strategy:
886
- gate_opts['solver'] = fit_opts['als_solver']
887
- gate_opts['dense'] = fit_opts['als_dense']
888
- gate_opts['enforce_pos'] = fit_opts['als_enforce_pos']
889
- gate_opts['pos_smudge'] = fit_opts['als_enforce_pos_smudge']
890
-
891
- elif 'autodiff' in strategy:
892
- gate_opts['autodiff_backend'] = fit_opts['autodiff_backend']
893
- gate_opts['autodiff_optimizer'] = fit_opts['autodiff_optimizer']
894
-
895
- return gate_opts
896
-
897
-
898
- class FullUpdate(TEBD2D):
899
- """Implements the 'Full Update' version of 2D imaginary time evolution,
900
- where each application of a gate is fitted to the current tensors using a
901
- boundary contracted environment.
902
-
903
- Parameters
904
- ----------
905
- psi0 : TensorNetwork2DVector
906
- The initial state.
907
- ham : LocalHam2D
908
- The Hamtiltonian consisting of local terms.
909
- tau : float, optional
910
- The default local exponent, if considered as time real values here
911
- imply imaginary time.
912
- max_bond : {'psi0', int, None}, optional
913
- The maximum bond dimension to keep when applying each gate.
914
- gate_opts : dict, optional
915
- Supplied to :meth:`quimb.tensor.tensor_2d.TensorNetwork2DVector.gate`,
916
- in addition to ``max_bond``. By default ``contract`` is set to
917
- 'reduce-split' and ``cutoff`` is set to ``0.0``.
918
- ordering : str, tuple[tuple[int]], callable, optional
919
- How to order the terms, if a string is given then use this as the
920
- strategy given to
921
- :meth:`~quimb.tensor.tensor_2d_tebd.LocalHam2D.get_auto_ordering`. An
922
- explicit list of coordinate pairs can also be given. The default is to
923
- greedily form an 'edge coloring' based on the sorted list of
924
- Hamiltonian pair coordinates. If a callable is supplied it will be used
925
- to generate the ordering before each sweep.
926
- second_order_reflect : bool, optional
927
- If ``True``, then apply each layer of gates in ``ordering`` forward
928
- with half the time step, then the same with reverse order.
929
- compute_energy_every : None or int, optional
930
- How often to compute and record the energy. If a positive integer 'n',
931
- the energy is computed *before* every nth sweep (i.e. including before
932
- the zeroth).
933
- compute_energy_final : bool, optional
934
- Whether to compute and record the energy at the end of the sweeps
935
- regardless of the value of ``compute_energy_every``. If you start
936
- sweeping again then this final energy is the same as the zeroth of the
937
- next set of sweeps and won't be recomputed.
938
- compute_energy_opts : dict, optional
939
- Supplied to
940
- :meth:`~quimb.tensor.tensor_2d.PEPS.compute_local_expectation`. By
941
- default ``max_bond`` is set to ``max(8, D**2)`` where ``D`` is the
942
- maximum bond to use for applying the gate, ``cutoff`` is set to ``0.0``
943
- and ``normalized`` is set to ``True``.
944
- compute_energy_fn : callable, optional
945
- Supply your own function to compute the energy, it should take the
946
- ``TEBD2D`` object as its only argument.
947
- callback : callable, optional
948
- A custom callback to run after every sweep, it should take the
949
- ``TEBD2D`` object as its only argument. If it returns any value
950
- that boolean evaluates to ``True`` then terminal the evolution.
951
- progbar : boolean, optional
952
- Whether to show a live progress bar during the evolution.
953
- fit_strategy : {'als', 'autodiff-fidelity'}, optional
954
- Core method used to fit the gate application.
955
-
956
- * ``'als'``: alternating least squares
957
- * ``'autodiff-fidelity'``: local fidelity using autodiff
958
-
959
- fit_opts : dict, optional
960
- Advanced options for the gate application fitting functions. Defaults
961
- are inserted and can be accessed via the ``.fit_opts`` attribute.
962
- compute_envs_every : {'term', 'group', 'sweep', int}, optional
963
- How often to recompute the environments used to the fit the gate
964
- application:
965
-
966
- * ``'term'``: every gate
967
- * ``'group'``: every set of commuting gates (the default)
968
- * ``'sweep'``: every total sweep
969
- * int: every ``x`` number of total sweeps
970
-
971
- pre_normalize : bool, optional
972
- Actively renormalize the state using the computed environments.
973
- condition_tensors : bool, optional
974
- Whether to actively equalize tensor norms for numerical stability.
975
- condition_balance_bonds : bool, optional
976
- If and when equalizing tensor norms, whether to also balance bonds as
977
- an additional conditioning.
978
- contract_optimize : str, optional
979
- Contraction path optimizer to use for gate + env + sites contractions.
980
-
981
- Attributes
982
- ----------
983
- state : TensorNetwork2DVector
984
- The current state.
985
- ham : LocalHam2D
986
- The Hamiltonian being used to evolve.
987
- energy : float
988
- The current of the current state, this will trigger a computation if
989
- the energy at this iteration hasn't been computed yet.
990
- energies : list[float]
991
- The energies that have been computed, if any.
992
- its : list[int]
993
- The corresponding sequence of iteration numbers that energies have been
994
- computed at.
995
- taus : list[float]
996
- The corresponding sequence of time steps that energies have been
997
- computed at.
998
- best : dict
999
- If ``keep_best`` was set then the best recorded energy and the
1000
- corresponding state that was computed - keys ``'energy'`` and
1001
- ``'state'`` respectively.
1002
- fit_opts : dict
1003
- Detailed options for fitting the applied gate.
1004
- """
1005
-
1006
- def __init__(
1007
- self,
1008
- psi0,
1009
- ham,
1010
- tau=0.01,
1011
- D=None,
1012
- chi=None,
1013
- fit_strategy='als',
1014
- fit_opts=None,
1015
- compute_envs_every=1,
1016
- pre_normalize=True,
1017
- condition_tensors=True,
1018
- condition_balance_bonds=True,
1019
- contract_optimize='auto-hq',
1020
- imag=True,
1021
- gate_opts=None,
1022
- ordering=None,
1023
- second_order_reflect=False,
1024
- compute_energy_every=None,
1025
- compute_energy_final=True,
1026
- compute_energy_opts=None,
1027
- compute_energy_fn=None,
1028
- compute_energy_per_site=False,
1029
- callback=None,
1030
- keep_best=False,
1031
- progbar=True,
1032
- ):
1033
- super().__init__(
1034
- psi0=psi0,
1035
- ham=ham,
1036
- tau=tau,
1037
- D=D,
1038
- chi=chi,
1039
- imag=imag,
1040
- gate_opts=gate_opts,
1041
- ordering=ordering,
1042
- second_order_reflect=second_order_reflect,
1043
- compute_energy_every=compute_energy_every,
1044
- compute_energy_final=compute_energy_final,
1045
- compute_energy_opts=compute_energy_opts,
1046
- compute_energy_fn=compute_energy_fn,
1047
- compute_energy_per_site=compute_energy_per_site,
1048
- callback=callback,
1049
- keep_best=keep_best,
1050
- progbar=progbar,
1051
- )
1052
-
1053
- self.fit_strategy = str(fit_strategy)
1054
- self.fit_opts = get_default_full_update_fit_opts()
1055
- if fit_opts is not None:
1056
- bad_opts = set(fit_opts) - set(self.fit_opts)
1057
- if bad_opts:
1058
- raise ValueError("Invalid fit option(s): {}".format(bad_opts))
1059
- self.fit_opts.update(fit_opts)
1060
-
1061
- self.pre_normalize = bool(pre_normalize)
1062
- self.contract_optimize = str(contract_optimize)
1063
- self.condition_tensors = bool(condition_tensors)
1064
- self.condition_balance_bonds = bool(condition_balance_bonds)
1065
-
1066
- self.compute_envs_every = compute_envs_every
1067
- self._env_n = self._env_term_count = self._env_group_count = -1
1068
-
1069
- self._psi.add_tag('KET')
1070
-
1071
- @property
1072
- def fit_strategy(self):
1073
- return self._fit_strategy
1074
-
1075
- @fit_strategy.setter
1076
- def fit_strategy(self, fit_strategy):
1077
- self._gate_fit_fn = {
1078
- 'als': gate_full_update_als,
1079
- 'autodiff-fidelity': gate_full_update_autodiff_fidelity,
1080
- }[fit_strategy]
1081
- self._fit_strategy = fit_strategy
1082
-
1083
- def set_state(self, psi):
1084
- self._psi = psi.copy()
1085
-
1086
- # ensure the final dimension of each tensor is the physical dim
1087
- for tag, ind in zip(self._psi.site_tags, self._psi.site_inds):
1088
- t = self._psi[tag]
1089
- if t.inds[-1] != ind:
1090
- new_inds = [i for i in t.inds if i != ind] + [ind]
1091
- t.transpose_(*new_inds)
1092
-
1093
- @property
1094
- def compute_envs_every(self):
1095
- return self._compute_envs_every
1096
-
1097
- @compute_envs_every.setter
1098
- def compute_envs_every(self, x):
1099
- if x == 'sweep':
1100
- self._need_to_recompute_envs = lambda: (
1101
- (self._n != self._env_n)
1102
- )
1103
- elif x == 'group':
1104
- self._need_to_recompute_envs = lambda: (
1105
- (self._n != self._env_n) or
1106
- (self._group_count != self._env_group_count)
1107
- )
1108
- elif x == 'term':
1109
- self._need_to_recompute_envs = lambda: (
1110
- (self._n != self._env_n) or
1111
- (self._group_count != self._env_group_count) or
1112
- (self._term_count != self._env_term_count)
1113
- )
1114
- else:
1115
- x = max(1, int(x))
1116
- self._need_to_recompute_envs = lambda: (self._n >= self._env_n + x)
1117
-
1118
- self._compute_envs_every = x
1119
-
1120
- def _maybe_compute_plaquette_envs(self, force=False):
1121
- """Compute and store the plaquette environments for all local terms.
1122
- """
1123
- # first check if we need to compute the envs
1124
- if not self._need_to_recompute_envs() and not force:
1125
- return
1126
-
1127
- if self.condition_tensors:
1128
- conditioner(self._psi, balance_bonds=self.condition_balance_bonds)
1129
-
1130
- # useful to store the bra that went into making the norm
1131
- norm, _, self._bra = self._psi.make_norm(return_all=True)
1132
-
1133
- envs = dict()
1134
- for x_bsz, y_bsz in calc_plaquette_sizes(self.ham.terms):
1135
- envs.update(norm.compute_plaquette_environments(
1136
- x_bsz=x_bsz, y_bsz=y_bsz, max_bond=self.chi, cutoff=0.0))
1137
-
1138
- if self.pre_normalize:
1139
- # get the first plaquette env and use it to compute current norm
1140
- p0, env0 = next(iter(envs.items()))
1141
- sites = plaquette_to_sites(p0)
1142
- tags_plq = tuple(starmap(norm.site_tag, sites))
1143
- norm_plq = norm.select_any(tags_plq) | env0
1144
-
1145
- # contract the local plaquette norm
1146
- nfactor = do(
1147
- 'abs', norm_plq.contract(all, optimize=self.contract_optimize))
1148
-
1149
- # scale the bra and ket and each of the plaquette environments
1150
- self._psi.multiply_(nfactor**(-1 / 2), spread_over='all')
1151
- self._bra.multiply_(nfactor**(-1 / 2), spread_over='all')
1152
-
1153
- # scale the envs, taking into account the number of sites missing
1154
- n = self._psi.num_tensors
1155
- for ((_, _), (di, dj)), env in envs.items():
1156
- n_missing = di * dj
1157
- env.multiply_(nfactor ** (n_missing / n - 1),
1158
- spread_over='all')
1159
-
1160
- self.plaquette_envs = envs
1161
- self.plaquette_mapping = calc_plaquette_map(envs)
1162
-
1163
- self._env_n = self._n
1164
- self._env_group_count = self._group_count
1165
- self._env_term_count = self._term_count
1166
-
1167
- def presweep(self, i):
1168
- """Full update presweep - compute envs and inject gate options.
1169
- """
1170
- # inject the specific gate options required (do
1171
- # here so user can change options between sweeps)
1172
- self._gate_opts = parse_specific_gate_opts(
1173
- self.fit_strategy, self.fit_opts)
1174
-
1175
- # keep track of number of gates applied, and commutative groups
1176
- self._term_count = 0
1177
- self._group_count = 0
1178
- self._current_group = set()
1179
-
1180
- def compute_energy(self):
1181
- """Full update compute energy - use the (likely) already calculated
1182
- plaquette environments.
1183
- """
1184
- self._maybe_compute_plaquette_envs(force=self._n != self._env_n)
1185
-
1186
- return self.state.compute_local_expectation(
1187
- self.ham.terms,
1188
- plaquette_envs=self.plaquette_envs,
1189
- plaquette_mapping=self.plaquette_mapping,
1190
- **self.compute_energy_opts
1191
- )
1192
-
1193
- def gate(self, G, where):
1194
- """Apply the gate ``G`` at sites where, using a fitting method that
1195
- takes into account the current environment.
1196
- """
1197
- # check if the new term commutes with those applied so far, this is to
1198
- # decide if we need to recompute the environments
1199
- swhere = set(where)
1200
- if self._current_group.isdisjoint(swhere):
1201
- # if so add it to the grouping
1202
- self._current_group |= swhere
1203
- else:
1204
- # else increment and reset the grouping
1205
- self._current_group = swhere
1206
- self._group_count += 1
1207
-
1208
- # get the plaquette containing ``where`` and the sites it contains -
1209
- # these will all be fitted
1210
- self._maybe_compute_plaquette_envs()
1211
- plq = self.plaquette_mapping[tuple(sorted(where))]
1212
- env = self.plaquette_envs[plq]
1213
- tags_plq = tuple(starmap(self._psi.site_tag, plaquette_to_sites(plq)))
1214
-
1215
- # perform the gate, inplace
1216
- self._gate_fit_fn(
1217
- ket=self._psi,
1218
- env=env,
1219
- bra=self._bra,
1220
- G=G,
1221
- where=where,
1222
- tags_plq=tags_plq,
1223
- max_bond=self.D,
1224
- optimize=self.contract_optimize,
1225
- condition_balance_bonds=self.condition_balance_bonds,
1226
- **self._gate_opts
1227
- )
1228
-
1229
- # increments every gate call regardless
1230
- self._term_count += 1