Skip to content

Commit fbf1f0c

Browse files
committed
tn3d: update contract_peps_sweep and docstrings
1 parent 6a98903 commit fbf1f0c

File tree

1 file changed

+148
-63
lines changed

1 file changed

+148
-63
lines changed

quimb/tensor/tensor_3d.py

Lines changed: 148 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,28 @@
22

33
import functools
44
import itertools
5-
from operator import add
6-
from numbers import Integral
75
from collections import defaultdict
8-
from itertools import product, combinations
6+
from itertools import combinations, product
7+
from numbers import Integral
8+
from operator import add
99

10-
from autoray import do, dag
10+
from autoray import dag, do
1111

12+
from ..gen.rand import randn, seed_rand
1213
from ..utils import check_opt, ensure_dict, pairwise
1314
from ..utils import progbar as Progbar
14-
from ..gen.rand import randn, seed_rand
1515
from . import array_ops as ops
16+
from .tensor_arbgeom import (
17+
TensorNetworkGen,
18+
TensorNetworkGenVector,
19+
)
1620
from .tensor_core import (
21+
Tensor,
1722
bonds,
1823
bonds_size,
1924
oset,
2025
rand_uuid,
2126
tags_to_oset,
22-
Tensor,
23-
)
24-
from .tensor_arbgeom import (
25-
TensorNetworkGen,
26-
TensorNetworkGenVector,
2727
)
2828

2929

