22
33import functools
44import itertools
5- from operator import add
6- from numbers import Integral
75from 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
1213from ..utils import check_opt , ensure_dict , pairwise
1314from ..utils import progbar as Progbar
14- from ..gen .rand import randn , seed_rand
1515from . import array_ops as ops
16+ from .tensor_arbgeom import (
17+ TensorNetworkGen ,
18+ TensorNetworkGenVector ,
19+ )
1620from .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