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.
The package is currently not registered. Please install with the commands:
julia> using Pkg; Pkg.add(url="https://github.com/ITensor/ITensorMPOConstruction.jl.git")If you use this library in your research, please cite the following preprint: https://doi.org/10.48550/arXiv.2506.07441
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 are also two additional constraints:
- 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.
- When constructing a quantum number conserving operator the total flux of the operator must be zero. It would be easy to remove this constraint.
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...)::MPOThe optional keyword arguments are
- basis_op_cache_vec: 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 of- tolas the single tolerance for each decomposition.
- combine_qn_sectors::Bool=false: When- true, 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 at- cur_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.
The one dimensional Fermi-Hubbard Hamiltonian with periodic boundary conditions on 
where the periodic boundary conditions enforce that MPO(os, sites) to MPO_New(os, sites).
ITensorMPOConstruction.jl/examples/fermi-hubbard.jl
Lines 4 to 24 in 637dd24
For 
The one dimensional Fermi-Hubbard Hamiltonian with periodic boundary conditions on 
where OpSum is shown below.
ITensorMPOConstruction.jl/examples/fermi-hubbard.jl
Lines 26 to 49 in 637dd24
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 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.
ITensorMPOConstruction.jl/examples/fermi-hubbard.jl
Lines 51 to 76 in 637dd24
With 
For 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 OpIDSum takes only 0.4s. Shown below is code to construct the Hamiltonian using an OpIDSum.
ITensorMPOConstruction.jl/examples/fermi-hubbard.jl
Lines 79 to 130 in 637dd24
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 Hamiltonian- N = 2, but in momentum space- N = 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 with- Ti = 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 type- Ti.
- max_terms: The maximum number of terms in the- OpIDSum, space is pre-allocated.
- op_cache_vec: Maps each- OpIDand 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...)::NothingAdditionally, 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, which it can modify, and returns a scalar which multiplies the weight. This is for advanced usage only, but in certain cases can greatly speed up and or simplify construction.
The Haldane-Shasty Hamiltonian defined on 
With 
Using the MPO from ITensorMPOConstruction we obtain an error of 
ITensorMPOConstruction is designed to construct exact MPOs (up to numerical precision), nevertheless, we can abuse it to perform approximate MPO construction. By setting tol = 2E10 we obtain an MPO of bond dimension 38, equal to that produced by TensorMPS. However, using this approximate MPO we obtain poor results, with errors of absolute_tol = true to use a uniform cutoff across QR decompositions does not help either.
Starting with the MPO from ITensorMPOConstruction obtained with the standard tol = 1 and then truncating down to a bond dimension of 38 using ITensorMPS.truncate yields DMRG errors of 
We constructed the momentum space Fermi-Hubbard Hamiltonian using ITensorMPS and ITensorMPOConstruction. For even 
| ITensorMPS | ITensorMPOConstruction | |
|---|---|---|
| 10 | 96 | 96 | 
| 20 | 196 | 196 | 
| 30 | 296 | 296 | 
| 40 | N/A | 396 | 
| 50 | N/A | 496 | 
| 100 | N/A | 996 | 
| 200 | N/A | 1996 | 
| 300 | N/A | 2996 | 
| 400 | N/A | 3996 | 
| 500 | N/A | 4996 | 
Sparsity of the ITensorMPS MPO with the default splitblocks=true, and the ITensorMPOConstruction MPO with the less aggressive combine_qn_sectors::Bool=false.
| ITensorMPS | ITensorMPOConstruction | |
|---|---|---|
| 10 | 92.7% | 99.32% | 
| 20 | 92.6% | 99.70% | 
| 30 | 92.6% | 99.81% | 
| 40 | N/A | 99.86% | 
| 50 | N/A | 99.89% | 
| 100 | N/A | 99.94% | 
| 200 | N/A | 99.97% | 
| 300 | N/A | 99.982% | 
| 400 | N/A | 99.986% | 
| 500 | N/A | 99.999% | 
| ITensorMPS | ITensorMPOConstruction | |
|---|---|---|
| 10 | 0.32s | 0.009s | 
| 20 | 30.6s | 0.052s | 
| 30 | 792s | 0.14s | 
| 40 | N/A | 0.38s | 
| 50 | N/A | 0.63s | 
| 100 | N/A | 7.7s | 
| 200 | N/A | 103s | 
| 300 | N/A | 500s | 
| 400 | N/A | 1554s | 
| 500 | N/A | 3802s | 
Thanks to Huanchen Zhai for providing the discussion and data motivating this section.
The core component of pretty many MPO construction algorithms is to take an operator 
and turn it into a two site MPO
The MPO bond dimension is 
In the case of operators with a global ITensorMPS algorithm is to use the quantum numbers associated with the operators 
In ITensorMPOConstruction ITensorMPOConstruction will produce a matrix ITensors' sparse format is more flexible, and most of the time each diagonal block in the 
To illustrate the suboptimal sparsity, we turn to Block2 which has a sophisticated set of MPO construction algorithms. Specifically we will use the FastBipartite algorithm, based on the bipartite graph algorithm from RenLi2020. The bipartite algorithm is very efficient and also produces MPO tensors of exceptional sparsity. The drawback is that it is unable to compress the MPO bond dimension. For example, for the momentum space Fermi-Hubbard Hamiltonian the bond dimension it produces is ITensorMPS and here) produce similar MPO bond dimensions. In these cases, the bipartite algorithm will likely produce MPOs of greater sparsity.
In the table below we present data from constructing two different electronic structure Hamiltonians, the second of which is from ZhaiLee2023. Our rank decomposition algorithm only slightly reduces the bond dimension compared to the bipartite MPO from Block2, but it results in a much denser MPO. This increase in sparsity has a significant impact on the subsequent DMRG performance, which is larger by 75% for our rank decomposition MPO (timings and sparsities taken from Block2's by transferring over the MPO from ITensorMPOConstruction).
| system | algorithm | bond dimension | sparsity | 
|---|---|---|---|
| C 2 | rank | 704 | 97.21% | 
| C 2 | bipartite | 722 | 98.68% | 
| [Fe 2 S(C H 3) (S C H 3)4]3- | rank | 2698 | 88.70% | 
| [Fe 2 S(C H 3) (S C H 3)4]3- | bipartite | 2738 | 95.64% | 
To further complicate matters Block2 has a different, less flexible, sparse storage format from ITensors. Specifically, they store MPO tensors in a "matrix of operators" format, where the onsite operator is always dense. Essentially this format stores a sparse representation of block2_nnz(mpo::MPO)::Tuple{Int, Int} which returns the total number of blocks (the size of