bsc-quantic / Tenet.jl

Composable Tensor Network library in Julia
https://bsc-quantic.github.io/Tenet.jl/
Apache License 2.0
17 stars 1 forks source link

Implement `SplitSimplification` transformation for `TensorNetwork` #70

Closed jofrevalles closed 1 year ago

jofrevalles commented 1 year ago

Summary

This PR introduces the SplitSimplification transformation as part of the transform! function for TensorNetworks, addressing issue #18 (resolve #18). The transformation applies the split simplification procedure, as outlined in this paper, to reduce the complexity of the tensor network while ensuring the rank of any tensor does not increase. The main strategy involves performing a singular value decomposition across all possible bipartitions of a tensor's indices, and replacing the tensor with a lower-rank approximation when this results in a simplification of the TensorNetwork.

Additionally, this PR includes comprehensive tests that verify the correctness and robustness of the newly implemented transformation.

Example

julia> using Tenet

julia> v1 = Tensor([1, 2, 3], (:i,))
3-element Tensor{Int64, 1, Vector{Int64}}:
...

julia> v2 = Tensor([4, 5, 6], (:j, ))
3-element Tensor{Int64, 1, Vector{Int64}}:
...

julia> m1 = Tensor(rand(3, 3), (:k, :l))
3×3 Tensor{Float64, 2, Matrix{Float64}}:
...

julia> t1 = contract(v1, v2)
3×3 Tensor{Int64, 2, Matrix{Int64}}:
...

julia> tensor = contract(t1, m1) # Define a tensor which can be splitted in three
3×3×3×3 Tensor{Float64, 4, Array{Float64, 4}}:
...

julia> tn = TensorNetwork([tensor, Tensor(rand(3, 3, 3), (:k, :m, :n)), Tensor(rand(3, 3, 3), (:l, :n, :o))])
TensorNetwork{Arbitrary, NamedTuple{(), Tuple{}}}(#tensors=3, #labels=7)

julia> reduced = transform(tn, SplitSimplification)
TensorNetwork{Arbitrary, NamedTuple{(), Tuple{}}}(#tensors=5, #labels=9)

julia> size.(tensors(tn))
3-element Vector{Tuple{Int64, Int64, Int64, Vararg{Int64}}}:
 (3, 3, 3, 3)
 (3, 3, 3)
 (3, 3, 3)

julia> size.(tensors(reduced))
5-element Vector{Tuple{Int64, Int64, Vararg{Int64}}}:
 (3, 3, 3)
 (3, 3, 3)
 (3, 1)
 (3, 1)
 (1, 1, 3, 3)

In this example, the SplitSimplification transformation is applied to a tensor network that includes a rank-4 tensor that can be simplified using svd into two vectors and a matrix. The result is a network containing five tensors, each of lower rank and dimensions.

codecov[bot] commented 1 year ago

Codecov Report

Merging #70 (274fc08) into master (2b37db3) will decrease coverage by 0.66%. The diff coverage is 95.83%.

@@            Coverage Diff             @@
##           master      #70      +/-   ##
==========================================
- Coverage   88.50%   87.84%   -0.66%     
==========================================
  Files           9        9              
  Lines         600      625      +25     
==========================================
+ Hits          531      549      +18     
- Misses         69       76       +7     
Impacted Files Coverage Δ
src/Transformations.jl 97.46% <95.83%> (-0.30%) :arrow_down:

... and 4 files with indirect coverage changes

mofeing commented 1 year ago

Super!

julia> size.(tensors(reduced))
5-element Vector{Tuple{Int64, Int64, Vararg{Int64}}}:
 (3, 3, 3)
 (3, 3, 3)
 (3, 1)
 (3, 1)
 (1, 1, 3, 3)

I think it's best to directly remove the indices whose resulting dimension is 1. What do you think @jofrevalles?

jofrevalles commented 1 year ago

I think it's best to directly remove the indices whose resulting dimension is 1. What do you think @jofrevalles?

Maybe we could extend dropdims for Tensors, right? This would be easier.

mofeing commented 1 year ago

I think it's best to directly remove the indices whose resulting dimension is 1. What do you think @jofrevalles?

Maybe we could extend dropdims for Tensors, right? This would be easier.

Yeah, that seems correct to me.

mofeing commented 1 year ago

@jofrevalles I've implemented dropdims for Tensor and released in the new Tensors v0.1.9 release. Update the compat section to get the dropdims method.

jofrevalles commented 1 year ago

@mofeing In the quimb library, they choose whether to apply split-simplification to a tensor based on the condition if max(prod(size(u)), prod(size(v))) < prod(size(tensor)). Here, u and v are the tensors obtained from the SVD decomposition of a given tensor. This condition essentially checks if the total number of elements in the larger of the two new tensors (resulting from the split) is less than the number of elements in the original tensor, aiming for a reduction in total tensor elements.

In our current implementation, we've been using a slightly different approach by examining the rank of the singular values. Our idea is to perform the split if there are some singular values that are zero. I believe this approach is equivalent to quimb's, but it saves us the computational expense of having to truncate the tensors after SVD.

Please let me know your thoughts on this.

mofeing commented 1 year ago

Both conditions should be equivalent mathematically, so our implementation should be correct.