QXTns
QXTns is a Julia package with data structures and utilities for manipulating tensor networks. As well as generic tensor network data structure, it also contains specific data structures for handling tensor networks derived from quantum circuits. It was developed as part of the QuantEx project, one of the individual software projects of WP8 of PRACE 6IP.
It uses some features from ITensors and NDTensors for representing tensors and indices and performing contractions.
Installation
QXTns is a Julia package and can be installed using Julia's inbuilt package manager from the Julia REPL using.
import Pkg
Pkg.add("QXTns")
To ensure everything is working, the unittests can be run using
import Pkg; Pkg.test()
Example usage
An example of creating a simple tensor network and contracting.
using QXTns
tn = TensorNetwork()
a, b, c, d = Index(2), Index(3), Index(5), Index(4)
# add a 2x3x5 rank tensor
push!(tn, [a, b, c], rand(2, 3, 5))
# add a 5x4 matrix
push!(tn, [c, d], rand(5, 4))
# contract network
simple_contraction!(tn)
# number of tensors after contraction
@show length(tn)
# resulting tensor has dimensions should have dimensions 2x3x4
@show size(first(tn))
Contributing
Contributions from users are welcome and we encourage users to open issues and submit merge/pull requests for any problems or feature requests they have. The CONTRIBUTING.md on the top level of the source folder has further details of the contribution guidelines.
Building documentation
QXTns.jl uses Documenter.jl to generate documentation. To build the documentation locally run the following from the root folder.
The first time it is will be necessary to instantiate the environment to install dependencies
julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
and then to build the documentation
julia --project=docs/ docs/make.jl
The generated document will be in the docs/build
folder. To serve these locally one can use the LiveServer package as
julia --project -e 'import Pkg; Pkg.add("LiveServer");
julia --project -e 'using LiveServer; serve(dir="docs/build")'
Or with python3 using from the docs/build
folder using
python3 -m http.server
The generated documentation should now be viewable locally in a browser at http://localhost:8000
.
API Reference
QXTns.MockTensor
QXTns.QXTensor
QXTns.QXTensor
QXTns.QXTensor
QXTns.TensorNetwork
QXTns.TensorNetwork
QXTns.TensorNetworkCircuit
Base.copy
Base.copy
Base.delete!
Base.delete!
Base.getindex
Base.merge
Base.push!
Base.push!
Base.push!
Base.show
Base.size
NDTensors.inds
NDTensors.store
QXTns.add_input!
QXTns.add_output!
QXTns.contract_pair
QXTns.contract_pair!
QXTns.contract_tensors
QXTns.contract_tn!
QXTns.contraction_indices
QXTns.contraction_indices
QXTns.create_test_tnc
QXTns.decompose_gate
QXTns.decompose_tensor!
QXTns.disable_hyperindices!
QXTns.expand_tensor
QXTns.find_connected_indices
QXTns.find_hyper_edges
QXTns.get_hyperedges
QXTns.hyperindices
QXTns.hyperindices
QXTns.indices2ranks
QXTns.isdiagonal
QXTns.isdiagonal
QXTns.neighbours
QXTns.push_input!
QXTns.push_output!
QXTns.reduce_tensor
QXTns.replace_tensor_symbol!
QXTns.simple_contraction
QXTns.simple_contraction!
QXTns.tensor_data
QXTns.tensor_data
QXTns.MockTensor
— TypeTensor store struct that just tracks tensor dimensions
QXTns.QXTensor
— TypeQXTensor(indices::Vector{<:Index},
hyper_indices::Union{Nothing, Vector{<:Vector{Int64}}},
storage::Union{Nothing, <: AbstractArray}=nothing;
diagonal_check::Bool=true)
QXTensor constructor creates a new instance of QXTensor with the given indices and hyper indices. If no storage data structure is given then a MockTensor of that shape is added as the storage. If diagonalcheck is true, it will automaticallly check which indices are hyper indices and record in the hyperindices field. If hyper_indices are given, then these are used.
QXTns.QXTensor
— MethodQXTensor(a::T) where T <: Number
QXTensor constructor which creates a new instance of QXTensor corresponding to a scalar
QXTns.TensorNetwork
— MethodTensorNetwork(array::Vector{<: QXTensor})
Outer constructor to create a tensor network object from an array of ITensor objects.
Base.copy
— MethodOverload functions from base to make MockTensor usable
Base.copy
— MethodOverload functions from base to make BlockTensor usable
Base.delete!
— Methoddelete!(tn::TensorNetwork, tensor_id::Symbol)
Function to remove a tensor from a tensor network.
Base.delete!
— Methoddelete!(tnc::TensorNetworkCircuit, tensor_id::Symbol)
Function to remove a tensor from a tensor network circuit.
Base.getindex
— MethodBase.getindex(t::BlockTensor{T, N, M}, i...) where {T, N, M}
Overload the getindex function. Returns zero if the indiex on ranks identified as hyperindex groups differ and the relevant tensor index otherwise.
Base.merge
— Methodmerge(a::TensorNetwork, b::TensorNetwork)
Join two networks together
Base.push!
— Methodpush!(tn::TensorNetwork,
tensor::QXTensor;
tid::Union{Nothing, Symbol}=nothing)
Function to add a tensor to the tensor network.
Keywords
tid::Union{Nothing, Symbol}=nothing
: the id for the new tensor intn
. An id is
generated if one is not set.
Base.push!
— Methodpush!(tn::TensorNetwork,
indices::Vector{Index},
data::Array{T, N}
tid::Union{Nothing, Symbol}=nothing) where {T, N}
Function to add a tensor to the tensor network.
Keywords
tid::Union{Nothing, Symbol}=nothing
: the id for the new tensor intn
. An id is
generated if one is not set.
Base.push!
— Methodpush!(tnc::TensorNetworkCircuit,
qubits::Vector{Int64},
data::Array{T, 2}) where T
Function to add a gate to the tensor network circuit given the qubits it acts on and an array of the matrix elements
Base.show
— MethodCustom show for QXTensors
Base.size
— MethodCustom size function
NDTensors.inds
— MethodImplement inds for QXTensor
NDTensors.store
— MethodImplement store for QXTensor
QXTns.add_input!
— Functionadd_input!(tnc::TensorNetworkCircuit; input::Union{String, Nothing}=nothing)
Function to add input tensors to the circuit
QXTns.add_output!
— Functionadd_output!(tnc::TensorNetworkCircuit; output::Union{String, Nothing}=nothing)
Function to add output tensors to the circuit
QXTns.contract_pair!
— Functioncontract_pair!(tn::TensorNetwork, a_sym::Symbol, b_sym::Symbol, c_sym::Symbol=:_; mock::Bool=false)
Contract the tensors in 'tn' with ids 'asym' and 'bsym'. If the mock flag is true then the new tensor will be a mock tensor with the right dimensions but without the actual data.
The resulting tensor is stored in tn
under the symbol c_sym
if one is provided, otherwise a new id is created for it.
QXTns.contract_pair
— Methodcontract_pair(tn::TensorNetwork, a_sym::Symbol, b_sym::Symbol; mock::Bool=false)
Contract the tensors in 'tn' with ids 'asym' and 'bsym'. If the mock flag is true then the new tensor will be a mock tensor with the right dimensions but without the actual data.
QXTns.contract_tensors
— Methodcontract_tensors(a::QXTensor, b::QXTensor; mock::Bool=false)
Function to contract two QXTensors and return another QXTensor. If the mock flag is false or either of the input tensors use MockTensor then the storage for the final tensor will be of type MockTensor.
QXTns.contract_tn!
— Methodcontract_tn!(tn::TensorNetwork, plan)
Contract the indices of 'tn' according to 'plan'.
QXTns.contraction_indices
— Methodcontraction_indices((a::QXTensor,b::QXTensor)
Function to work out the contraction indices that would be used to contract the given tensors. Exptected indices in Einstein notation using positive integers
QXTns.contraction_indices
— Methodcontraction_indices(tn::TensorNetwork, a_sym::Symbol, b_sym::Symbol)
Function to work out the contraction indices that would be used to contract the given tensors. Expected indices in Einstein notation using positive integers
QXTns.create_test_tnc
— Methodcreate_test_tnc(;input::Union{String, Nothing}=nothing,
output::Union{String, Nothing}=nothing,
no_input::Bool=false,
no_output::Bool=false,
kwargs...)
Create a tensor network circuit for a small example circuit, 3 qubit ghz preparation circuit in this case
QXTns.decompose_gate
— Functionfunction decompose_gate!(gate_data::Array{<:Number, 4},
threshold::AbstractFloat=1e-15)
Function to decompose a tensor into two smaller tensors
QXTns.decompose_tensor!
— Methoddecompose_tensor!(tn::TensorNetwork,
tensor_id::Symbol,
left_indices::Array{<:Index, 1};
contract_S_with::Symbol=:V,
kwargs...)
Function to decompose a tensor in a tensor network using svd.
Keywords
contract_S_with::Symbol=:V
: the maxtrix which should absorb the matrix of singular valuesmaxdim::Int
: the maximum number of singular values to keep.mindim::Int
: the minimum number of singular values to keep.cutoff::Float64
: set the desired truncation error of the SVD.
QXTns.disable_hyperindices!
— Methoddisable_hyperindices!(t::QXTensor)
Function to disable use of hyper indices with this tensor by removing the hyper indices and reshaping storage
QXTns.expand_tensor
— Methodexpand_tensor(A::AbstractArray{Elt, N}, hyper_index_groups::Array{Int64, 1})
Function to expand the rank of the given tensor assuming the given hyper edge groups. Like a generalisation of Diagonal. For example if passed a vector and given hyperindex groups [1,2], it will return a matrix where non diagonal elements are zero. Returns a a BlockTensor object which does not store "off-diagonal" elements.
julia> expand_tensor([1, 2], [[1, 2]])
BlockTensor with dims (2, 2) and index map (1, 1)
QXTns.find_connected_indices
— Methodfind_connected_indices(tn::TensorNetwork, bond::Index)
Given a tensor network and an index in the network, find all indices that are related via hyper edge relations. Involves recurisively checking bonds connected to neighbouring tensors of any newly related edges found. Returns an array of all edges in the group including the initial edge.
QXTns.find_hyper_edges
— Methodfunction find_hyper_edges(A::AbstractArray{Elt, N}) where {Elt, N}
Function to identify hyper edges of tensors. Returns an array of tuples of indices of the original tensor which can be identified
QXTns.get_hyperedges
— Methodget_hyperedges(tn::TensorNetwork)::Array{Array{Symbol, 1}, 1}
Return an array of hyperedges in the given tensornetwork tn
.
Hyperedges are represented as arrays of tensor symbols.
QXTns.hyperindices
— Methodhyperindices(t::QXTensor)
Function to get the hyper indices as an array of Indices. If the all_indices flag is true, then all indices are returned, if false then just the groups of 2 or more are returned.
QXTns.hyperindices
— Methodhyperindices(tn::TensorNetwork, i::Symbol; global_hyperindices=true)
Find groups of hyper indices for the given tensor. When global_hyperindices is set to true, then indices which are identified as hyperindices because of groups of hyperindices in conneted tensors in the network are also included.
QXTns.indices2ranks
— Methodindices2ranks(tensor::QXTensor, hi::Vector{<:Vector{<:Index}})
Function convert groups of indices to groups of index positions (ranks).
QXTns.isdiagonal
— Methodisdiagonal(A::AbstractArray{Elt, 2}) where Elt
Function to check if the given matrix is diagonal
QXTns.isdiagonal
— Methodisdiagonal(A::AbstractArray{Elt, N}, Pair{Int64, Int64}) where {Elt, N}
Function to check if the given matrix is diagonal along given axes
QXTns.neighbours
— Methodneighbours(tn::TensorNetwork, tensor::Symbol)
Function get the symbols of the neighbouring tensors
QXTns.push_input!
— Methodpush_input!(tnc::TensorNetworkCircuit, tensor::Array{Elt, 1}, pos::Int64) where Elt
Function to add a single input tensor to the tensor network circuit at the given position
QXTns.push_output!
— Methodpush_output!(tnc::TensorNetworkCircuit, tensor::Array{Elt, 1}, pos::Int64) where Elt
Function to add a single output tensor to the tensor network circuit at the given position
QXTns.reduce_tensor
— Methodreduce_tensor(A::AbstractArray{Elt, N}, hyper_index_groups::Array{Int64, 1})
Function to reduce the rank of the given tensor assuming the given hyper edge groups. For example a diagonal matrix will have a single hyper edge group with both indices [1, 2]. This function will reduce this to a vector containing only the diagonal elements. can be seen as a generalisation of the diag function.
julia> reduce_tensor([[1, 0] [0, 2]], [[1, 2]])
2-element Vector{Int64}:
1
2
QXTns.replace_tensor_symbol!
— Methodreplace_tensor_symbol!(tn::TensorNetwork, orig_sym::Symbol, new_sym::Symbol)
Replace the given symbol with the given new symbol
QXTns.simple_contraction!
— Methodsimple_contraction!(tn::TensorNetwork)
Function to perfrom a simple contraction, contracting all tensors in order. Only useful for very small networks for testing.
QXTns.simple_contraction
— Methodsimple_contraction(tn::TensorNetwork)
Function to perfrom a simple contraction, contracting all tensors in order. Only useful for very small networks for testing.
QXTns.tensor_data
— Methodtensor_data(tensor::QXTensor; consider_hyperindices::Bool=true)
Get the data associated with the given tensor. If the considerhyperindices flag is true then the rank is reduced to merge related indices. For example for a 5 rank tensor where the 2nd and 4th indices form a group of hyper indices, with this option set to true would return a rank 4 tensor where the 2nd index has been merged with the 4th. With `considerhyperindices` set to false a rank 5 tensor is returned.
QXTns.tensor_data
— Methodtensor_data(tn::TensorNetwork, i::Symbol; consider_hyperindices=true, global_hyperindices=true)
Retrieve the tensor data for the given tensor. If the considerhyperindices flag is true then then the data is reshaped to take into account the local hyperindices of the tensor. If the globalhyperindices index is also true then groups of hyperindices related via hyperindices for other tensors in the network are also considered.