Trajectree 0.0.1__py3-none-any.whl → 0.0.3__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 +0 -3
- trajectree/fock_optics/devices.py +1 -1
- trajectree/fock_optics/light_sources.py +2 -2
- trajectree/fock_optics/measurement.py +9 -9
- trajectree/fock_optics/outputs.py +10 -6
- trajectree/fock_optics/utils.py +9 -6
- trajectree/sequence/swap.py +5 -4
- trajectree/trajectory.py +5 -4
- {trajectree-0.0.1.dist-info → trajectree-0.0.3.dist-info}/METADATA +2 -3
- trajectree-0.0.3.dist-info/RECORD +16 -0
- trajectree/quimb/docs/_pygments/_pygments_dark.py +0 -118
- trajectree/quimb/docs/_pygments/_pygments_light.py +0 -118
- trajectree/quimb/docs/conf.py +0 -158
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +0 -62
- trajectree/quimb/quimb/__init__.py +0 -507
- trajectree/quimb/quimb/calc.py +0 -1491
- trajectree/quimb/quimb/core.py +0 -2279
- trajectree/quimb/quimb/evo.py +0 -712
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +0 -129
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +0 -109
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +0 -397
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +0 -653
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +0 -571
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +0 -775
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +0 -537
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +0 -194
- trajectree/quimb/quimb/experimental/cluster_update.py +0 -286
- trajectree/quimb/quimb/experimental/merabuilder.py +0 -865
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +0 -15
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +0 -1631
- trajectree/quimb/quimb/experimental/schematic.py +0 -7
- trajectree/quimb/quimb/experimental/tn_marginals.py +0 -130
- trajectree/quimb/quimb/experimental/tnvmc.py +0 -1483
- trajectree/quimb/quimb/gates.py +0 -36
- trajectree/quimb/quimb/gen/__init__.py +0 -2
- trajectree/quimb/quimb/gen/operators.py +0 -1167
- trajectree/quimb/quimb/gen/rand.py +0 -713
- trajectree/quimb/quimb/gen/states.py +0 -479
- trajectree/quimb/quimb/linalg/__init__.py +0 -6
- trajectree/quimb/quimb/linalg/approx_spectral.py +0 -1109
- trajectree/quimb/quimb/linalg/autoblock.py +0 -258
- trajectree/quimb/quimb/linalg/base_linalg.py +0 -719
- trajectree/quimb/quimb/linalg/mpi_launcher.py +0 -397
- trajectree/quimb/quimb/linalg/numpy_linalg.py +0 -244
- trajectree/quimb/quimb/linalg/rand_linalg.py +0 -514
- trajectree/quimb/quimb/linalg/scipy_linalg.py +0 -293
- trajectree/quimb/quimb/linalg/slepc_linalg.py +0 -892
- trajectree/quimb/quimb/schematic.py +0 -1518
- trajectree/quimb/quimb/tensor/__init__.py +0 -401
- trajectree/quimb/quimb/tensor/array_ops.py +0 -610
- trajectree/quimb/quimb/tensor/circuit.py +0 -4824
- trajectree/quimb/quimb/tensor/circuit_gen.py +0 -411
- trajectree/quimb/quimb/tensor/contraction.py +0 -336
- trajectree/quimb/quimb/tensor/decomp.py +0 -1255
- trajectree/quimb/quimb/tensor/drawing.py +0 -1646
- trajectree/quimb/quimb/tensor/fitting.py +0 -385
- trajectree/quimb/quimb/tensor/geometry.py +0 -583
- trajectree/quimb/quimb/tensor/interface.py +0 -114
- trajectree/quimb/quimb/tensor/networking.py +0 -1058
- trajectree/quimb/quimb/tensor/optimize.py +0 -1818
- trajectree/quimb/quimb/tensor/tensor_1d.py +0 -4778
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +0 -1854
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +0 -662
- trajectree/quimb/quimb/tensor/tensor_2d.py +0 -5954
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +0 -96
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +0 -1230
- trajectree/quimb/quimb/tensor/tensor_3d.py +0 -2869
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +0 -46
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +0 -60
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +0 -3237
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +0 -565
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +0 -1138
- trajectree/quimb/quimb/tensor/tensor_builder.py +0 -5411
- trajectree/quimb/quimb/tensor/tensor_core.py +0 -11179
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +0 -1472
- trajectree/quimb/quimb/tensor/tensor_mera.py +0 -204
- trajectree/quimb/quimb/utils.py +0 -892
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +0 -501
- trajectree/quimb/tests/test_calc.py +0 -788
- trajectree/quimb/tests/test_core.py +0 -847
- trajectree/quimb/tests/test_evo.py +0 -565
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +0 -361
- trajectree/quimb/tests/test_gen/test_rand.py +0 -296
- trajectree/quimb/tests/test_gen/test_states.py +0 -261
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +0 -368
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +0 -351
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +0 -127
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +0 -84
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +0 -134
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +0 -283
- 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 +0 -39
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +0 -67
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +0 -64
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +0 -51
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +0 -142
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +0 -101
- trajectree/quimb/tests/test_tensor/test_circuit.py +0 -816
- trajectree/quimb/tests/test_tensor/test_contract.py +0 -67
- trajectree/quimb/tests/test_tensor/test_decomp.py +0 -40
- trajectree/quimb/tests/test_tensor/test_mera.py +0 -52
- trajectree/quimb/tests/test_tensor/test_optimizers.py +0 -488
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +0 -1171
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +0 -606
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +0 -144
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +0 -123
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +0 -226
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +0 -441
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +0 -2066
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +0 -388
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +0 -63
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +0 -270
- trajectree/quimb/tests/test_utils.py +0 -85
- trajectree-0.0.1.dist-info/RECORD +0 -126
- {trajectree-0.0.1.dist-info → trajectree-0.0.3.dist-info}/WHEEL +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.3.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.3.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
|