ITensorMPOConstruction.jl
Alternative backends for constructing MPOs from sums of operators.
Install / Use
/learn @ITensor/ITensorMPOConstruction.jlREADME
ITensorMPOConstruction
A fast algorithm for constructing a Matrix Product Operator (MPO) from a sum of local operators. This is a replacement for ITensorMPS.MPO(os::OpSum, sites::Vector{<:Index}). If julia is started with multiple threads, they are used to transparently speed up the construction.
The three goals of this library are
- Produce exact MPOs (up to floating point error) of the smallest possible bond dimension.
- Maximize the block sparsity of the resulting MPOs.
- Accomplish these goals as fast as possible.
ITensorMPOConstruction is not designed to construct approximate compressed MPOs. If this is your workflow, use ITensorMPOConstruction to construct the exact MPO and call ITensorMPS.truncate!.
All runtimes below are taken from a single sample on a 2021 MacBook Pro with the M1 Max CPU and 32GB of memory.
Installation
The package is currently not registered. Please install with the commands:
julia> using Pkg; Pkg.add(url="https://github.com/ITensor/ITensorMPOConstruction.jl.git")
Citing
If you use this library in your research, please cite the following article https://doi.org/10.1103/nzrt-l2j1
Constraints
This algorithm shares the same constraints as the default algorithm from ITensorMPS.
- The operator must be expressed as a sum of products of single site operators. For example a CNOT could not appear in the sum since it is a two site operator.
- When dealing with Fermionic systems the parity of each term in the sum must be even. That is the combined number of creation and annihilation operators in each term in the sum must be even. It should be possible to relax this constraint.
There is also one additional constraint:
- Each term in the sum of products representation can only have a single operator acting on a site. For example a term such as $\mathbf{X}^{(1)} \mathbf{X}^{(1)}$ is not allowed. However, there is a pre-processing utility that can automatically replace $\mathbf{X}^{(1)} \mathbf{X}^{(1)}$ with $\mathbf{I}^{(1)}$. This is not a hard requirement for the algorithm but a simplification to improve performance.
MPO_new
The main exported function is MPO_new which takes an OpSum and transforms it into a MPO.
function MPO_new(os::OpSum, sites::Vector{<:Index}; kwargs...)::MPO
The optional keyword arguments are
basis_op_cache_vec=nothing: A list of operators to use as a basis for each site. The operators on each site are expressed as one of these basis operators.check_for_errors::Bool: Check the input OpSum for errors, this can be expensive for larger problems.tol::Real=1: A multiplicative modifier to the default tolerance used in the SPQR, see SPQR user guide Section 2.3. The value of the default tolerance depends on the input matrix, which means a different tolerance is used for each decomposition. In the cases we have examined, the default tolerance works great for producing accurate MPOs.absolute_tol::Bool=false: Override the default adaptive tolerance scheme outlined above, and use the value oftolas the single tolerance for each decomposition.combine_qn_sectors::Bool=false: Whentrue, the blocks of the MPO corresponding to the same quantum numbers are merged together into a single block. This can decrease the resulting sparsity.call_back::Function=(cur_site::Int, H::MPO, sites::Vector{<:Index}, llinks::Vector{<:Index},cur_graph::MPOGraph, op_cache_vec::OpCacheVec) -> nothing: A function that is called after constructing the MPO tensor atcur_site. Primarily used for writing checkpoints to disk for massive calculations.output_level::Int=0: Specify the amount of output printed to standard out, larger values produce more output.
Examples: Fermi-Hubbard Hamiltonian in Real Space
The one dimensional Fermi-Hubbard Hamiltonian with periodic boundary conditions on $N$ sites can be expressed in real space as
$$ \mathcal{H} = -t \sum_{i = 1}^N \sum_{\sigma \in (\uparrow, \downarrow)} \left( c^\dagger_{i, \sigma} c_{i + 1, \sigma} + c^\dagger_{i, \sigma} c_{i - 1, \sigma} \right) + U \sum_{i = 1}^N n_{i, \uparrow} n_{i, \downarrow} \ , $$
where the periodic boundary conditions enforce that $c_k = c_{k + N}$. For this Hamiltonian all that needs to be done to switch over to using ITensorMPOConstruction is switch MPO(os, sites) to MPO_New(os, sites).
https://github.com/ITensor/ITensorMPOConstruction.jl/blob/637dd2409f27ede41aa916822ea8acb4cd557a9e/examples/fermi-hubbard.jl#L4-L24
For $N = 1000$ both ITensorMPS and ITensorMPOConstruction can construct an MPO of bond dimension 10 in under two seconds. When quantum number conservation is enabled, ITensorMPS produces an MPO that is 93.4% block sparse, whereas ITensorMPOConstruction's MPO is 97.4% block sparse.
Examples: Fermi-Hubbard Hamiltonian in Momentum Space
The one dimensional Fermi-Hubbard Hamiltonian with periodic boundary conditions on $N$ sites can be expressed in momentum space as
$$ \mathcal{H} = \sum_{k = 1}^N \epsilon(k) \left( n_{k, \downarrow} + n_{k, \uparrow} \right) + \frac{U}{N} \sum_{p, q, k = 1}^N c^\dagger_{p - k, \uparrow} c^\dagger_{q + k, \downarrow} c_{q, \downarrow} c_{p, \uparrow} $$
where $\epsilon(k) = -2 t \cos(\frac{2 \pi k}{N})$ and $c_k = c_{k + N}$. The code to construct the OpSum is shown below.
https://github.com/ITensor/ITensorMPOConstruction.jl/blob/637dd2409f27ede41aa916822ea8acb4cd557a9e/examples/fermi-hubbard.jl#L26-L49
Unlike the previous example, some more involved changes will be required to use ITensorMPOConstruction. This is because the OpSum has multiple operators acting on the same site, violating constraint #3. For example when $k = 0$ in the second loop we have terms of the form $c^\dagger_{p, \uparrow} c^\dagger_{q, \downarrow} c_{q, \downarrow} c_{p, \uparrow}$. You could always create a special case for $k = 0$ and rewrite it as $n_{p, \uparrow} n_{q, \downarrow}$. However if using "Electron" sites then you would also need to consider other cases such as when $p = q$, this would introduce a lot of extraneous complication. Luckily ITensorMPOConstruction provides a method to automatically perform these transformations. If you provide a set of operators for each site to MPO_new it will attempt to express the operators acting on each site as a single one of these "basis" operators. The code to do this is shown below.
https://github.com/ITensor/ITensorMPOConstruction.jl/blob/637dd2409f27ede41aa916822ea8acb4cd557a9e/examples/fermi-hubbard.jl#L51-L76
With $N = 20$ and quantum number conservation turned on, ITensorMPS produces an MPO of bond dimension 196 which is 92.6% sparse in 30s, whereas ITensorMPOConstruction produces an MPO of equal bond dimension but which is 99.7% sparse in 0.1s.
OpIDSum
For $N = 200$ constructing the OpSum takes 42s and constructing the MPO from the OpSum with ITensorMPOConstruction takes another 306s. For some systems constructing the OpSum can actually be the bottleneck. In these cases you can construct an OpIDSum instead.
OpIDSum plays the same roll as OpSum but in a much more efficient manner. To specify an operator in a term of an OpSum you specify a string (the operator's name) and a site index, whereas to specify an operator in a term of an OpIDSum you specify an OpID which contains an operator index and a site. The operator index is the index of the operator in the provided basis for the site.
For $N = 200$ constructing an OpIDSum takes only 0.4s. Shown below is code to construct the Hamiltonian using an OpIDSum.
https://github.com/ITensor/ITensorMPOConstruction.jl/blob/637dd2409f27ede41aa916822ea8acb4cd557a9e/examples/fermi-hubbard.jl#L79-L130
Unlike OpSum, the OpIDSum constructor takes a few important arguments
function OpIDSum{N, C, Ti}(
max_terms::Int,
op_cache_vec::OpCacheVec;
abs_tol::Real=0
)::OpIDSum{N, C, Ti} where {N, C, Ti}
N: The maximum number of local operators appearing in any individual term. For the real space Fermi-Hubbard HamiltonianN = 2, but in momentum spaceN = 4.C: The scalar weight type.Ti: The integer type used to index both the local operator basis and the number of sites. For example withTi = UInt8, the local operator basis can have up to 255 elements and 255 sites can be used. Much of the memory consumption comes storing elements of typeTi.max_terms: The maximum number of terms in theOpIDSum, space is pre-allocated.op_cache_vec: Maps eachOpIDand site to a physical operator.abs_tol: Drop terms that have a weight of absolute value less than this.
An OpIDSum is constructed similarly to an OpSum, with support for the following two threadsafe functions for adding a term.
function ITensorMPS.add!(os::OpIDSum, scalar::Number, ops)::Nothing
function ITensorMPS.add!(os::OpIDSum, scalar::Number, ops::OpID...)::Nothing
Additionally, a further constructor is provided which takes in a modifying function modify!(ops)::C which is called as each term is added to the sum. It accepts a list of the sorted OpID, wh
