Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define an ITensorNetwork struct #12

Open
mtfishman opened this issue Jul 16, 2021 · 1 comment
Open

Define an ITensorNetwork struct #12

mtfishman opened this issue Jul 16, 2021 · 1 comment

Comments

@mtfishman
Copy link
Member

mtfishman commented Jul 16, 2021

Define an ITensorNetwork struct that stores a collection of ITensors that make up a network as well as metadata about the connectivity of the network (such as an adjacency list or lattice iterator). For example, a PEPS class might be defined as an alias of an ITensorNetwork with a Square lattice:

struct ITensorNetwork{N,Lattice}
  tensors::Array{ITensor,N}
  lattice::Lattice
end

const MPS = ITensorNetwork{CartesianIndex{2},Chain}
const PEPS = ITensorNetwork{CartesianIndex{2},Square}

# This would be a generic, unstructured network (not fully connected, but not with any regular pattern)
const TN = ITensorNetwork{CartesianIndex{2},Unstructured}

# And other lattice definitions like `Tree`, `FullyConnected`, etc.

This could have a generic method for operations like priming the links of a network, which makes use of the lattice object to make it faster to determine the neighbors of a tensor in order to determine which indices to prime.

An example of how this would work in practice is that right now there are two "neighbors" functions which determine the neighbors of a site/node in an ITensor network. One is based on the specialized definition for a HyperCubic lattice:

# The neighboring sites of the specified site
function neighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}) where {N}
lattice_size = size(lattice)
site_neighbors = Vector{NTuple{N,Int}}()
for dim in 1:N, dir in (-1, 1)
site_neighbor = neighbor(site, dim, dir; lattice_size=lattice_size)
push!(site_neighbors, neighbor)
end
return site_neighbors
end
and one is based on naively searching for which tensors in the network have shared indices with the specified tensor:
function filterneighbors(f, tn, n)
neighbors_tn = keytype(tn)[]
tnₙ = tn[n]
for m in keys(tn)
if f(n, m) && hascommoninds(tnₙ, tn[m])
push!(neighbors_tn, m)
end
end
return neighbors_tn
end
neighbors(tn, n) = filterneighbors(, tn, n)
inneighbors(tn, n) = filterneighbors(>, tn, n)
outneighbors(tn, n) = filterneighbors(<, tn, n)

Then, something like prime(linkinds, tn) would use one or the other neighbors function depending on the type of lattice.

@mtfishman
Copy link
Member Author

An alternative design is to have a type hierarchy like:

abstract type AbstractITensorNetwork end

struct HyperCubicITensorNetwork{N} <: AbstractITensorNetwork
  tensors::Array{ITensor,N}
end

# The lattice is determined by overloading the `lattice` function
lattice(::HyperCubicITensorNetwork{N}) where {N} = HyperCubic{N}()

const MPS = HyperCubicITensorNetwork{1}
const PEPS = HyperCubicITensorNetwork{2}

struct UnstructuredITensorNetwork <: AbstractITensorNetwork
  tensors::Array{ITensor,N}
end

const TN = UnstructuredITensorNetwork

lattice(::UnstructuredITensorNetwork) = Unstructured()

I'm not sure which design would be better. The second design based on a type hierarchy and function overloading is a bit more flexible because different lattice types could store different types of information about the network connectivity. For example, in practice the UnstructuredITensorNetwork could store an adjacency list that caches the actual network structure based on shared indices, which it could do once when it is constructed and then update if ITensors in the network are modified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant