Details on the lattice geometry

The Lattice class defines the geometry of the system. In the basic form, it represents a unit cell of a few sites repeated in one or multiple directions. Moreover, it maps this higher-dimensional geometry to a one-dimensional chain for MPS-based algorithms.

Visualization

A plot of the lattice can greatly help to understand which sites are connected by what couplings. The methods plot_* of the Lattice can do a good job for a quick illustration. Let’s look at the Honeycomb lattice as an example.

import matplotlib.pyplot as plt
from tenpy.models import lattice

plt.figure(figsize=(5, 6))
ax = plt.gca()
lat = lattice.Honeycomb(Lx=4, Ly=4, sites=None, bc='periodic')
lat.plot_coupling(ax)
lat.plot_order(ax, linestyle=':')
lat.plot_sites(ax)
lat.plot_basis(ax, origin=-0.5*(lat.basis[0] + lat.basis[1]))
ax.set_aspect('equal')
ax.set_xlim(-1)
ax.set_ylim(-1)
plt.show()

(Source code, png, hires.png, pdf)

../_images/lattices-1.png

In this case, the unit cell (shaded green) consists of two sites, which for the purpose of plotting we just set to sites=None; in general you should specify instances of Site for that. The unit cell gets repeated in the directions given by the lattice basis (green arrows at the unit cell boundary). Hence, we can label each site by a lattice index (x, y, u) in this case, where x in range(Lx), y in range(Ly) specify the translation of the unit cell and u in range(len(unit_cell)), here u in [0, 1], specifies the index within the unit cell.

How an MPS winds through the lattice: the order

For MPS-based algorithms, we need to map a 2D lattice like the one above to a 1D chain. The red, dashed line in the plot indicates how an MPS winds through the 2D lattice. The MPS index i is a simple enumeration of the sites along this line, shown as numbers next to the sites in the plot. The methods mps2lat_idx() and lat2mps_idx() map indices of the MPS to and from indices of the lattice.

The MPS class itself is (mostly) agnostic of the underlying geometry. For example, expectation_value() will return a 1D array of the expectation value on each site indexed by the MPS index i. If you have a two-dimensional lattice, you can use mps2lat_values() to map this result to a 2D array index by the lattice indices.

A suitable order is critical for the efficiency of MPS-based algorithms. On one hand, different orderings can lead to different MPO bond-dimensions, with direct impact on the complexity scaling. On the other hand, it influences how much entanglement needs to go through each bonds of the underlying MPS, e.g., the ground strate to be found in DMRG, and therefore influences the required MPS bond dimensions. For the latter reason, the “optimal” ordering can not be known a priori and might even depend on your coupling parameters (and the phase you are in). In the end, you can just try different orderings and see which one works best.

The simplets way to change the order is to use a non-default value for the initialization parameter order of the Lattice class. This gets passed on to ordering(), which you an override in a custom lattice class to define new possible orderings. Alternatively, you can go the most general way and simply set the attribute order to be a 2D numpy array with lattice indices as rows, in the order you want.

import matplotlib.pyplot as plt
from tenpy.models import lattice

Lx, Ly = 3, 3
fig, axes = plt.subplots(2, 2, figsize=(7, 8))

lat1 = lattice.Honeycomb(Lx, Ly, sites=None, bc='periodic')  # default order
lat2 = lattice.Honeycomb(Lx, Ly, sites=None, bc='periodic',
                        order="Cstyle")  # first method to change order
# alternative: directly set "Cstyle" order
lat3 = lattice.Honeycomb(Lx, Ly, sites=None, bc='periodic')
lat3.order = lat2.ordering("Cstyle")  # now equivalent to lat2

# general: can apply arbitrary permutation to the order
lat4 = lattice.Honeycomb(Lx, Ly, sites=None, bc='periodic',
                        order="Cstyle")
old_order = lat4.order
permutation = []
for i in range(0, len(old_order), 2):
    permutation.append(i+1)
    permutation.append(i)
lat4.order = old_order[permutation, :]

for lat, label, ax in zip([lat1, lat2, lat3, lat4],
                          ["order='default'",
                           "order='Cstyle'",
                           "order='Cstyle'",
                           "custom permutation"],
                          axes.flatten()):
    lat.plot_coupling(ax)
    lat.plot_sites(ax)
    lat.plot_order(ax, linestyle=':', linewidth=2.)
    ax.set_aspect('equal')
    ax.set_title('order = ' + repr(label))

plt.show()

(Source code, png, hires.png, pdf)

../_images/lattices-2.png

Boundary conditions

The Lattice defines the boundary conditions bc in each direction. It can be one of the usual 'open' or 'periodic' in each direcetion and will be used by the CouplingModel to determine whether there should be added periodic couplings in the corresponding directions.

On top of that, there is the bc_MPS boundary condition of the MPS, one of 'finite', 'segment', 'infinite'. For an 'infinite' MPS, the whole lattice is repeated in the direction of the first basis vector of the lattice. For bc_MPS='infinite', the first direction should always be 'periodic', but you can also define a lattice with bc_MPS='finite', bc=['periodic', 'perioid'] for a finite system on the torus. This is discouraged, though, because the ground state MPS will require the squared bond dimension for the same precision in this case!

For two (or higher) dimensional lattices, e.g for DMRG on an infinite cylinder, you can also specify an integer shift instead of just saying 'periodic': Rolling the 2D lattice up into a cylinder, you have a degree of freedom which sites to connect. This is illustrated in the figure below for a Square lattice with bc=['periodic', shift] for shift in [-1, 0, 1] (different columns). In the first row, the orange markers indicate a pair of identified sites (see plot_bc_shift()). The dashed orange line indicates the direction of the cylinder axis. The line where the cylinder is “cut open” therefore winds around the the cylinder for a non-zero shift. (A similar thing happens even for shift=0 for more complicated lattices with non-orthogonal basis.) In the second row, we directly draw lines between all sites connected by nearest-neighbor couplings, as they appear in the MPO.

(Source code, png, hires.png, pdf)

../_images/lattices-3.png

Irregular Lattices

The IrregularLattice allows to add or remove sites from/to an existing regular lattice. The doc-string of IrregularLattice contains several examples, let us consider another one here, where we use the IrregularLattice to “fix” the boundary of the Honeycomb lattice: when we use "open" boundary conditions for a finite system, there are two sites (on the lower left, and upper right), wich are not included into any hexagonal. The following example shows how to remove them from the system:

import matplotlib.pyplot as plt
from tenpy.models import lattice

Lx, Ly = 3, 3
fig, axes = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(6, 4))

reg_lat = lattice.Honeycomb(Lx=Lx, Ly=Ly, sites=None, bc='open')
irr_lat = lattice.IrregularLattice(reg_lat, remove=[[0, 0, 0], [-1, -1, 1]])
for lat, label, ax in zip([reg_lat, irr_lat],
                          ["regular", "irregular"],
                          axes.flatten()):
    lat.plot_coupling(ax)
    lat.plot_order(ax, linestyle=':')
    lat.plot_sites(ax)
    ax.set_aspect('equal')
    ax.set_title(label)

plt.show()

(Source code, png, hires.png, pdf)

../_images/lattices-4.png