@@ -1070,6 +1070,11 @@ def _contract_boundary_core(
10701070
compress_opts=None,
10711071
canonize_opts=None,
10721072
):
1073+
"""Core function for the 'peps' mode boundary contraction, which
1074+
eagerly contracts two layers of tensors, then compresses all the bonds,
1075+
possibly interleaving canonization between e.g. each direction of
1076+
compression sweep.
1077+
"""
10731078
canonize_opts = ensure_dict(canonize_opts)
10741079
canonize_opts.setdefault("absorb", "right")
10751080
compress_opts = ensure_dict(compress_opts)
@@ -1182,12 +1187,14 @@ def _contract_boundary_projector(
11821187

11831188
r = Rotator3D(self, xrange, yrange, zrange, from_which)
11841189

1185-
if canonize:
1186-
canonize_opts = ensure_dict(canonize_opts)
1187-
canonize_opts.setdefault("max_iterations", 2)
1188-
self.select(r.x_tag(r.sweep[0])).gauge_all_(**canonize_opts)
1189-
11901190
for i, inext in pairwise(r.sweep):
1191+
if canonize:
1192+
# do some simple gauging of the layers we will be contracting
1193+
canonize_opts = ensure_dict(canonize_opts)
1194+
canonize_opts.setdefault("max_iterations", 2)
1195+
tn_x_layers = self.select_any([r.x_tag(i), r.x_tag(inext)])
1196+
tn_x_layers.gauge_all_(**canonize_opts)
1197+
11911198
# we compute the projectors from an untouched copy
11921199
tn_calc = self.copy()
11931200

@@ -1293,7 +1300,6 @@ def contract_boundary_from(
12931300
mode="peps",
12941301
equalize_norms=False,
12951302
compress_opts=None,
1296-
canonize_opts=None,
12971303
inplace=False,
12981304
**contract_boundary_opts,
12991305
):
@@ -1313,18 +1319,13 @@ def contract_boundary_from(
13131319
contract_boundary_opts["compress_opts"] = compress_opts
13141320

13151321
if mode == "peps":
1316-
return tn._contract_boundary_core(
1317-
canonize_opts=canonize_opts,
1318-
**contract_boundary_opts,
1319-
)
1322+
return tn._contract_boundary_core(**contract_boundary_opts)
13201323

13211324
if mode == "l2bp3d":
13221325
return tn._contract_boundary_l2bp(**contract_boundary_opts)
13231326

13241327
if mode == "projector3d":
1325-
return tn._contract_boundary_projector(
1326-
canonize_opts=canonize_opts, **contract_boundary_opts
1327-
)
1328+
return tn._contract_boundary_projector(**contract_boundary_opts)
13281329

13291330
return tn._contract_boundary_core_via_2d(
13301331
method=mode, **contract_boundary_opts
@@ -1349,8 +1350,6 @@ def _contract_interleaved_boundary_sequence(
13491350
max_unfinished=1,
13501351
around=None,
13511352
equalize_norms=False,
1352-
canonize=False,
1353-
canonize_opts=None,
13541353
final_contract=True,
13551354
final_contract_opts=None,
13561355
optimize="auto-hq",
@@ -1363,9 +1362,6 @@ def _contract_interleaved_boundary_sequence(
13631362
tn = self if inplace else self.copy()
13641363

13651364
contract_boundary_opts = ensure_dict(contract_boundary_opts)
1366-
if canonize:
1367-
canonize_opts = ensure_dict(canonize_opts)
1368-
canonize_opts.setdefault("max_iterations", 2)
13691365

13701366
if progbar:
13711367
pbar = Progbar()
@@ -1395,14 +1391,6 @@ def _contract_interleaved_boundary_sequence(
13951391
separations = {
13961392
d: boundaries[f"{d}max"] - boundaries[f"{d}min"] for d in "xyz"
13971393
}
1398-
boundary_tags = {
1399-
"xmin": tn.x_tag(boundaries["xmin"]),
1400-
"xmax": tn.x_tag(boundaries["xmax"]),
1401-
"ymin": tn.y_tag(boundaries["ymin"]),
1402-
"ymax": tn.y_tag(boundaries["ymax"]),
1403-
"zmin": tn.z_tag(boundaries["zmin"]),
1404-
"zmax": tn.z_tag(boundaries["zmax"]),
1405-
}
14061394
if around is not None:
14071395
target_xmin = min(x[0] for x in around)
14081396
target_xmax = max(x[0] for x in around)
@@ -1469,9 +1457,6 @@ def _is_finished(direction):
14691457
f"Lz={separations['z'] + 1}"
14701458
)
14711459

1472-
if canonize:
1473-
tn.select(boundary_tags[direction]).gauge_all_(**canonize_opts)
1474-
14751460
if direction[0] == "x":
14761461
if direction[1:] == "min":
14771462
xrange = (boundaries["xmin"], boundaries["xmin"] + 1)
@@ -1568,20 +1553,87 @@ def contract_boundary(
15681553
inplace=False,
15691554
**contract_boundary_opts,
15701555
):
1571-
""" """
1556+
"""Contract this 3D tensor network by sweeping a sequence of the 2D
1557+
boundaries inwards.
1558+
1559+
Parameters
1560+
----------
1561+
max_bond : int
1562+
The maximum bond dimension to allow during the contraction. You
1563+
can set this to `None` to use cutoff based compression only but
1564+
this is not recommended for loopy compressions.
1565+
cutoff : float, optional
1566+
The cutoff to use when compressing the bonds.
1567+
mode : {"peps", "projector3d", "l2bp3d", "projector", "l2bp",
1568+
"local-early", "local-late", "su", ...}, optional
1569+
The mode to use for the contraction:
1570+
1571+
- "peps" : eagerly contract two layers of tensors, then compress
1572+
all bonds, possibly interleaving canonization between. This is a
1573+
mode specific to 3D.
1574+
- "projector3d" : use CTMRG/HOTRG boundary projectors inserted
1575+
between layers to compress lazily. This mode is specific to 3D.
1576+
- "l2bp3d" : use lazy 2-norm belief propagation compression to
1577+
insert projectors between layers. This mode is specific to 3D.
1578+
- "projector", "l2bp", "local-early", "local-late", "su" :
1579+
use any 'arbitrary' geometry compression mode (see
1580+
`quimb.tensor.tensor_arbgeom_compress`) to compress the boundary.
1581+
1582+
canonize : bool, optional
1583+
Whether to perform canonization/gauging while compressing. What
1584+
this means depends on the mode.
1585+
compress_opts : dict, optional
1586+
Low level arguments to pass to the individual compression routine.
1587+
sequence : tuple[str, ...], optional
1588+
The sequence of boundaries to contract inwards. By default this
1589+
is chosen automatically as all three directions, but you can
1590+
specify a custom sequence with elements from {'xmin', 'xmax',
1591+
'ymin', 'ymax', 'zmin', 'zmax'}, which will be cycled through.
1592+
xmin, xmax, ymin, ymax, zmin, zmax : int, optional
1593+
The initial boundaries to start contracting from. By default
1594+
these are chosen as the outermost boundaries which contain
1595+
tensors.
1596+
max_separation : int, optional
1597+
The maximum separation to contract the boundaries to. By default
1598+
this is 1, meaning that the contraction stops when two opposing
1599+
boundaries are adjacent.
1600+
max_unfinished : int, optional
1601+
The maximum number of directions which are allowed to be not
1602+
finished (i.e. not reached max_separation) before stopping.
1603+
around : ((int, int, int), ...), optional
1604+
A set of coordinates to contract around. The contraction will
1605+
stop once all these coordinates are within the current boundaries.
1606+
equalize_norms : bool, optional
1607+
Whether to try and keep the norms of all tensors roughly equal
1608+
during the contraction, accruing any overall factors, log10, into
1609+
the attribute `tn.exponent`. If `equalize_norms` is not a specific
1610+
float, this factor is redistributed at the end of the contraction.
1611+
final_contract : bool, optional
1612+
Whether to perform the final, exact contraction of the remaining
1613+
tensors once the boundary contraction is finished. If ``around``
1614+
is specified, this is automatically set to ``False``.
1615+
final_contract_opts : dict, optional
1616+
Extra or custom options to pass to the final contraction step.
1617+
optimize : str or list, optional
1618+
The contraction path optimizer to use for the final contraction
1619+
step, if performed. By default uses "auto-hq".
1620+
progbar : bool, optional
1621+
Whether to show a progress bar of the boundary contraction.
1622+
inplace : bool, optional
1623+
Whether to perform the contraction in place or on a copy.
1624+
"""
15721625
contract_boundary_opts["max_bond"] = max_bond
15731626
contract_boundary_opts["cutoff"] = cutoff
15741627
contract_boundary_opts["mode"] = mode
15751628
contract_boundary_opts["canonize"] = canonize
15761629
contract_boundary_opts["compress_opts"] = compress_opts
15771630

1578-
if (
1579-
(mode == "peps") and
1580-
(self.is_cyclic_x() or self.is_cyclic_y() or self.is_cyclic_z())
1631+
if (mode == "peps") and (
1632+
self.is_cyclic_x() or self.is_cyclic_y() or self.is_cyclic_z()
15811633
):
15821634
raise NotImplementedError(
15831635
"Cannot yet use _contract_boundary_core "
1584-
"(i.e. `mode=\"peps\"`) on cyclic networks."
1636+
'(i.e. `mode="peps"`) on cyclic networks.'
15851637
)
15861638

15871639
return self._contract_interleaved_boundary_sequence(
@@ -1613,6 +1665,7 @@ def contract_peps_sweep(
16131665
max_bond,
16141666
cutoff=0.0,
16151667
from_which=None,
1668+
mode="peps",
16161669
canonize=True,
16171670
canonize_interleave=True,
16181671
peps_opts=None,
@@ -1626,6 +1679,31 @@ def contract_peps_sweep(
16261679
canonicalize. This is one natural generalization of contracting a PEPS
16271680
by sweeping a MPS across it. It is perhaps not a very accurate
16281681
contraction method and is provided as a reference only.
1682+
1683+
Parameters
1684+
----------
1685+
max_bond : int
1686+
The maximum bond dimension to allow during the contraction. By
1687+
default the MPS bond dimension is set to twice this value.
1688+
cutoff : float, optional
1689+
The cutoff to use when compressing the MPS.
1690+
from_which : {'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax'}, optional
1691+
Which direction to contract the PEPS from. By default the
1692+
direction which requires the smallest area PEPS is chosen.
1693+
mode : {"peps", "projector", ...}, optional
1694+
The boundary contraction method to use. See ``contract_boundary``.
1695+
canonize : bool, optional
1696+
Whether to perform canonization/gauging during the PEPS and MPS
1697+
compression steps. By default applies to both. What this means
1698+
depends on the mode.
1699+
canonize_interleave : bool, optional
1700+
If ``mode=='peps'``, whether to interleave canonization and
1701+
compression sweeps, or do all canonization then all compression.
1702+
peps_opts : dict, optional
1703+
Extra or custom options to pass to the PEPS compression steps.
1704+
mps_opts : dict, optional
1705+
Extra or custom options to pass to the MPS compression steps.
1706+
16291707
"""
16301708
from quimb.tensor.tensor_2d import TensorNetwork2D
16311709

@@ -1639,49 +1717,56 @@ def contract_peps_sweep(
16391717
}
16401718
from_which = min(plane_sizes, key=plane_sizes.get)
16411719
else:
1642-
check_opt(from_which, ("xmin", "ymin", "zmin"))
1720+
check_opt(
1721+
"from_which",
1722+
from_which,
1723+
("xmin", "xmax", "ymin", "ymax", "zmin", "zmax"),
1724+
)
16431725

16441726
peps_opts = ensure_dict(peps_opts)
1727+
peps_opts.setdefault("max_bond", max_bond)
1728+
peps_opts.setdefault("cutoff", 0.0)
16451729
peps_opts.setdefault("canonize", canonize)
16461730
peps_opts.setdefault("equalize_norms", equalize_norms)
16471731

1648-
# this specifies that we canonize <- and compress -> in one direction
1649-
# then move on to the second direction (^ v), rather than doing both
1650-
# in a single step
1651-
peps_opts.setdefault("canonize_interleave", canonize_interleave)
1652-
peps_opts.setdefault("compress_opts", {})
1653-
peps_opts["compress_opts"].setdefault("absorb", "right")
1654-
peps_opts.setdefault("canonize_opts", {})
1655-
peps_opts["canonize_opts"].setdefault("absorb", "right")
1732+
if mode == "peps":
1733+
# this specifies that we canonize <- and compress -> in one
1734+
# direction then move on to the second direction (^ v), rather than
1735+
# doing both in a single step
1736+
peps_opts.setdefault("canonize_interleave", canonize_interleave)
1737+
peps_opts.setdefault("compress_opts", {})
1738+
peps_opts["compress_opts"].setdefault("absorb", "right")
1739+
peps_opts.setdefault("canonize_opts", {})
1740+
peps_opts["canonize_opts"].setdefault("absorb", "right")
16561741

16571742
tn.contract_boundary_from_(
1743+
mode=mode,
16581744
xrange=None,
16591745
yrange=None,
16601746
zrange=None,
16611747
from_which=from_which,
1662-
max_bond=max_bond,
1663-
cutoff=0.0,
16641748
**peps_opts,
16651749
)
16661750

16671751
site_tag_id = {
1668-
"xmin": "I0,{},{}",
1669-
"ymin": "I{},0,{}",
1670-
"zmin": "I{},{},0",
1671-
}[from_which]
1752+
"x": "I0,{},{}",
1753+
"y": "I{},0,{}",
1754+
"z": "I{},{},0",
1755+
}[from_which[0]]
16721756

16731757
tn.view_as_(
16741758
TensorNetwork2D, Lx=tn.Lx, Ly=tn.Ly, site_tag_id=site_tag_id
16751759
)
16761760

16771761
mps_opts = ensure_dict(mps_opts)
16781762
mps_opts.setdefault("max_bond", 2 * max_bond)
1763+
mps_opts.setdefault("cutoff", cutoff)
16791764
mps_opts.setdefault("sequence", ["b"])
16801765
mps_opts.setdefault("canonize", canonize)
16811766
mps_opts.setdefault("equalize_norms", equalize_norms)
16821767
mps_opts.setdefault("inplace", inplace)
16831768

1684-
return tn.contract_boundary(cutoff=cutoff, **mps_opts)
1769+
return tn.contract_boundary(**mps_opts)
16851770

16861771
def contract_simple_sweep(
16871772
self,
@@ -1762,6 +1847,8 @@ def contract_ctmrg(
17621847
contract_boundary_opts["mode"] = mode
17631848
contract_boundary_opts["compress_opts"] = compress_opts
17641849
contract_boundary_opts["lazy"] = lazy
1850+
contract_boundary_opts["canonize"] = canonize
1851+
contract_boundary_opts["canonize_opts"] = canonize_opts
17651852

17661853
if lazy:
17671854
# we are implicitly asking for the tensor network
@@ -1786,8 +1873,6 @@ def contract_ctmrg(
17861873

17871874
return self._contract_interleaved_boundary_sequence(
17881875
contract_boundary_opts=contract_boundary_opts,
1789-
canonize=canonize,
1790-
canonize_opts=canonize_opts,
17911876
sequence=sequence,
17921877
xmin=xmin,
17931878
xmax=xmax,
@@ -2206,7 +2291,7 @@ def _is_finished(direction):
22062291

22072292
if pbar is not None:
22082293
pbar.set_description(
2209-
f"contracted HOTRG, " f"Lx={tn.Lx}, Ly={tn.Ly}, Lz={tn.Lz}"
2294+
f"contracted HOTRG, Lx={tn.Lx}, Ly={tn.Ly}, Lz={tn.Lz}"
22102295
)
22112296
pbar.close()
22122297

0 commit comments

Comments
 (0)