Background
Ultracold atomic experiments can squeeze nonrelativistic fermionic atoms in one dimension, constructing a two-dimensional Fermi gas. The leading-order effective field theory [1]
yields a Hamiltonian
invariant under scale and nonrelativistic-conformal transformations [2]. We can rescale our Hamiltonian by \(ML^2\), which removes the dimensions. With a lattice regulation, the result is
where \(\tilde{\psi} = \psi/\Delta x\) so that \(\{\tilde{\psi}_{a\sigma}, \tilde{\psi}^\dagger_{b\tau}\} = \delta_{ab}\delta_{\sigma\tau}\), \(\kappa\) is a lattice dispersion relation we are free to pick so long as it goes to the correct continuum limit, and \(\tilde{V}\) is a lattice-regularized interaction written as sums of terms, each a LegoSphere interaction.
The purpose of the tdg library is to compute observables of many-body systems in thermodynamic equilibrium governed by this Hamiltonian. We pursue a lattice field theory-like approach, discretizing the two-dimensional space via a regular square lattice with periodic boundary conditions.
tdg.Lattice
A lattice is a two-dimensional square grid of points, a discretization of space.
For simplicity we restrict our attention to an isotropic lattice with the same number of points in each rectilinear direction; a list of coordinates() is provided.
We use periodic boundary conditions, and therefore can validly mod() points into the coordinates() of the lattice.
The periodic boundary conditions also inform the computation of distance_squared(), because the distance might be shorter than the naive route might suggest.
A lattice knows how big a spatial vector() is and knows how to Fourier transform them via fft() and ifft().
We will use the perfect nonrelativistic dispersion relation, and therefore can use the adjacency_matrix() to construct the kinetic matrix kappa().
The lattice has translational invariance and a \(D_4\) point group.
- class tdg.lattice.Lattice(nx, ny=None)[source]
Bases:
H5able- dims
The dimension sizes in order.
>>> lattice = Lattice(5) >>> lattice.dims tensor([5, 5])
- sites
The total number of sites.
>>> lattice = Lattice(5) >>> lattice.sites 25
- x
The coordinates in the x direction.
>>> lattice = Lattice(5) >>> lattice.x tensor([ 0, 1, 2, -2, -1])
- y
The coordinates in the y direction.
>>> lattice = Lattice(5) >>> lattice.y tensor([ 0, 1, 2, -2, -1])
- X
A tensor of size
dimswith the x coordinate as a value.>>> lattice = Lattice(5) >>> lattice.X tensor([[ 0, 0, 0, 0, 0], [ 1, 1, 1, 1, 1], [ 2, 2, 2, 2, 2], [-2, -2, -2, -2, -2], [-1, -1, -1, -1, -1]])
- Y
A tensor of size
dimswith the y coordinate as a value.>>> lattice = Lattice(5) >>> lattice.Y tensor([[ 0, 1, 2, -2, -1], [ 0, 1, 2, -2, -1], [ 0, 1, 2, -2, -1], [ 0, 1, 2, -2, -1], [ 0, 1, 2, -2, -1]])
- coordinates
A tensor of size
[sites, len(dims)]. Each row contains a pair of coordinates. The order matches{X,Y}.flatten().>>> lattice = Lattice(5) >>> lattice.coordinates >>> lattice.coordinates tensor([[ 0, 0], [ 0, 1], [ 0, 2], [ 0, -2], [ 0, -1], [ 1, 0], [ 1, 1], [ 1, 2], [ 1, -2], [ 1, -1], [ 2, 0], [ 2, 1], [ 2, 2], [ 2, -2], [ 2, -1], [-2, 0], [-2, 1], [-2, 2], [-2, -2], [-2, -1], [-1, 0], [-1, 1], [-1, 2], [-1, -2], [-1, -1]])
- mod(x)[source]
Mod integer coordinates x into values on the lattice.
- Parameters
x (torch.tensor) – Either one coordinate pair of .shape==torch.Size([2]) or a set of pairs .shape==torch.Size([*,2]) The last dimension should be of size 2.
- Returns
Each x is identified with an entry of
coordinatesby periodic boundary conditions. The output is the same shape as the input.- Return type
torch.tensor
- distance_squared(a, b)[source]
- \[\texttt{distance_squared}(a,b) = \left| \texttt{mod}(a - b)\right|^2\]
- Parameters
a (torch.tensor) – coordinates that need not be on the lattice
b (torch.tensor) – coordinates that need not be on the lattice
- Returns
The distance between
aandbon the lattice accounting for the fact that, because of periodic boundary conditions, the distance may shorter than naively expected. Eitheraandbare the same shape (a single or 1D-tensor of coordinate pairs) or one is a singlet and one is a tensor.- Return type
torch.tensor
- cross(a, b)[source]
The cross product of two vectors \(a \times b\). In two dimensions this is a scalar value. However, for compatibility with three dimensions we add an extra index.
- Parameters
a (torch.tensor:) – first vector
b (torch.tensor:) – second vector
- Return type
torch.tensor
If
aandbare single vectors, returns a single value in a one-dimensional torch.tensor.>>> lattice = Lattice(5) >>> x = lattice.coordinates >>> lattice.cross(x[1], x[5]) tensor([-1])
If
ais a single vector andbis two-dimensional (vector index last), returns a tensor of shape[b.shape[0], 1],>>> lattice.cross(x[1], x[1:-1:5]) tensor([[ 0], [-1], [-2], [ 2], [ 1]])
Similarly, if
ais two-dimensional (vector index last), returns a tensor of shape[a.shape[0], 1],>>> lattice.cross(x[1:-1:5], x[2]) tensor([[ 0], [ 2], [ 4], [-4], [-2]])
Finally, if both
aandbare two-dimensional (vector index last), returns a tensor of shape[a.shape[0], b.shape[0], 1],>>> lattice.cross(x[:5], x[:7]).shape torch.Size([5, 7, 1])
- coordinatize(v, dims=(-1,), center_origin=False)[source]
Unflattens all the dims from a linear superindex to one index for each dimension in
.dims.- Parameters
v (torch.tensor) – A tensor with at least one dimension linearized in space.
dims (tuple of integers) – The directions you wish to unflatten into a meaningful shape that matches the lattice.
center_origin (boolean) – If true, each coordinatized dimension is rolled so that the origin is in the center of the two slices. This is primarily good for making pictures.
linearize()does not provide an inverse of this, because you really should not do it in the middle of a calculation!
- Returns
vbut tensor more, shorter dimensions. Dimensions specified bydimsare unflattened.- Return type
torch.tensor
- linearize(v, dims=(-1,))[source]
Flattens adjacent dimensions of v with shape
.dimsinto a dimension of size.sites.- Parameters
v (torch.tensor) –
dims (tuples of integers that specify that dimensions in the result that come from flattening.) – Modded by the dimension of the resulting tensor so that any dimension is legal. However, one should take care to ensure that no two are the SAME index of the result; this causes a RuntimeError.
- Returns
vbut with fewer, larger dimensions- Return type
torch.tensor
Note
The
dimsparameter may be a bit confusing. This perhaps-peculiar convention is to make it easier to combine withcoordinatize.linearizeandcoordinatizeare inverses when they get the same dims arguments.>>> import torch >>> import tdg >>> nx = 5 >>> dims = (0, -1) >>> lattice = tdg.Lattice(5) >>> v = torch.arange(nx**(2*3)).reshape(nx**2, nx**2, nx**2) >>> u = lattice.coordinatize(v, dims) >>> u.shape torch.Size([5, 5, 25, 5, 5]) >>> w = lattice.linearize(u, dims) # dims indexes into the dimensions of w, not u! >>> w.shape torch.Size([25, 25, 25]) >>> (v == w).all() tensor(True)
- vector(*dims)[source]
- Parameters
dims (tuple) – Specifies how how many vectors to produce.
- Returns
A
dims-dimensional stack of linearized zero vectors.- Return type
torch.tensor
- fft(vector, axis=-1, norm='backward')[source]
The Fourier transform on a linearized axis.
- Parameters
vector (torch.tensor) – A vector of data.
axis – The axis along which to perform a 2D Fourier transform on the vector.
norm – A convention for the Fourier transform, one of
"forward","backward", or"ortho". The default is “backward”, to match our notes.
- Returns
F(vector) with the same shape as the input vector, transformed along the axis.
- Return type
torch.tensor
- ifft(vector, axis=-1, norm='backward')[source]
The Fourier inverse transform on a linearized axis.
- Parameters
vector (torch.tensor) – A vector of data.
axis – The axis along which to perform a 2D inverse Fourier transform on the vector.
norm – A convention for the inverse Fourier transform, one of
"forward","backward", or"ortho". The default is “backward”, to match our notes.
- Returns
Inverse[F](vector) with the same shape as the input vector, transformed along the axis.
- Return type
torch.tensor
- inversion(vector, dims=(-1,))[source]
Reflect data across the origin for each dimension.
- Parameters
vector (torch.tensor) – A vector of data
dims (iterable) – Linearized dimensions apply the inversion to.
- Returns
Data with the same shape, but with the data at a given index is now at the index that corresponds to the reflected coordinate. For example,
>>> import torch >>> torch.set_default_dtype(torch.float64) >>> import tdg >>> nx = 5 >>> L = tdg.Lattice(5) >>> (L.inversion(L.coordinates, dims=(0,)) + L.coordinates).abs().sum() < 1e-14 tensor(True)
- Return type
torch.tensor
- property adjacency_tensor
The adjacency_matrix but the two superindices are transformed into coordinate indices.
- property adjacency_matrix
A matrix which is 1 if the corresponding
coordinatesare nearest neighbors (accounting for periodic boundary conditions) and 0 otherwise.
- property kappa
The kinetic \(\kappa\) with a perfect dispersion relation, as a
[sites, sites]matrix.
- property convolver
The convolution of two vectors \(u\) and \(v\) is \(u * v = \frac{1}{V} \sum_a u_a v_{a-r}\).
The convolution can be computed quickly using fast fourier transforms. However, sometimes it is useful to implement the convolution as part of a tensor contraction.
We can introduce this via the convolver, which satisfies
\[\frac{1}{V} \sum_a u_a v_{a-r} = \sum_{ab} u_a\, v_b\, \texttt{convolver}_{bra} \text{ (note the order)}\]which can be implemented via einsum,
u * v = torch.einsum('a,b,bra->r', u, v, convolver) # order as above
where
uandvhave one linearized spatial index.Note
This includes the factor of volume!
- plot_2d_scalar(ax, data, center_origin=True, **kwargs)[source]
Use matshow to plot the (linear) data as a function of space, using the lattice to coordinatize, centering the origin by default.
- Parameters
ax (matplotlib axis) – Where to draw the data.
data (torch.tensor) – A single linearized spatial vector.
center_origin (True or False) – If true the origin is centered in the figure.
**kwargs (matshow arguments) – Simply forwarded. origin is fixed to be
'lower'
#!/usr/bin/env python import matplotlib.pyplot as plt import tdg fig, ax = plt.subplots(1,2) L = tdg.Lattice(5) L.plot_2d_scalar(ax[0], L.coordinates[:,0]) L.plot_2d_scalar(ax[1], L.coordinates[:,1], center_origin=False) ax[0].set_xlabel('x') ax[0].set_ylabel('y') ax[1].set_xlabel('x') fig.tight_layout() plt.show()
(
Source code,png,hires.png,pdf)
tdg.LegoSphere
On a square lattice, ‘rotationally symmetric’ has a limited meaning, because the lattice breaks SO(2) in both the UV (via the discretization) and in the IR (via the boundary conditions). However, we still reliably have the smaller \(D_4\) symmetry, and the trivial ireducible representation of \(D_4\) is called \(A_1\). For more details on this symmetry, and exactly why these LegoSpheres are \(A_1\), see D_4 Symmmetry.
- class tdg.LegoSphere.LegoSphere(r, c=1)[source]
Bases:
H5ableTranslationally-invariant potentials in the \(A_1\) representations of the lattice \(D_4\) symmetry can be written as a sum of LegoSpheres,
\[\begin{align} \tilde{V}_{a,a+r} &= \tilde{V}_{0,r} & \tilde{V}_{0,r} &= \mathcal{V} \sum_R \tilde{C}_R \mathcal{S}^R_{0,r} \end{align}\]where each LegoSphere \(\mathcal{S}\) has a radius \(R\).
To be in the \(A_1\) representation each LegoSphere is a uniformly-weighted stencil with no phases,
\[\begin{align} \mathcal{S}^R_{0,r} &= \frac{1}{\mathcal{N}_R^2} \sum_{g\in D_4} \delta_{r,gR} & \mathcal{S}^R_{ab} &= \frac{1}{\mathcal{N}_R^2} \sum_{g\in D_4} \delta_{b-a,gR} \end{align}\]where \(g\) acts to rotate spatial displacements on the lattice.
- Parameters
r (list or tuple) – the radius given as a vector
[x, y]. The magnitude is insufficient information, because two radii with the same magnitude might yield different LegoSpheres (consider[3, 4]and[0, 5], for example).c (float or a torch.tensor-wrapped number) – the Wilson coefficient \(\tilde{C}_R\).
LegoSpheres may be multiplied by coefficients on either side to give new LegoSpheres with c changed appropriately,
>>> sphere = LegoSphere([0,1], 1.23) >>> stronger = 2*sphere >>> stronger.c tensor(2.4600)
- spatial(Lattice)[source]
- Parameters
lattice (tdg.Lattice) – a spatial lattice on which to construct \(\tilde{C}_R \mathcal{S}^R_{ab}\)
- Returns
a square matrix of dimension
[lattice.sites, lattice.sites]where the first index is the superindex \(a\) and the second index is the superindex \(b\).- Return type
torch.tensor
tdg.potential
- class tdg.potential.Potential(*spheres)[source]
Bases:
H5ableA potential encodes a term in the many-body Hamiltonian like \(nVn\), where \(n\) are number operators and \(V\) can connect different sites.
A potential is built up from one or more LegoSpheres \(\mathcal{S}^{\vec{R}}\), each which carries its own Wilson coefficient \(C_\vec{R}\),
\[\tilde{V} = N_x^2 \sum_R C_R \mathcal{S}^R\]- spatial(lattice)[source]
- Parameters
lattice (tdg.Lattice) –
- Returns
A matrix encoding \(V\) on the given lattice. The two axes of the matrix are in the order of the lattice
coordinates.- Return type
torch.tensor
- inverse(lattice)[source]
- Parameters
lattice (tdg.Lattice) –
- Returns
The inverse matrix of
spatial(lattice).- Return type
torch.tensor
- eigvals(lattice)[source]
- Parameters
lattice (tdg.Lattice) –
- Returns
An array of eigenvalues of
spatial(lattice). If any of the eigenvalues are imaginary, raises a TypeError; the potential should be Hermitian. Raises a ValueError if any of the eigenvalues are positive; we require the attractive channel.- Return type
torch.tensor
- property C0
The Wilson coefficient of the \(\mathcal{S}^0\) piece of the potential.