From 23c8fd818411a959a5ce452b668909b2c6361ff4 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 25 Oct 2022 16:35:19 -0500 Subject: [PATCH 01/24] Add approximation tn contraction subpackage --- Project.toml | 10 +- examples/peps/utils.jl | 12 +- .../ApproximateTNContraction.jl | 21 + src/ApproximateTNContraction/ITensors.jl | 49 + .../contract/contract.jl | 907 ++++++++++++++++++ .../contract/index_group.jl | 38 + .../contract/mincut_tree.jl | 249 +++++ .../contract/tensornetwork_graph.jl | 137 +++ .../contract/tree_embedding.jl | 119 +++ .../contract/tree_utils.jl | 104 ++ .../contract/union_find.jl | 28 + .../interfaces/sweep_contractor.jl | 33 + .../models/hamiltonians.jl | 144 +++ .../models/ising_classical_2d.jl | 95 ++ src/ApproximateTNContraction/models/models.jl | 19 + .../networks/3d_classical_ising.jl | 54 ++ .../networks/inds_network.jl | 68 ++ .../networks/itensor_network.jl | 303 ++++++ .../networks/lattices.jl | 125 +++ .../orthogonal_tensor.jl | 47 + src/ITensorNetworks.jl | 11 +- test/Manifest.toml | 556 ----------- test/Project.toml | 1 + test/approximate_tn_contraction/cache.jl | 17 + test/approximate_tn_contraction/indexgroup.jl | 78 ++ test/approximate_tn_contraction/interface.jl | 170 ++++ .../itensor_network.jl | 52 + test/approximate_tn_contraction/lattice.jl | 9 + test/approximate_tn_contraction/models.jl | 14 + test/approximate_tn_contraction/mpstensor.jl | 52 + test/approximate_tn_contraction/runtests.jl | 18 + test/approximate_tn_contraction/tree.jl | 26 + test/approximate_tn_contraction/treetensor.jl | 373 +++++++ test/approximate_tn_contraction/utils.jl | 34 + test/runtests.jl | 5 + 35 files changed, 3416 insertions(+), 562 deletions(-) create mode 100644 src/ApproximateTNContraction/ApproximateTNContraction.jl create mode 100644 src/ApproximateTNContraction/ITensors.jl create mode 100644 src/ApproximateTNContraction/contract/contract.jl create mode 100644 src/ApproximateTNContraction/contract/index_group.jl create mode 100644 src/ApproximateTNContraction/contract/mincut_tree.jl create mode 100644 src/ApproximateTNContraction/contract/tensornetwork_graph.jl create mode 100644 src/ApproximateTNContraction/contract/tree_embedding.jl create mode 100644 src/ApproximateTNContraction/contract/tree_utils.jl create mode 100644 src/ApproximateTNContraction/contract/union_find.jl create mode 100644 src/ApproximateTNContraction/interfaces/sweep_contractor.jl create mode 100644 src/ApproximateTNContraction/models/hamiltonians.jl create mode 100644 src/ApproximateTNContraction/models/ising_classical_2d.jl create mode 100644 src/ApproximateTNContraction/models/models.jl create mode 100644 src/ApproximateTNContraction/networks/3d_classical_ising.jl create mode 100644 src/ApproximateTNContraction/networks/inds_network.jl create mode 100644 src/ApproximateTNContraction/networks/itensor_network.jl create mode 100644 src/ApproximateTNContraction/networks/lattices.jl create mode 100644 src/ApproximateTNContraction/orthogonal_tensor.jl delete mode 100644 test/Manifest.toml create mode 100644 test/approximate_tn_contraction/cache.jl create mode 100644 test/approximate_tn_contraction/indexgroup.jl create mode 100644 test/approximate_tn_contraction/interface.jl create mode 100644 test/approximate_tn_contraction/itensor_network.jl create mode 100644 test/approximate_tn_contraction/lattice.jl create mode 100644 test/approximate_tn_contraction/models.jl create mode 100644 test/approximate_tn_contraction/mpstensor.jl create mode 100644 test/approximate_tn_contraction/runtests.jl create mode 100644 test/approximate_tn_contraction/tree.jl create mode 100644 test/approximate_tn_contraction/treetensor.jl create mode 100644 test/approximate_tn_contraction/utils.jl diff --git a/Project.toml b/Project.toml index b5d621da..a7577f7b 100644 --- a/Project.toml +++ b/Project.toml @@ -4,15 +4,23 @@ authors = ["Matthew Fishman and contributors"] version = "0.1.0" [deps] +Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DataGraphs = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" +GraphRecipes = "bd48cda9-67a9-57be-86fa-5b3c104eda73" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MultiDimDictionaries = "87ff4268-a46e-478f-b30a-76b83dd64e3c" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Requires = "ae029012-a4dd-5104-9daa-d747884805df" +SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +SweepContractor = "75a5deae-e917-4509-af32-a989148c8d5f" +TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" [compat] Dictionaries = "0.3.15" @@ -20,7 +28,7 @@ Graphs = "1.6.0" ITensors = "0.3" Requires = "1.3.0" Suppressor = "0.2.1" -julia = "1.6" +julia = "1.6, 1.8" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/examples/peps/utils.jl b/examples/peps/utils.jl index 099c4d24..74b5a7b7 100644 --- a/examples/peps/utils.jl +++ b/examples/peps/utils.jl @@ -10,7 +10,9 @@ function cartesian_to_linear(dims::Tuple) end NamedGraphs.NamedDimGraph(vertices::Vector) = NamedDimGraph(tuple.(vertices)) -NamedGraphs.NamedDimGraph(vertices::Vector{<:Tuple}) = NamedDimGraph(Graph(length(vertices)); vertices) +function NamedGraphs.NamedDimGraph(vertices::Vector{<:Tuple}) + return NamedDimGraph(Graph(length(vertices)); vertices) +end function rename_vertices(e::AbstractEdge, name_map::Dictionary) return typeof(e)(name_map[src(e)], name_map[dst(e)]) @@ -92,7 +94,9 @@ function ITensors.apply(o::ITensor, ψ::ITensorNetwork; cutoff, maxdim, normaliz error("Vertices where the gates are being applied must be neighbors for now.") end oψᵥ = apply(o, ψ[v⃗[1]] * ψ[v⃗[2]]) - ψᵥ₁, ψᵥ₂ = factorize(oψᵥ, inds(ψ[v⃗[1]]); cutoff, maxdim, tags=ITensorNetworks.edge_tag(e)) + ψᵥ₁, ψᵥ₂ = factorize( + oψᵥ, inds(ψ[v⃗[1]]); cutoff, maxdim, tags=ITensorNetworks.edge_tag(e) + ) if normalize ψᵥ₁ ./= norm(ψᵥ₁) ψᵥ₂ ./= norm(ψᵥ₂) @@ -107,7 +111,9 @@ function ITensors.apply(o::ITensor, ψ::ITensorNetwork; cutoff, maxdim, normaliz return ψ end -function ITensors.apply(o⃗::Vector{ITensor}, ψ::ITensorNetwork; cutoff, maxdim, normalize=false) +function ITensors.apply( + o⃗::Vector{ITensor}, ψ::ITensorNetwork; cutoff, maxdim, normalize=false +) o⃗ψ = ψ for oᵢ in o⃗ o⃗ψ = apply(oᵢ, o⃗ψ; cutoff, maxdim, normalize) diff --git a/src/ApproximateTNContraction/ApproximateTNContraction.jl b/src/ApproximateTNContraction/ApproximateTNContraction.jl new file mode 100644 index 00000000..e1492d5d --- /dev/null +++ b/src/ApproximateTNContraction/ApproximateTNContraction.jl @@ -0,0 +1,21 @@ +@reexport module ApproximateTNContraction + +using ITensors + +using ITensors: data, contract + +using TimerOutputs + +const timer = TimerOutput() + +include("ITensors.jl") +include("orthogonal_tensor.jl") +include("networks/lattices.jl") +include("networks/inds_network.jl") +include("networks/itensor_network.jl") +include("networks/3d_classical_ising.jl") +include("models/models.jl") +include("interfaces/sweep_contractor.jl") +include("contract/contract.jl") + +end diff --git a/src/ApproximateTNContraction/ITensors.jl b/src/ApproximateTNContraction/ITensors.jl new file mode 100644 index 00000000..bab000bd --- /dev/null +++ b/src/ApproximateTNContraction/ITensors.jl @@ -0,0 +1,49 @@ +# +# ITensors.jl extensions +# + +# Generalize siteind to n-dimensional lattice +function ITensors.siteind(st::SiteType, N1::Integer, N2::Integer, Ns::Integer...; kwargs...) + s = siteind(st; kwargs...) + if !isnothing(s) + ts = "n1=$N1,n2=$N2" + for i in eachindex(Ns) + ts *= ",n$(i + 2)=$(Ns[i])" + end + return addtags(s, ts) + end + return isnothing(s) && error(space_error_message(st)) +end + +# Generalize siteinds to n-dimensional lattice +function ITensors.siteinds( + str::AbstractString, N1::Integer, N2::Integer, Ns::Integer...; kwargs... +) + st = SiteType(str) + return [siteind(st, ns...) for ns in Base.product(1:N1, 1:N2, UnitRange.(1, Ns)...)] +end + +# Get the promoted type of the Index objects in a collection +# of Index (Tuple, Vector, ITensor, etc.) +indtype(i::Index) = typeof(i) +indtype(T::Type{<:Index}) = T +indtype(is::Tuple{Vararg{<:Index}}) = eltype(is) +indtype(is::Vector{<:Index}) = eltype(is) +indtype(A::ITensor...) = indtype(inds.(A)) + +indtype(tn1, tn2) = promote_type(indtype(tn1), indtype(tn2)) +indtype(tn) = mapreduce(indtype, promote_type, tn) + +# +# MPS functionality extensions +# + +Base.keytype(m::MPS) = keytype(data(m)) + +# A version of indexing which returns an empty order-0 ITensor +# when out of bounds +get_itensor(x::MPS, n::Int) = n in 1:length(x) ? x[n] : ITensor() + +# Reverse the site ordering of an MPS. +# XXX: also reverse the orthogonality limits. +Base.reverse(x::MPS) = MPS(reverse(x.data)) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl new file mode 100644 index 00000000..366cdbd6 --- /dev/null +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -0,0 +1,907 @@ +using Graphs, GraphsFlows, Combinatorics, SimpleWeightedGraphs +using GraphRecipes, Plots + +function Base.show(io::IO, tensor::ITensor) + return print(io, string(inds(tensor))) +end + +include("tree_utils.jl") +include("index_group.jl") +include("tensornetwork_graph.jl") +include("mincut_tree.jl") +include("tree_embedding.jl") + +ITensors.enable_contraction_sequence_optimization() + +function optcontract(t_list::Vector) + @timeit timer "optcontract" begin + # TODO: make this support orthotensor + if length(t_list) == 0 + return ITensor(1.0) + end + @assert t_list isa Vector{OrthogonalITensor} + t_list = get_tensors(t_list) + # for t in t_list + # @info "size of t is", size(t) + # end + # TODO: use optimized contraction path + output = contract(t_list) + return OrthogonalITensor(output) + end +end + +approximate_contract(tn::ITensor, inds_groups; kwargs...) = [tn] + +approximate_contract(tn::OrthogonalITensor, inds_groups; kwargs...) = [tn] + +function approximate_contract(tn::Vector{ITensor}, inds_btree=nothing; kwargs...) + out = approximate_contract(orthogonal_tensors(tn), inds_btree; kwargs...) + return get_tensors(out) +end + +function approximate_contract(tn::Vector{OrthogonalITensor}, inds_btree=nothing; kwargs...) + ctree_to_tensor = approximate_contract_ctree_to_tensor(tn, inds_btree; kwargs...) + return Vector{OrthogonalITensor}(vcat(collect(values(ctree_to_tensor))...)) +end + +function approximate_contract_ctree_to_tensor( + tn::Vector{OrthogonalITensor}, + inds_btree=nothing; + cutoff, + maxdim, + maxsize=10^15, + algorithm="mincut-mps", +) + uncontract_inds = noncommoninds(tn...) + allinds = collect(Set(mapreduce(t -> collect(inds(t)), vcat, tn))) + innerinds = setdiff(allinds, uncontract_inds) + if length(uncontract_inds) <= 2 + if inds_btree == nothing + inds_btree = [[i] for i in uncontract_inds] + end + return Dict{Vector,OrthogonalITensor}(inds_btree => optcontract(tn)) + end + # # cases where tn is a tree, or contains 2 disconnected trees + # if length(innerinds) <= length(tn) - 1 + # # TODO + # return tn + # end + # # TODO: may want to remove this + # if inds_groups != nothing + # deltainds = vcat(filter(g -> length(g) > 1, inds_groups)...) + # deltas, tnprime, _ = split_deltas(deltainds, tn) + # tn = Vector{ITensor}(vcat(deltas, tnprime)) + # end + if inds_btree == nothing + inds_btree = inds_binary_tree(get_tensors(tn), nothing; algorithm=algorithm) + end + # tree_approximation(tn, inds_btree; cutoff=cutoff, maxdim=maxdim) + embedding = tree_embedding(tn, inds_btree) + tn = Vector{OrthogonalITensor}(vcat(collect(values(embedding))...)) + i2 = noncommoninds(tn...) + @assert (length(uncontract_inds) == length(i2)) + @timeit timer "tree_approximation_cache" begin + return tree_approximation_cache( + embedding, inds_btree; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + ) + end +end + +function uncontractinds(tn) + if tn isa ITensor + return inds(tn) + else + return noncommoninds(vectorize(tn)...) + end +end + +# Note that the children ordering matters here. +mutable struct IndexAdjacencyTree + children::Union{Vector{IndexAdjacencyTree},Vector{IndexGroup}} + fixed_direction::Bool + fixed_order::Bool +end + +function Base.copy(tree::IndexAdjacencyTree) + node_to_copynode = Dict{IndexAdjacencyTree,IndexAdjacencyTree}() + for node in topo_sort(tree; type=IndexAdjacencyTree) + if node.children isa Vector{IndexGroup} + node_to_copynode[node] = IndexAdjacencyTree( + node.children, node.fixed_direction, node.fixed_order + ) + continue + end + copynode = IndexAdjacencyTree( + [node_to_copynode[n] for n in node.children], node.fixed_direction, node.fixed_order + ) + node_to_copynode[node] = copynode + end + return node_to_copynode[tree] +end + +function Base.show(io::IO, tree::IndexAdjacencyTree) + out_str = "\n" + stack = [tree] + node_to_level = Dict{IndexAdjacencyTree,Int}() + node_to_level[tree] = 0 + # pre-order traversal + while length(stack) != 0 + node = pop!(stack) + indent_vec = [" " for _ in 1:node_to_level[node]] + indent = string(indent_vec...) + if node.children isa Vector{IndexGroup} + for c in node.children + out_str = out_str * indent * string(c) * "\n" + end + else + out_str = + out_str * + indent * + "AdjTree: [fixed_direction]: " * + string(node.fixed_direction) * + " [fixed_order]: " * + string(node.fixed_order) * + "\n" + for c in node.children + node_to_level[c] = node_to_level[node] + 1 + push!(stack, c) + end + end + end + return print(io, out_str) +end + +function IndexAdjacencyTree(index_group::IndexGroup) + return IndexAdjacencyTree([index_group], false, false) +end + +function get_adj_tree_leaves(tree::IndexAdjacencyTree) + @timeit timer "get_adj_tree_leaves" begin + if tree.children isa Vector{IndexGroup} + return tree.children + end + leaves = [get_adj_tree_leaves(c) for c in tree.children] + return vcat(leaves...) + end +end + +function Base.contains(adj_tree::IndexAdjacencyTree, adj_igs::Set{IndexGroup}) + leaves = Set(get_adj_tree_leaves(adj_tree)) + return issubset(adj_igs, leaves) +end + +function Base.iterate(x::IndexAdjacencyTree) + return iterate(x, 1) +end + +function Base.iterate(x::IndexAdjacencyTree, index) + if index > length(x.children) + return nothing + end + return x.children[index], index + 1 +end + +function boundary_state(ancestor::IndexAdjacencyTree, adj_igs::Set{IndexGroup}) + if ancestor.children isa Vector{IndexGroup} + return "all" + end + if !ancestor.fixed_order + filter_children = filter(a -> contains(a, adj_igs), ancestor.children) + @assert length(filter_children) <= 1 + if length(filter_children) == 1 + return "middle" + elseif Set(get_adj_tree_leaves(ancestor)) == adj_igs + return "all" + else + return "invalid" + end + end + @assert length(ancestor.children) >= 2 + if contains(ancestor.children[1], adj_igs) + return "left" + elseif contains(ancestor.children[end], adj_igs) + return "right" + elseif Set(get_adj_tree_leaves(ancestor)) == adj_igs + return "all" + else + return "invalid" + end +end + +function reorder_to_right!( + ancestor::IndexAdjacencyTree, filter_children::Vector{IndexAdjacencyTree} +) + remain_children = setdiff(ancestor.children, filter_children) + @assert length(filter_children) >= 1 + @assert length(remain_children) >= 1 + if length(remain_children) == 1 + new_child1 = remain_children[1] + else + new_child1 = IndexAdjacencyTree(remain_children, false, false) + end + if length(filter_children) == 1 + new_child2 = filter_children[1] + else + new_child2 = IndexAdjacencyTree(filter_children, false, false) + end + ancestor.children = [new_child1, new_child2] + return ancestor.fixed_order = true +end + +""" +reorder adj_tree based on adj_igs +""" +function reorder!(adj_tree::IndexAdjacencyTree, adj_igs::Set{IndexGroup}; boundary="right") + @assert boundary in ["left", "right"] + if boundary_state(adj_tree, adj_igs) == "all" + return false + end + adj_trees = topo_sort(adj_tree; type=IndexAdjacencyTree) + ancestors = [tree for tree in adj_trees if contains(tree, adj_igs)] + ancestor_to_state = Dict{IndexAdjacencyTree,String}() + # get the boundary state + for ancestor in ancestors + state = boundary_state(ancestor, adj_igs) + if state == "invalid" + return false + end + ancestor_to_state[ancestor] = state + end + # update ancestors + for ancestor in ancestors + # reorder + if ancestor_to_state[ancestor] == "left" + ancestor.children = reverse(ancestor.children) + elseif ancestor_to_state[ancestor] == "middle" + @assert ancestor.fixed_order == false + filter_children = filter(a -> contains(a, adj_igs), ancestor.children) + reorder_to_right!(ancestor, filter_children) + end + # merge + if ancestor.fixed_order && ancestor.children isa Vector{IndexAdjacencyTree} + new_children = Vector{IndexAdjacencyTree}() + for child in ancestor.children + if !child.fixed_order + push!(new_children, child) + else + push!(new_children, child.children...) + end + end + ancestor.children = new_children + end + end + # check boundary + if boundary == "left" + for ancestor in ancestors + ancestor.children = reverse(ancestor.children) + end + end + return true +end + +# Update both keys and values in igs_to_adjacency_tree based on list_adjacent_igs +function update_igs_to_adjacency_tree!( + list_adjacent_igs::Vector, igs_to_adjacency_tree::Dict{Set{IndexGroup},IndexAdjacencyTree} +) + function update!(root_igs, adjacent_igs) + if !haskey(root_igs_to_adjacent_igs, root_igs) + root_igs_to_adjacent_igs[root_igs] = adjacent_igs + else + val = root_igs_to_adjacent_igs[root_igs] + root_igs_to_adjacent_igs[root_igs] = union(val, adjacent_igs) + end + end + @timeit timer "update_igs_to_adjacency_tree" begin + # get each root igs, get the adjacent igs needed. TODO: do we need to consider boundaries here? + root_igs_to_adjacent_igs = Dict{Set{IndexGroup},Set{IndexGroup}}() + for adjacent_igs in list_adjacent_igs + for root_igs in keys(igs_to_adjacency_tree) + if issubset(adjacent_igs, root_igs) + update!(root_igs, adjacent_igs) + end + end + end + if length(root_igs_to_adjacent_igs) == 1 + return nothing + end + # if at least 3: for now just put everything together + if length(root_igs_to_adjacent_igs) >= 3 + root_igs = keys(root_igs_to_adjacent_igs) + root = union(root_igs...) + igs_to_adjacency_tree[root] = IndexAdjacencyTree( + [igs_to_adjacency_tree[r] for r in root_igs], false, false + ) + for r in root_igs + delete!(igs_to_adjacency_tree, r) + end + end + # if 2: assign adjacent_igs to boundary of root_igs (if possible), then concatenate + igs1, igs2 = collect(keys(root_igs_to_adjacent_igs)) + reordered_1 = reorder!( + igs_to_adjacency_tree[igs1], root_igs_to_adjacent_igs[igs1]; boundary="right" + ) + reordered_2 = reorder!( + igs_to_adjacency_tree[igs2], root_igs_to_adjacent_igs[igs2]; boundary="left" + ) + adj_tree_1 = igs_to_adjacency_tree[igs1] + adj_tree_2 = igs_to_adjacency_tree[igs2] + if (!reordered_1) && (!reordered_2) + out_adj_tree = IndexAdjacencyTree([adj_tree_1, adj_tree_2], false, false) + elseif (!reordered_1) + out_adj_tree = IndexAdjacencyTree([adj_tree_1, adj_tree_2.children...], false, true) + elseif (!reordered_2) + out_adj_tree = IndexAdjacencyTree([adj_tree_1.children..., adj_tree_2], false, true) + else + out_adj_tree = IndexAdjacencyTree( + [adj_tree_1.children..., adj_tree_2.children...], false, true + ) + end + root_igs = keys(root_igs_to_adjacent_igs) + root = union(root_igs...) + igs_to_adjacency_tree[root] = out_adj_tree + for r in root_igs + delete!(igs_to_adjacency_tree, r) + end + end +end + +# Generate the adjacency tree of a contraction tree +# Args: +# ========== +# ctree: the input contraction tree +# ancestors: ancestor ctrees of the input ctree +# ctree_to_igs: mapping each ctree to neighboring index groups +function generate_adjacency_tree(ctree, ancestors, ctree_to_igs) + @timeit timer "generate_adjacency_tree" begin + # mapping each index group to adjacent input igs + ig_to_adjacent_igs = Dict{IndexGroup,Set{IndexGroup}}() + # mapping each igs to an adjacency tree + igs_to_adjacency_tree = Dict{Set{IndexGroup},IndexAdjacencyTree}() + for ig in ctree_to_igs[ctree] + ig_to_adjacent_igs[ig] = Set([ig]) + igs_to_adjacency_tree[Set([ig])] = IndexAdjacencyTree(ig) + end + for (i, a) in ancestors + inter_igs = intersect(ctree_to_igs[a[1]], ctree_to_igs[a[2]]) + new_igs_index = (i == 1) ? 2 : 1 + new_igs = setdiff(ctree_to_igs[a[new_igs_index]], inter_igs) + # Tensor product is not considered for now + @assert length(inter_igs) >= 1 + list_adjacent_igs = [ig_to_adjacent_igs[ig] for ig in inter_igs] + update_igs_to_adjacency_tree!(list_adjacent_igs, igs_to_adjacency_tree) + for ig in new_igs + ig_to_adjacent_igs[ig] = union(list_adjacent_igs...) + end + if length(igs_to_adjacency_tree) == 1 + return collect(values(igs_to_adjacency_tree))[1] + end + end + end +end + +function get_ancestors(ctree) + @timeit timer "get_ancestors" begin + ctree_to_ancestors = Dict{Vector,Vector}() + queue = [ctree] + ctree_to_ancestors[ctree] = [] + while queue != [] + node = popfirst!(queue) + if node isa Vector{ITensor} + continue + end + for (i, child) in enumerate(node) + queue = [queue..., child] + ctree_to_ancestors[child] = [(i, node), ctree_to_ancestors[node]...] + end + end + return ctree_to_ancestors + end +end + +# Mutates `v` by sorting elements `x[lo:hi]` using the insertion sort algorithm. +# This method is a copy-paste-edit of sort! in base/sort.jl, amended to return the bubblesort distance. +function _insertion_sort(v::Vector, lo::Int, hi::Int) + @timeit timer "_insertion_sort" begin + v = copy(v) + if lo == hi + return 0 + end + nswaps = 0 + for i in (lo + 1):hi + j = i + x = v[i] + while j > lo + if x < v[j - 1] + nswaps += 1 + v[j] = v[j - 1] + j -= 1 + continue + end + break + end + v[j] = x + end + return nswaps + end +end + +function insertion_sort(v1::Vector, v2::Vector) + value_to_index = Dict{Int,Int}() + for (i, v) in enumerate(v2) + value_to_index[v] = i + end + new_v1 = [value_to_index[v] for v in v1] + return _insertion_sort(new_v1, 1, length(new_v1)) +end + +function minswap_adjacency_tree!(adj_tree::IndexAdjacencyTree) + leaves = Vector{IndexGroup}(get_adj_tree_leaves(adj_tree)) + adj_tree.children = leaves + adj_tree.fixed_order = true + return adj_tree.fixed_direction = true +end + +function minswap_adjacency_tree!( + adj_tree::IndexAdjacencyTree, input_tree::IndexAdjacencyTree +) + nodes = input_tree.children + node_to_int = Dict{IndexGroup,Int}() + int_to_node = Dict{Int,IndexGroup}() + index = 1 + for node in nodes + node_to_int[node] = index + int_to_node[index] = node + index += 1 + end + for node in topo_sort(adj_tree; type=IndexAdjacencyTree) + if node.children isa Vector{IndexGroup} + continue + end + children_tree = [get_adj_tree_leaves(n) for n in node.children] + children_order = vcat(children_tree...) + input_int_order = [node_to_int[n] for n in nodes if n in children_order] + if node.fixed_order + perms = [children_tree, reverse(children_tree)] + else + perms = collect(permutations(children_tree)) + end + nswaps = [] + for perm in perms + int_order = [node_to_int[n] for n in vcat(perm...)] + push!(nswaps, insertion_sort(int_order, input_int_order)) + end + children_tree = perms[argmin(nswaps)] + node.children = vcat(children_tree...) + node.fixed_order = true + node.fixed_direction = true + end + int_order = [node_to_int[n] for n in adj_tree.children] + return _insertion_sort(int_order, 1, length(int_order)) +end + +function split_igs(igs::Vector{IndexGroup}, inter_igs::Vector{IndexGroup}) + igs_left = Vector{IndexGroup}() + igs_right = Vector{IndexGroup}() + target_array = igs_left + for i in igs + if i in inter_igs + target_array = igs_right + continue + end + push!(target_array, i) + end + return igs_left, igs_right +end + +function minswap_adjacency_tree( + adj_tree::IndexAdjacencyTree, + input_tree1::IndexAdjacencyTree, + input_tree2::IndexAdjacencyTree, +) + function merge(l1_left, l1_right, l2_left, l2_right) + if length(l1_left) < length(l2_left) + left_lists = [[l2_left..., l1_left...]] + elseif length(l1_left) > length(l2_left) + left_lists = [[l1_left..., l2_left...]] + else + left_lists = [[l2_left..., l1_left...], [l1_left..., l2_left...]] + end + if length(l1_right) < length(l2_right) + right_lists = [[l1_right..., l2_right...]] + elseif length(l1_right) > length(l2_right) + right_lists = [[l2_right..., l1_right...]] + else + right_lists = [[l2_right..., l1_right...], [l1_right..., l2_right...]] + end + out_lists = [] + for l in left_lists + for r in right_lists + push!(out_lists, IndexAdjacencyTree([l..., r...], true, true)) + end + end + return out_lists + end + @timeit timer "minswap_adjacency_tree" begin + leaves_1 = get_adj_tree_leaves(input_tree1) + leaves_2 = get_adj_tree_leaves(input_tree2) + inter_igs = intersect(leaves_1, leaves_2) + leaves_1_left, leaves_1_right = split_igs(leaves_1, inter_igs) + leaves_2_left, leaves_2_right = split_igs(leaves_2, inter_igs) + num_swaps_1 = + min(length(leaves_1_left), length(leaves_2_left)) + + min(length(leaves_1_right), length(leaves_2_right)) + num_swaps_2 = + min(length(leaves_1_left), length(leaves_2_right)) + + min(length(leaves_1_right), length(leaves_2_left)) + if num_swaps_1 == num_swaps_2 + inputs_1 = merge(leaves_1_left, leaves_1_right, leaves_2_left, leaves_2_right) + inputs_2 = merge( + leaves_1_left, leaves_1_right, reverse(leaves_2_right), reverse(leaves_2_left) + ) + inputs = [inputs_1..., inputs_2...] + elseif num_swaps_1 > num_swaps_2 + inputs = merge( + leaves_1_left, leaves_1_right, reverse(leaves_2_right), reverse(leaves_2_left) + ) + else + inputs = merge(leaves_1_left, leaves_1_right, leaves_2_left, leaves_2_right) + end + # TODO: may want to change this back + # leaves_1 = [i for i in leaves_1 if !(i in inter_igs)] + # leaves_2 = [i for i in leaves_2 if !(i in inter_igs)] + # input1 = IndexAdjacencyTree([leaves_1..., leaves_2...], true, true) + # input2 = IndexAdjacencyTree([leaves_1..., reverse(leaves_2)...], true, true) + # input3 = IndexAdjacencyTree([reverse(leaves_1)..., leaves_2...], true, true) + # input4 = IndexAdjacencyTree([reverse(leaves_1)..., reverse(leaves_2)...], true, true) + # inputs = [input1, input2, input3, input4] + # ====================================== + adj_tree_copies = [copy(adj_tree) for _ in 1:length(inputs)] + nswaps = [minswap_adjacency_tree!(t, i) for (t, i) in zip(adj_tree_copies, inputs)] + return adj_tree_copies[argmin(nswaps)] + end +end + +function _approximate_contract_pre_process(tn_leaves, ctrees) + @timeit timer "_approximate_contract_pre_process" begin + # mapping each contraction tree to its uncontracted index groups + ctree_to_igs = Dict{Vector,Vector{IndexGroup}}() + index_groups = get_index_groups(ctrees[end]) + for c in vcat(tn_leaves, ctrees) + ctree_to_igs[c] = neighbor_index_groups(c, index_groups) + end + ctree_to_ancestors = get_ancestors(ctrees[end]) + # mapping each contraction tree to its index adjacency tree + ctree_to_adj_tree = Dict{Vector,IndexAdjacencyTree}() + for leaf in tn_leaves + ctree_to_adj_tree[leaf] = generate_adjacency_tree( + leaf, ctree_to_ancestors[leaf], ctree_to_igs + ) + minswap_adjacency_tree!(ctree_to_adj_tree[leaf]) + end + for c in ctrees + ancestors = ctree_to_ancestors[c] + if ancestors == [] + continue + end + adj_tree = generate_adjacency_tree(c, ancestors, ctree_to_igs) + ctree_to_adj_tree[c] = minswap_adjacency_tree( + adj_tree, ctree_to_adj_tree[c[1]], ctree_to_adj_tree[c[2]] + ) + end + # mapping each index group to the index group tree + ig_to_ig_tree = Dict{IndexGroup,IndexGroup}() + for leaf in tn_leaves + for ig in ctree_to_igs[leaf] + if !haskey(ig_to_ig_tree, ig) + inds_tree = inds_binary_tree(leaf, ig.data; algorithm="mincut") + ig_to_ig_tree[ig] = IndexGroup(inds_tree, true) + end + end + end + return ctree_to_igs, ctree_to_adj_tree, ig_to_ig_tree + end +end + +function ordered_igs_to_binary_tree(ordered_igs, contract_igs, ig_to_ig_tree) + @timeit timer "ordered_igs_to_binary_tree" begin + @assert contract_igs != [] + left_igs, right_igs = split_igs(ordered_igs, contract_igs) + tree_1 = line_to_tree([ig_to_ig_tree[ig].data for ig in left_igs]) + tree_contract = line_to_tree([ig_to_ig_tree[ig].data for ig in contract_igs]) + tree_2 = line_to_tree([ig_to_ig_tree[ig].data for ig in reverse(right_igs)]) + return merge_tree(merge_tree(tree_1, tree_contract), tree_2) + end +end + +function get_igs_cache_info(igs_list, contract_igs_list) + function split_boundary(list1::Vector{IndexGroup}, list2::Vector{IndexGroup}) + index = 1 + boundary = Vector{IndexGroup}() + while list1[index] == list2[index] + push!(boundary, list2[index]) + index += 1 + if index > length(list1) || index > length(list2) + break + end + end + if index <= length(list1) + remain_list1 = list1[index:end] + else + remain_list1 = Vector{IndexGroup}() + end + return boundary, remain_list1 + end + function split_boundary(igs::Vector{IndexGroup}, lists::Vector{Vector{IndexGroup}}) + if length(igs) <= 1 + return Vector{IndexGroup}(), igs + end + for l in lists + if length(l) >= 2 && igs[1] == l[1] && igs[2] == l[2] + return split_boundary(igs, l) + end + end + return Vector{IndexGroup}(), igs + end + @timeit timer "get_igs_cache_info" begin + out, input1, input2 = igs_list + contract_out, contract_input1, contract_input2 = contract_igs_list + out_left, out_right = split_igs(out, contract_out) + out_right = reverse(out_right) + input1_left, input1_right = split_igs(input1, contract_input1) + input2_left, input2_right = split_igs(input2, contract_input2) + inputs = [input1_left, reverse(input1_right), input2_left, reverse(input2_right)] + boundary_left, remain_left = split_boundary(out_left, inputs) + boundary_right, remain_right = split_boundary(out_right, inputs) + return [remain_left..., contract_out..., reverse(remain_right)...], + boundary_left, + boundary_right + end +end + +function get_tn_cache_sub_info( + tn_tree::Dict{Vector,OrthogonalITensor}, cache_binary_trees::Vector +) + cached_tn = [] + cached_tn_tree = Dict{Vector,OrthogonalITensor}() + new_igs = [] + for binary_tree in cache_binary_trees + if binary_tree == [] || !haskey(tn_tree, binary_tree) + push!(new_igs, nothing) + else + binary_tree = Vector{Vector}(binary_tree) + @info "binary_tree is", binary_tree, typeof(binary_tree) + nodes = topo_sort(binary_tree; type=Vector{<:Vector}) + @info "nodes is", nodes + sub_tn = [tn_tree[n] for n in nodes] + @info "sub_tn is", sub_tn + sub_tn_tree = Dict([n => tn_tree[n] for n in nodes]...) + index_leaves = vectorize(binary_tree) + new_indices = setdiff(noncommoninds(sub_tn...), index_leaves) + @assert length(new_indices) == 1 + new_indices = Vector{<:Index}(new_indices) + push!(new_igs, IndexGroup(new_indices)) + cached_tn = vcat(cached_tn, sub_tn) + cached_tn_tree = merge(cached_tn_tree, sub_tn_tree) + end + end + tn = vcat(collect(values(tn_tree))...) + uncached_tn = setdiff(tn, cached_tn) + return cached_tn_tree, uncached_tn, new_igs +end + +function get_tn_cache_info( + tn_tree_1::Dict{Vector,OrthogonalITensor}, + tn_tree_2::Dict{Vector,OrthogonalITensor}, + cache_binary_trees::Vector, +) + @info "tn_tree_1 is", tn_tree_1 + @info "tn_tree_2 is", tn_tree_2 + @info "cache_binary_trees is", cache_binary_trees + @timeit timer "get_tn_cache_info" begin + cached_tn_tree1, uncached_tn1, new_igs_1 = get_tn_cache_sub_info( + tn_tree_1, cache_binary_trees + ) + cached_tn_tree2, uncached_tn2, new_igs_2 = get_tn_cache_sub_info( + tn_tree_2, cache_binary_trees + ) + uncached_tn = [uncached_tn1..., uncached_tn2...] + new_igs_left = [i for i in [new_igs_1[1], new_igs_2[1]] if i != nothing] + @assert length(new_igs_left) <= 1 + if length(new_igs_left) == 1 + new_ig_left = new_igs_left[1] + else + new_ig_left = nothing + end + new_igs_right = [i for i in [new_igs_1[2], new_igs_2[2]] if i != nothing] + @assert length(new_igs_right) <= 1 + if length(new_igs_right) == 1 + new_ig_right = new_igs_right[1] + else + new_ig_right = nothing + end + return merge(cached_tn_tree1, cached_tn_tree2), uncached_tn, new_ig_left, new_ig_right + end +end + +function update_tn_tree_keys!(tn_tree, inds_btree, pairs::Vector{Pair}) + @timeit timer "update_tn_tree_keys!" begin + current_to_update_key = Dict{Vector,Vector}(pairs...) + nodes = topo_sort(inds_btree; type=Vector{<:Vector}) + for n in nodes + @assert haskey(tn_tree, n) + new_key = n + if haskey(current_to_update_key, n[1]) + new_key = [current_to_update_key[n[1]], n[2]] + end + if haskey(current_to_update_key, n[2]) + new_key = [new_key[1], current_to_update_key[n[2]]] + end + if new_key != n + tn_tree[new_key] = tn_tree[n] + delete!(tn_tree, n) + current_to_update_key[n] = new_key + end + end + end +end + +# ctree: contraction tree +# tn: vector of tensors representing a tensor network +# tn_tree: a dict maps each index tree in the tn to a tensor +# adj_tree: index adjacency tree +# ig: index group +# contract_ig: the index group to be contracted next +# ig_tree: an index group with a tree hierarchy +function approximate_contract(ctree::Vector; kwargs...) + tn_leaves = get_leaves(ctree) + ctrees = topo_sort(ctree; leaves=tn_leaves) + ctree_to_igs, ctree_to_adj_tree, ig_to_ig_tree = _approximate_contract_pre_process( + tn_leaves, ctrees + ) + ctree_to_contract_igs = Dict{Vector,Vector{IndexGroup}}() + for c in ctrees + contract_igs = intersect(ctree_to_igs[c[1]], ctree_to_igs[c[2]]) + ctree_to_contract_igs[c[1]] = contract_igs + ctree_to_contract_igs[c[2]] = contract_igs + end + # mapping each contraction tree to a tensor network + ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() + for leaf in tn_leaves + inds_btree = ordered_igs_to_binary_tree( + ctree_to_adj_tree[leaf].children, ctree_to_contract_igs[leaf], ig_to_ig_tree + ) + ctree_to_tn_tree[leaf] = approximate_contract_ctree_to_tensor( + orthogonal_tensors(leaf), inds_btree; kwargs... + ) + end + for c in ctrees + if ctree_to_igs[c] == [] + @assert c == ctrees[end] + tn1 = vcat(collect(values(ctree_to_tn_tree[c[1]]))...) + tn2 = vcat(collect(values(ctree_to_tn_tree[c[2]]))...) + tn = vcat(tn1, tn2) + return get_tensors([optcontract(tn)]) + end + uncache_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( + [ctree_to_adj_tree[i].children for i in [c, c[1], c[2]]], + [ctree_to_contract_igs[i] for i in [c, c[1], c[2]]], + ) + cache_binary_tree_left = line_to_tree([ig_to_ig_tree[ig].data for ig in cache_igs_left]) + cache_binary_tree_right = line_to_tree([ + ig_to_ig_tree[ig].data for ig in cache_igs_right + ]) + @info "cache_binary_tree_left has type", typeof(cache_binary_tree_left) + @info "cache_binary_tree_right has type", typeof(cache_binary_tree_right) + cached_tn_tree, uncached_tn, new_ig_left, new_ig_right = get_tn_cache_info( + ctree_to_tn_tree[c[1]], + ctree_to_tn_tree[c[2]], + [cache_binary_tree_left, cache_binary_tree_right], + ) + new_ig_to_binary_tree_pairs = Vector{Pair}() + new_igs = uncache_igs + new_ig_to_ig_tree = ig_to_ig_tree + if new_ig_left != nothing + new_ig_to_ig_tree = merge(new_ig_to_ig_tree, Dict(new_ig_left => new_ig_left)) + new_igs = [new_ig_left, new_igs...] + push!(new_ig_to_binary_tree_pairs, new_ig_left.data => cache_binary_tree_left) + end + if new_ig_right != nothing + new_ig_to_ig_tree = merge(new_ig_to_ig_tree, Dict(new_ig_right => new_ig_right)) + new_igs = [new_igs..., new_ig_right] + push!(new_ig_to_binary_tree_pairs, new_ig_right.data => cache_binary_tree_right) + end + inds_btree = ordered_igs_to_binary_tree( + new_igs, ctree_to_contract_igs[c], new_ig_to_ig_tree + ) + new_tn_tree = approximate_contract_ctree_to_tensor(uncached_tn, inds_btree; kwargs...) + update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) + ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) + end + tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) + return get_tensors(tn) +end + +# interlaced HOSVD using caching +function tree_approximation_cache( + embedding::Dict, inds_btree::Vector; cutoff=1e-15, maxdim=10000, maxsize=10000 +) + ctree_to_tensor = Dict{Vector,OrthogonalITensor}() + # initialize sim_dict + network = vcat(collect(values(embedding))...) + uncontractinds = noncommoninds(network...) + innerinds = mapreduce(t -> [i for i in inds(t)], vcat, network) + innerinds = Vector(setdiff(innerinds, uncontractinds)) + siminner_dict = Dict([ind => sim(ind) for ind in innerinds]) + + function closednet(tree) + netbra = embedding[tree] + netket = replaceinds(netbra, siminner_dict) + if length(tree) == 1 + return optcontract(vcat(netbra, netket)) + end + tleft, tright = closednet(tree[1]), closednet(tree[2]) + return optcontract(vcat(netbra, netket, [tleft], [tright])) + end + + function insert_projectors(tree::Vector, env::OrthogonalITensor) + netbra = embedding[tree] + netket = replaceinds(netbra, siminner_dict) + if length(tree) == 1 + tensor_bra = optcontract(netbra) + tensor_ket = replaceinds([tensor_bra], siminner_dict)[1] + inds_pair = (tree[1], sim(tree[1])) + tensor_ket = replaceinds([tensor_ket], Dict([inds_pair[1] => inds_pair[2]]))[1] + return inds_pair, optcontract([netbra..., netket...]), [tensor_bra, tensor_ket] + end + # update children + subenvtensor = optcontract([env, netbra...]) + envnet = [subenvtensor, closednet(tree[2]), netket...] + ind1_pair, subnetsq1, subnet1 = insert_projectors(tree[1], optcontract(envnet)) + envnet = [subenvtensor, subnetsq1, netket...] + ind2_pair, _, subnet2 = insert_projectors(tree[2], optcontract(envnet)) + # compute the projector + rinds = (ind1_pair[1], ind2_pair[1]) + linds = (ind1_pair[2], ind2_pair[2]) + # to handle the corner cases where subnet1/subnet2 could be empty + netket = replaceinds( + netket, Dict([ind1_pair[1] => ind1_pair[2], ind2_pair[1] => ind2_pair[2]]) + ) + net = [subenvtensor, netket..., subnet1..., subnet2...] + tnormal = optcontract(net) + dim2 = floor(maxsize / (space(ind1_pair[1]) * space(ind2_pair[1]))) + dim = min(maxdim, dim2) + @timeit timer "eigen" begin + diag, U = eigen( + tnormal.tensor, linds, rinds; cutoff=cutoff, maxdim=dim, ishermitian=true + ) + end + dr = commonind(diag, U) + Usim = replaceinds(U, rinds => linds) + ortho_U = OrthogonalITensor(U) + ortho_Usim = OrthogonalITensor(Usim) + net1 = [netbra..., subnet1[1], subnet2[1], ortho_U] + net2 = [netket..., subnet1[2], subnet2[2], ortho_Usim] + tensor1 = optcontract(net1) + tensor2 = replaceinds(tensor1, noncommoninds(net1...) => noncommoninds(net2...)) + subnetsq = optcontract([tensor1, tensor2]) + dr_pair = (dr, sim(dr)) + tensor2 = replaceinds(tensor2, [dr_pair[1]] => [dr_pair[2]]) + subnet = [tensor1, tensor2] + ctree_to_tensor[tree] = ortho_U + return dr_pair, subnetsq, subnet + end + + @assert (length(inds_btree) >= 2) + bra = embedding[inds_btree] + ket = replaceinds(bra, siminner_dict) + # update children + envnet = [closednet(inds_btree[2]), bra..., ket...] + _, netsq1, n1 = insert_projectors(inds_btree[1], optcontract(envnet)) + envnet = [netsq1, bra..., ket...] + _, _, n2 = insert_projectors(inds_btree[2], optcontract(envnet)) + # last tensor + envnet = [n1[1], n2[1], bra...] + last_tensor = optcontract(envnet) + ctree_to_tensor[inds_btree] = last_tensor + return ctree_to_tensor +end diff --git a/src/ApproximateTNContraction/contract/index_group.jl b/src/ApproximateTNContraction/contract/index_group.jl new file mode 100644 index 00000000..51d34a51 --- /dev/null +++ b/src/ApproximateTNContraction/contract/index_group.jl @@ -0,0 +1,38 @@ +mutable struct IndexGroup + data::Vector + istree::Bool +end + +# TODO: general tags are not comparable +Base.isless(a::Index, b::Index) = id(a) < id(b) || (id(a) == id(b) && plev(a) < plev(b)) # && tags(a) < tags(b) + +function IndexGroup(indices::Vector{<:Index}) + return IndexGroup(sort(indices), false) +end + +function get_index_groups(tn_tree::Vector) + @timeit timer "get_index_groups" begin + tn_leaves = get_leaves(tn_tree) + igs = [] + for (t1, t2) in powerset(tn_leaves, 2, 2) + inds = intersect(noncommoninds(t1...), noncommoninds(t2...)) + if length(inds) >= 1 + push!(igs, IndexGroup(inds)) + end + end + return igs + end +end + +function neighbor_index_groups(contraction, index_groups) + @timeit timer "get_index_groups" begin + inds = noncommoninds(vectorize(contraction)...) + nigs = [] + for ig in index_groups + if issubset(ig.data, inds) + push!(nigs, ig) + end + end + return nigs + end +end diff --git a/src/ApproximateTNContraction/contract/mincut_tree.jl b/src/ApproximateTNContraction/contract/mincut_tree.jl new file mode 100644 index 00000000..f24811a9 --- /dev/null +++ b/src/ApproximateTNContraction/contract/mincut_tree.jl @@ -0,0 +1,249 @@ + +# a large number to prevent this edge being a cut +MAX_WEIGHT = 100000 + +function inds_binary_tree( + network::Vector{ITensor}, inds_groups::Vector{<:Vector}; kwargs... +) + tng = TensorNetworkGraph(network) + function get_sub_tree(inds) + @assert all(ind -> ind isa Index, inds) + if length(inds) == 1 + return inds + end + inds = [[i] for i in inds] + return inds_binary_tree!(tng, inds; kwargs...) + end + inds_groups = [get_sub_tree(inds) for inds in inds_groups] + if length(inds_groups) <= 2 + return inds_groups + end + return inds_binary_tree!(tng, inds_groups; kwargs...) +end + +function inds_binary_tree!(tng::TensorNetworkGraph, outinds::Vector; algorithm="mincut") + @assert algorithm in ["mincut", "mincut-mps", "mps"] + @assert all(ind -> ind in keys(tng.out_edge_dict), outinds) + if algorithm == "mincut" + return mincut_inds!(tng, outinds) + elseif algorithm == "mincut-mps" + inds_tree = mincut_inds!(tng, outinds) + linear_tree = linearize(inds_tree, tng) + out_inds = linear_tree[1] + for i in 2:length(linear_tree) + out_inds = [out_inds, linear_tree[i]] + end + return out_inds + elseif algorithm == "mps" + return mps_inds!(tng, outinds) + end +end + +function inds_binary_tree( + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" +) + if outinds == nothing + outinds = noncommoninds(network...) + end + if length(outinds) == 1 + return outinds + end + if algorithm == "sequential-mps" + out_inds = [outinds[1]] + for i in 2:length(outinds) + out_inds = [out_inds, [outinds[i]]] + end + return out_inds + end + tng = TensorNetworkGraph(network) + grouped_uncontracted_inds = [[i] for i in outinds] + return inds_binary_tree!(tng, grouped_uncontracted_inds; algorithm=algorithm) +end + +#TODO: rewrite this function +#TODO: pick one end deterministically +function linearize(inds_tree::Vector, tng::TensorNetworkGraph) + get_dist(edge, distances) = distances.dists[tng.inner_edge_dict[edge][1]] + function get_boundary_dists(line, source) + first, last = line[1], line[end] + ds = dijkstra_shortest_paths(tng.graph, source, tng.weights) + return get_dist(first, ds), get_dist(last, ds) + end + + if length(inds_tree) == 1 + return inds_tree + end + left = linearize(inds_tree[1], tng) + right = linearize(inds_tree[2], tng) + if length(left) == 1 && length(right) == 1 + return [left, right] + end + if length(left) == 1 + source = tng.inner_edge_dict[left][1] + dist_first, dist_last = get_boundary_dists(right, source) + if dist_last < dist_first + right = reverse(right) + end + return [left, right...] + end + if length(right) == 1 + source = tng.inner_edge_dict[right][1] + dist_first, dist_last = get_boundary_dists(left, source) + if dist_last > dist_first + left = reverse(left) + end + return [left..., right] + end + s1, s2 = tng.inner_edge_dict[left[1]][1], tng.inner_edge_dict[left[end]][1] + dist1_first, dist1_last = get_boundary_dists(right, s1) + dist2_first, dist2_last = get_boundary_dists(right, s2) + if min(dist1_first, dist1_last) < min(dist2_first, dist2_last) + left = reverse(left) + if dist1_last < dist1_first + right = reverse(right) + end + else + if dist2_last < dist2_first + right = reverse(right) + end + end + return [left..., right...] +end + +function mincut_subnetwork( + network::Vector{ITensor}, sourceinds::Vector, uncontract_inds::Vector +) + @timeit timer "mincut_subnetwork" begin + if length(sourceinds) == length(uncontract_inds) + return network + end + tng = TensorNetworkGraph(network) + grouped_sourceinds = [[ind] for ind in sourceinds] + part1, part2, mincut = mincut_value(tng, grouped_sourceinds) + @assert length(part1) > 1 + @assert length(part2) > 1 + return [network[i] for i in part1 if i <= length(network)] + end +end + +function mincut_inds!(tng::TensorNetworkGraph, outinds::Vector) + @assert length(outinds) >= 1 + # base case here, for the case length(outinds) == 2, we still need to do the update + if length(outinds) == 1 + return outinds[1] + end + new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) + outinds = update!(tng, outinds, new_edge, minval) + return mincut_inds!(tng, outinds) +end + +function mincut_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) + @timeit timer "mincut_inds" begin + tng = copy(tng) + uncontract_inds = copy(uncontract_inds) + return mincut_inds!(tng, uncontract_inds) + end +end + +function mps_inds!(tng::TensorNetworkGraph, outinds::Vector) + @assert length(outinds) >= 1 + # base case here, for the case length(outinds) == 2, we still need to do the update + if length(outinds) == 1 + return outinds[1] + end + new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) + outinds = update!(tng, outinds, new_edge, minval) + first_ind = new_edge + while length(outinds) > 2 + splitinds = [[first_ind, i] for i in outinds if i != first_ind] + new_edge, minval = new_edge_mincut(tng, splitinds) + outinds = update!(tng, outinds, new_edge, minval) + first_ind = new_edge + end + return outinds +end + +function mps_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) + tng = copy(tng) + uncontract_inds = copy(uncontract_inds) + return mps_inds!(tng, uncontract_inds) +end + +# update the graph +function update!(tng::TensorNetworkGraph, uncontract_inds::Vector, new_edge::Vector, minval) + add_vertex!(tng.graph) + last_vertex = size(tng.graph)[1] + u1, w_u1 = tng.out_edge_dict[new_edge[1]] + u2, w_u2 = tng.out_edge_dict[new_edge[2]] + Graphs.add_edge!(tng.graph, u1, last_vertex) + Graphs.add_edge!(tng.graph, u2, last_vertex) + Graphs.add_edge!(tng.graph, last_vertex, u1) + Graphs.add_edge!(tng.graph, last_vertex, u2) + new_weights = zeros(last_vertex, last_vertex) + new_weights[1:(last_vertex - 1), 1:(last_vertex - 1)] = tng.weights + #if not setting to MAX_WEIGHT would affect later tree selections + new_weights[u1, last_vertex] = MAX_WEIGHT + new_weights[u2, last_vertex] = MAX_WEIGHT + new_weights[last_vertex, u1] = MAX_WEIGHT + new_weights[last_vertex, u2] = MAX_WEIGHT + # update the dict + tng.inner_edge_dict[new_edge[1]] = (u1, last_vertex, MAX_WEIGHT)#w_u1) + tng.inner_edge_dict[new_edge[2]] = (u2, last_vertex, MAX_WEIGHT)#w_u2) + delete!(tng.out_edge_dict, new_edge[1]) + delete!(tng.out_edge_dict, new_edge[2]) + tng.out_edge_dict[new_edge] = (last_vertex, minval) + # update uncontract_inds + uncontract_inds = setdiff(uncontract_inds, new_edge) + uncontract_inds = vcat([new_edge], uncontract_inds) + tng.weights = new_weights + return uncontract_inds +end + +# TODO: rewrite this function +function new_edge_mincut(tng::TensorNetworkGraph, split_inds_list::Vector) + mincuts = [mincut_value(tng, split_inds)[3] for split_inds in split_inds_list] + split_sizes = [ + sum([tng.out_edge_dict[ind][2] for ind in split_inds]) for split_inds in split_inds_list + ] + dists = [distance(tng, inds...) for inds in split_inds_list] + weights = [min(mincuts[i], split_sizes[i]) for i in 1:length(mincuts)] + indices_min = [i for i in 1:length(mincuts) if weights[i] == min(weights...)] + cuts_min = [mincuts[i] for i in indices_min] + indices_min = [i for i in indices_min if mincuts[i] == min(cuts_min...)] + dists_min = [dists[i] for i in indices_min] + _, index = findmin(dists_min) + i = indices_min[index] + minval = weights[i] + new_edge = split_inds_list[i] + return new_edge, minval +end + +function mincut_value(tng::TensorNetworkGraph, split_inds::Vector) + tng = copy(tng) + # add two vertices to the graph to model the s and t + add_vertices!(tng.graph, 2) + t = size(tng.graph)[1] + s = t - 1 + new_weights = zeros(t, t) + new_weights[1:(t - 2), 1:(t - 2)] = tng.weights + for ind in split_inds + u, _ = tng.out_edge_dict[ind] + Graphs.add_edge!(tng.graph, u, s) + Graphs.add_edge!(tng.graph, s, u) + new_weights[u, s] = MAX_WEIGHT + new_weights[s, u] = MAX_WEIGHT + end + terminal_inds = setdiff(noncommoninds(tng), split_inds) + for ind in terminal_inds + u, _ = tng.out_edge_dict[ind] + Graphs.add_edge!(tng.graph, u, t) + Graphs.add_edge!(tng.graph, t, u) + new_weights[u, t] = MAX_WEIGHT + new_weights[t, u] = MAX_WEIGHT + end + # this t and s sequence makes sure part1 is the largest subgraph yielding mincut + part2, part1, flow = GraphsFlows.mincut( + tng.graph, t, s, new_weights, EdmondsKarpAlgorithm() + ) + return part1, part2, flow +end diff --git a/src/ApproximateTNContraction/contract/tensornetwork_graph.jl b/src/ApproximateTNContraction/contract/tensornetwork_graph.jl new file mode 100644 index 00000000..7dae1078 --- /dev/null +++ b/src/ApproximateTNContraction/contract/tensornetwork_graph.jl @@ -0,0 +1,137 @@ +mutable struct TensorNetworkGraph + network::Vector{ITensor} + graph::SimpleDiGraph + weights::Matrix + #a dict that maps uncontracted Index to the adjacent vertices pair (i,j) + out_edge_dict::Dict + inner_edge_dict::Dict +end + +function TensorNetworkGraph(network::Vector{ITensor}) + uncontract_inds = noncommoninds(network...) + graph = Graphs.DiGraph(length(network)) + # construct contract_edges + contract_edges = [] + inner_edge_dict = Dict() + for (i, t) in enumerate(network) + for ind in setdiff(inds(t), uncontract_inds) + if !haskey(inner_edge_dict, [ind]) + inner_edge_dict[[ind]] = (i, log2(space(ind))) + else + @assert(length(inner_edge_dict[[ind]]) == 2) + inner_edge_dict[[ind]] = (inner_edge_dict[[ind]][1], i, inner_edge_dict[[ind]][2]) + push!(contract_edges, inner_edge_dict[[ind]]) + end + end + end + weights = zeros(length(network), length(network)) + for e in contract_edges + u, v, f = e + Graphs.add_edge!(graph, u, v) + Graphs.add_edge!(graph, v, u) + weights[u, v] = f + weights[v, u] = f + end + # construct out_edge_dict + out_edge_dict = Dict() + for (i, t) in enumerate(network) + ucinds = intersect(inds(t), uncontract_inds) + if length(ucinds) == 0 + continue + end + for ind in ucinds + out_edge_dict[[ind]] = (i, log2(space(ind))) + end + end + return TensorNetworkGraph(network, graph, weights, out_edge_dict, inner_edge_dict) +end + +Base.show(io::IO, tng::TensorNetworkGraph) = print(io, tng.out_edge_dict) + +function Base.copy(tng::TensorNetworkGraph) + return TensorNetworkGraph( + tng.network, + copy(tng.graph), + copy(tng.weights), + copy(tng.out_edge_dict), + copy(tng.inner_edge_dict), + ) +end + +function ITensors.noncommoninds(tng::TensorNetworkGraph) + return keys(tng.out_edge_dict) +end + +function distance(tng::TensorNetworkGraph, s, t) + sindex = tng.out_edge_dict[s][1] + ds = dijkstra_shortest_paths(tng.graph, sindex, tng.weights) + get_dist(edge) = ds.dists[tng.out_edge_dict[edge][1]] + return get_dist(t) +end + +#TODO: delete this +distance(tng::TensorNetworkGraph, s) = 0.0 + +function insert_outedge_vertex!(tng::TensorNetworkGraph) + if length(tng.out_edge_dict) == 0 + return nothing + end + add_vertices!(tng.graph, 1) + t = size(tng.graph)[1] + new_weights = zeros(t, t) + new_weights[1:(t - 1), 1:(t - 1)] = tng.weights + for (inds, edge) in tng.out_edge_dict + u, wu = edge + Graphs.add_edge!(tng.graph, u, t) + Graphs.add_edge!(tng.graph, t, u) + new_weights[u, t] = wu + new_weights[t, u] = wu + tng.out_edge_dict[inds] = (u, t, wu) + end + return tng.weights = new_weights +end + +function indsname(inds::Vector) + if length(inds) == 1 + return string(inds[1].tags) + end + return "" +end + +function visualize(tng::TensorNetworkGraph) + tng = copy(tng) + insert_outedge_vertex!(tng::TensorNetworkGraph) + wg = SimpleWeightedGraph(tng.graph) + for e in edges(tng.graph) + add_edge!(wg, src(e), dst(e), tng.weights[src(e), dst(e)]) + end + edgelabel_dict = Dict{Tuple{Int,Int},String}() + edgecolor_dict = Dict() + for (inds, edge) in tng.inner_edge_dict + if indsname(inds) == "" + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :blue + else + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :black + end + end + for (inds, edge) in tng.out_edge_dict + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :red + end + return graphplot( + wg; + markersize=0.3, + # names=names, + edgelabel=edgelabel_dict, + curves=false, + edgecolor=edgecolor_dict, + linewidth=20, + fontsize=30, + size=(7000, 7000), + ) +end diff --git a/src/ApproximateTNContraction/contract/tree_embedding.jl b/src/ApproximateTNContraction/contract/tree_embedding.jl new file mode 100644 index 00000000..45f885ef --- /dev/null +++ b/src/ApproximateTNContraction/contract/tree_embedding.jl @@ -0,0 +1,119 @@ +include("union_find.jl") + +function tree_embedding(network::Vector{OrthogonalITensor}, inds_btree::Vector) + # TODO: consider identity matrices + tensor_to_ortho_tensor = Dict{ITensor,OrthogonalITensor}() + for ortho_tensor in network + tensor_to_ortho_tensor[ortho_tensor.tensor] = ortho_tensor + end + tnets_dict = tree_embedding(get_tensors(network), inds_btree) + ortho_tnets_dict = Dict() + for (key, tensors) in tnets_dict + ortho_tensors = Vector{OrthogonalITensor}() + for t in tensors + if haskey(tensor_to_ortho_tensor, t) + push!(ortho_tensors, tensor_to_ortho_tensor[t]) + else + push!(ortho_tensors, OrthogonalITensor(t)) + end + end + ortho_tnets_dict[key] = ortho_tensors + end + return ortho_tnets_dict +end + +function tree_embedding(network::Vector{ITensor}, inds_btree::Vector) + deltas, networkprime, _ = split_deltas(noncommoninds(network...), network) + network = vcat(deltas, networkprime) + # tnets_dict map each inds_btree node to a tensor network + tnets_dict = Dict() + function embed(tree::Vector) + if length(tree) == 1 + # add delta to handle the case with two output edges neighboring to one tensor + # being split (MPS case with element grouping) + ind = tree[1] + sim_dict = Dict([ind => sim(ind)]) + tnets_dict[tree] = [delta(ind, sim_dict[ind])] + network = replaceinds(network, sim_dict) + return Tuple([sim_dict[ind]]) + end + ind1 = embed(tree[1]) + ind2 = embed(tree[2]) + deltas, splitinds, tnets_dict[tree[1]] = insert_deltas(ind1, ind2, tnets_dict[tree[1]]) + network = Vector{ITensor}(vcat(network, deltas)) + # use mincut to get the subnetwork + subnetwork = mincut_subnetwork(network, splitinds, noncommoninds(network...)) + subsplitinds = intersect(splitinds, noncommoninds(subnetwork...)) + remaininds = collect(setdiff(noncommoninds(subnetwork...), subsplitinds)) + network = collect(setdiff(network, subnetwork)) + # remaininds + deltas, subnetwork, _ = split_deltas(remaininds, subnetwork) + network = vcat(network, deltas) + # subsplitinds + inds = collect(setdiff(splitinds, subsplitinds)) + if length(inds) > 0 + inds = Vector{Index}(inds) + deltas, network, _ = split_deltas(inds, network) + subnetwork = vcat(subnetwork, deltas) + end + # @info "$(tree), $(TreeTensor(subnetwork...))" + tnets_dict[tree] = subnetwork + return Tuple(setdiff(noncommoninds(subnetwork...), splitinds)) + end + @assert (length(inds_btree) >= 2) + embed(inds_btree) + return remove_deltas(tnets_dict) +end + +is_delta(t) = (t.tensor.storage.data == 1.0) + +# remove deltas to improve the performance +function remove_deltas(tnets_dict) + # only remove deltas in intermediate nodes + ks = filter(k -> (length(k) > 1), collect(keys(tnets_dict))) + network = vcat([tnets_dict[k] for k in ks]...) + # outinds will always be the roots in union-find + outinds = noncommoninds(network...) + + deltas = filter(t -> is_delta(t), network) + inds_list = map(t -> collect(inds(t)), deltas) + deltainds = collect(Set(vcat(inds_list...))) + uf = UF(deltainds) + for t in deltas + i1, i2 = inds(t) + if root(uf, i1) in outinds + connect(uf, i2, i1) + else + connect(uf, i1, i2) + end + end + sim_dict = Dict([ind => root(uf, ind) for ind in deltainds]) + for k in ks + net = tnets_dict[k] + net = setdiff(net, deltas) + tnets_dict[k] = replaceinds(net, sim_dict) + # @info "$(k), $(TreeTensor(net...))" + end + return tnets_dict +end + +function split_deltas(inds, subnet) + sim_dict = Dict([ind => sim(ind) for ind in inds]) + deltas = [delta(i, sim_dict[i]) for i in inds] + subnet = replaceinds(subnet, sim_dict) + return deltas, subnet, collect(values(sim_dict)) +end + +function insert_deltas(ind1, ind2, subnet1) + intersect_inds = intersect(ind1, ind2) + ind1_unique = collect(setdiff(ind1, intersect_inds)) + ind2_unique = collect(setdiff(ind2, intersect_inds)) + outinds = vcat(ind1_unique, ind2_unique) + # look at intersect_inds + deltas = [] + if length(intersect_inds) >= 1 + deltas, subnet1, siminds = split_deltas(intersect_inds, subnet1) + outinds = vcat(outinds, intersect_inds, siminds) + end + return deltas, outinds, subnet1 +end diff --git a/src/ApproximateTNContraction/contract/tree_utils.jl b/src/ApproximateTNContraction/contract/tree_utils.jl new file mode 100644 index 00000000..196a45d7 --- /dev/null +++ b/src/ApproximateTNContraction/contract/tree_utils.jl @@ -0,0 +1,104 @@ + +# merge two trees +# new tree: +# s +# / \ +# t1 t2 +function merge_tree(t1::Vector, t2::Vector; append=false) + if t2 == [] + return t1 + end + if t1 == [] + return t2 + end + if isleaf(t1) && isleaf(t2) + return [t1, t2] + end + if isleaf(t1) + return append ? [t1, t2...] : [t1, t2] + end + if isleaf(t2) + return append ? [t1..., t2] : [t1, t2] + end + return append ? [t1..., t2...] : [t1, t2] +end + +function isleaf(tree::Vector) + if tree == [] + @info "tree is empty" + return false + end + if all(v -> !(v isa Vector), tree) + return true + end + return false +end + +# get the subtree of tree that is in the subset +# example: +# subtree([[1, 2], [3, 4]], [1, 3]) = ([[1], [3]]) +function subtree(tree::Vector, subset::Union{Vector,Tuple}) + if tree == [] + return [] + end + if isleaf(tree) + return intersect(tree, subset) + end + tree = [subtree(i, subset) for i in tree] + tree = filter(t -> t != [], tree) + if length(tree) == 1 && tree[1] isa Vector + return tree[1] + end + return tree +end + +# vectorize a tree +# example: [[1,2], [3,4]] = [1, 2, 3, 4] +function vectorize(tree) + @assert tree != [] + if !(tree isa Vector) + return [tree] + end + return mapreduce(vectorize, vcat, tree) +end + +# example: [[[1,2], [3,4]], [[5,6], [7,8]]] = [[1,2], [3,4], [5,6], [7,8]] +function get_leaves(tree::Vector) + @timeit timer "get_leaves" begin + if !(tree isa Vector{<:Vector}) + return [tree] + end + return mapreduce(get_leaves, vcat, tree) + end +end + +function line_to_tree(line::Vector) + @timeit timer "line_to_tree" begin + if length(line) == 1 && line[1] isa Vector + return line[1] + end + if length(line) <= 2 + return line + end + return [line_to_tree(line[1:(end - 1)]), line[end]] + end +end + +function topo_sort(tn; type=Vector, leaves=[]) + @timeit timer "topo_sort" begin + topo_order = [] + topo_sort_dfs!(tn, topo_order, leaves, type) + return topo_order + end +end + +function topo_sort_dfs!(tn, topo_order, leaves, type) + #Post-order DFS + if (tn in leaves) || !(tn isa type) + return nothing + end + for subtn in tn + topo_sort_dfs!(subtn, topo_order, leaves, type) + end + return append!(topo_order, [tn]) +end diff --git a/src/ApproximateTNContraction/contract/union_find.jl b/src/ApproximateTNContraction/contract/union_find.jl new file mode 100644 index 00000000..0ae3a4dc --- /dev/null +++ b/src/ApproximateTNContraction/contract/union_find.jl @@ -0,0 +1,28 @@ +struct UF + parent_map::Dict +end + +function UF(values::Vector) + parent_map = Dict() + for value in values + parent_map[value] = value + end + return UF(parent_map) +end + +function root(uf::UF, n) + while uf.parent_map[n] != n + n = uf.parent_map[n] + end + return n +end + +function connect(uf, n1, n2) + rootn1 = root(uf, n1) + rootn2 = root(uf, n2) + if rootn1 == rootn2 + # Already connected + return nothing + end + return uf.parent_map[rootn1] = rootn2 +end diff --git a/src/ApproximateTNContraction/interfaces/sweep_contractor.jl b/src/ApproximateTNContraction/interfaces/sweep_contractor.jl new file mode 100644 index 00000000..e62a3a40 --- /dev/null +++ b/src/ApproximateTNContraction/interfaces/sweep_contractor.jl @@ -0,0 +1,33 @@ +using SweepContractor +using SweepContractor: TensorNetwork, LabelledTensorNetwork + +function ITensor_networks(TN::TensorNetwork) + index_dict = Dict() + function itensor(i, t) + # Construct indices + inds = [] + for (dim, j) in enumerate(t.adj) + label = sort([i, j]) + if !haskey(index_dict, label) + s = size(t.arr)[dim] + index_dict[label] = Index(s, string(label)) + end + push!(inds, index_dict[label]) + end + # build the tensor + return ITensor(t.arr, inds...) + end + inetwork = [itensor(i, t) for (i, t) in enumerate(TN)] + return inetwork +end + +function ITensor_networks(LTN::LabelledTensorNetwork) + return ITensor_networks(SweepContractor.delabel(LTN)) +end + +function line_network(network::Vector) + if length(network) <= 2 + return network + end + return [line_network(network[1:(end - 1)]), [network[end]]] +end diff --git a/src/ApproximateTNContraction/models/hamiltonians.jl b/src/ApproximateTNContraction/models/hamiltonians.jl new file mode 100644 index 00000000..ff132677 --- /dev/null +++ b/src/ApproximateTNContraction/models/hamiltonians.jl @@ -0,0 +1,144 @@ +using ITensors +include("../networks/lattices.jl") + +struct LocalMPO + mpo::MPO + coord1::Tuple{<:Integer,<:Integer} + coord2::Tuple{<:Integer,<:Integer} +end + +struct LineMPO + mpo::MPO + coord::Union{Tuple{Colon,<:Integer},Tuple{<:Integer,Colon}} +end + +# Transverse field +# The critical point is h = 1.0 +# This is the most challenging part of the model for DMRG +function mpo(::Model"tfim", sites::Matrix{<:Index}; h::Float64) + Ny, Nx = size(sites) + sites_vec = reshape(sites, Nx * Ny) + lattice = square_lattice(Nx, Ny; yperiodic=false) + + opsum = OpSum() + for b in lattice + opsum += -1, "X", b.s1, "X", b.s2 + end + for i in 1:(Nx * Ny) + opsum += h, "Z", i + end + return MPO(opsum, sites_vec) +end + +function localham_term( + ::Model"tfim", + sites::Matrix{<:Index}, + bond::Tuple{Tuple{<:Integer,<:Integer},Tuple{<:Integer,<:Integer}}; + h::Float64, +) + Ny, Nx = size(sites) + coord1, coord2 = bond + opsum = OpSum() + opsum += -1, "X", 1, "X", 2 + if coord2[1] == coord1[1] + 1 + opsum += h, "Z", 1 + end + if coord2[1] == coord1[1] + 1 && coord2[1] == Ny + opsum += h, "Z", 2 + end + mpo = MPO(opsum, [sites[coord1...], sites[coord2...]]) + return LocalMPO(mpo, coord1, coord2) +end + +function localham(m::Model, sites; kwargs...) + Ny, Nx = size(sites) + lattice = Square((Ny, Nx)) + bds = bonds(lattice; periodic=false) + return [localham_term(m, sites, bond; kwargs...) for bond in bds] +end + +function localham_term( + ::Model"tfim", sites::Matrix{<:Index}, bond::Tuple{Colon,<:Integer}; h::Float64 +) + Ny, Nx = size(sites) + opsum = OpSum() + for i in 1:(Ny - 1) + opsum += -1, "X", i, "X", i + 1 + opsum += h, "Z", i + end + opsum += h, "Z", Ny + return LineMPO(MPO(opsum, sites[bond...]), bond) +end + +function localham_term( + ::Model"tfim", sites::Matrix{<:Index}, bond::Tuple{<:Integer,Colon}; h::Float64 +) + Ny, Nx = size(sites) + opsum = OpSum() + for i in 1:(Nx - 1) + opsum += -1, "X", i, "X", i + 1 + end + return LineMPO(MPO(opsum, sites[bond...]), bond) +end + +function lineham(m::Model, sites; kwargs...) + Ny, Nx = size(sites) + lattice = Square((Ny, Nx)) + bonds_row = [(i, :) for i in 1:Ny] + bonds_column = [(:, i) for i in 1:Nx] + bds = vcat(bonds_row, bonds_column) + return [localham_term(m, sites, bond; kwargs...) for bond in bds] +end + +# Check that the local Hamiltonian is the same as the MPO +function checkham(Hlocal::Array{LocalMPO}, H, sites) + @disable_warn_order begin + Ny, Nx = size(sites) + lattice = Square((Ny, Nx)) + bds = bonds(lattice; periodic=false) + Hlocal_full = ITensor() + for (i, bond) in enumerate(bds) + Hlocalterm_full = prod(Hlocal[i].mpo) + for y in 1:Ny + for x in 1:Nx + if !((y, x) in bond) + Hlocalterm_full *= op("Id", vec(sites), (x - 1) * Ny + y) + end + end + end + Hlocal_full += Hlocalterm_full + end + @show norm(Hlocal_full - prod(H)) + end + return isapprox(norm(Hlocal_full), norm(prod(H))) +end + +function checkham(Hline::Array{LineMPO}, H, sites) + @disable_warn_order begin + Ny, Nx = size(sites) + Hlocal_full = ITensor() + for h in Hline + h_full = prod(h.mpo) + if h.coord[1] isa Colon + for y in 1:Ny + for x in 1:Nx + if x != h.coord[2] + h_full *= op("Id", vec(sites), (x - 1) * Ny + y) + end + end + end + else + for y in 1:Ny + for x in 1:Nx + if y != h.coord[1] + h_full *= op("Id", vec(sites), (x - 1) * Ny + y) + end + end + end + end + Hlocal_full += h_full + end + @show norm(Hlocal_full - prod(H)) + end + return isapprox(norm(Hlocal_full), norm(prod(H))) +end diff --git a/src/ApproximateTNContraction/models/ising_classical_2d.jl b/src/ApproximateTNContraction/models/ising_classical_2d.jl new file mode 100644 index 00000000..dd6b9be2 --- /dev/null +++ b/src/ApproximateTNContraction/models/ising_classical_2d.jl @@ -0,0 +1,95 @@ +using ITensors + +function local_boltzmann_weight(m::String, args...; kwargs...) + return local_boltzmann_weight(Model(m), args...; kwargs...) +end + +f(λ₊, λ₋) = [ + (λ₊ + λ₋)/2 (λ₊ - λ₋)/2 + (λ₊ - λ₋)/2 (λ₊ + λ₋)/2 +] + +function sqrt_bond_matrix(; β::Real, J::Real=1.0) + # Alternative method + #Q = [exp(β * J) exp(-β * J); exp(-β * J) exp(β * J)] + #return √Q + λ₊ = √(exp(β * J) + exp(-β * J)) + λ₋ = √(exp(β * J) - exp(-β * J)) + return f(λ₊, λ₋) +end + +# The local Boltzmann weight for the Ising +# model in 1 dimension +function local_boltzmann_weight( + ::Model"ising", ::Val{1}; β::Real, J::Real=1.0, sz::Bool=false +) + d = 2 # local dimension of the Ising local Boltzmann factor + s, s′ = Index.((d, d)) + T = ITensor(s, s′) + for i in 1:d + T[i, i] = 1.0 + end + if sz + T[1, 1] = -T[1, 1] + end + s̃, s̃′ = sim.((s, s′)) + T̃ = T * δ(s, s̃) * δ(s′, s̃′) + sqrtQ = sqrt_bond_matrix(; β=β, J=J) + @show sqrtQ + X = itensor(vec(sqrtQ), s̃, s) + X′ = itensor(vec(sqrtQ), s̃′, s′) + return array(permute(T̃ * X′ * X, s, s′)) +end + +# The local Boltzmann weight for the Ising +# model in 2 dimensions +function local_boltzmann_weight( + ::Model"ising", ::Val{2}; β::Real, J::Real=1.0, sz::Bool=false +) + d = 2 # local dimension of the Ising local Boltzmann factor + sₕ, sₕ′ = Index.((d, d)) + sᵥ, sᵥ′ = Index.((d, d)) + @assert dim(sₕ) == dim(sᵥ) + d = dim(sₕ) + T = ITensor(sₕ, sₕ′, sᵥ, sᵥ′) + for i in 1:d + T[i, i, i, i] = 1.0 + end + if sz + T[1, 1, 1, 1] = -T[1, 1, 1, 1] + end + s̃ₕ, s̃ₕ′, s̃ᵥ, s̃ᵥ′ = sim.((sₕ, sₕ′, sᵥ, sᵥ′)) + T̃ = T * δ(sₕ, s̃ₕ) * δ(sₕ′, s̃ₕ′) * δ(sᵥ, s̃ᵥ) * δ(sᵥ′, s̃ᵥ′) + X = sqrt_bond_matrix(; β=β, J=J) + Xₕ = itensor(vec(X), s̃ₕ, sₕ) + Xₕ′ = itensor(vec(X), s̃ₕ′, sₕ′) + Xᵥ = itensor(vec(X), s̃ᵥ, sᵥ) + Xᵥ′ = itensor(vec(X), s̃ᵥ′, sᵥ′) + return array(permute(T̃ * Xₕ′ * Xᵥ′ * Xₕ * Xᵥ, sₕ, sₕ′, sᵥ, sᵥ′)) +end + +function mpo_itensor( + m::Model, pair_sₕ::Pair{<:Index,<:Index}, pair_sᵥ::Pair{<:Index,<:Index}; kwargs... +) + sₕ, sₕ′ = pair_sₕ + sᵥ, sᵥ′ = pair_sᵥ + return itensor(mpo_array(m; kwargs...), sₕ, sₕ′, sᵥ, sᵥ′) +end + +critical_point(::Model"ising") = 0.5 * log(√2 + 1) + +function free_energy(::Model"ising"; β::Real, J::Real=1.0) + k = β * J + c = cosh(2 * k) + s = sinh(2 * k) + xmin = 0.0 + xmax = π + integrand(x) = log(c^2 + √(s^4 + 1 - 2 * s^2 * cos(x))) + integral, err = quadgk(integrand, xmin, xmax)::Tuple{Float64,Float64} + return -(log(2) + integral / π) / (2 * β) +end + +function magnetization(::Model"ising"; β::Real) + β > βc && return (1 - sinh(2 * β)^(-4))^(1 / 8) + return 0.0 +end diff --git a/src/ApproximateTNContraction/models/models.jl b/src/ApproximateTNContraction/models/models.jl new file mode 100644 index 00000000..241d5792 --- /dev/null +++ b/src/ApproximateTNContraction/models/models.jl @@ -0,0 +1,19 @@ +module Models + +export Model, critical_point, local_boltzmann_weight, mpo, localham, checklocalham + +struct Model{model} end + +Model(s::AbstractString) = Model{Symbol(s)}() + +# For notation: +# Model"tfim" == Model{:tfim} +# Model"heisenberg" == Model{:heisenberg} +macro Model_str(s) + return :(Model{$(Expr(:quote, Symbol(s)))}) +end + +include("ising_classical_2d.jl") +include("hamiltonians.jl") + +end diff --git a/src/ApproximateTNContraction/networks/3d_classical_ising.jl b/src/ApproximateTNContraction/networks/3d_classical_ising.jl new file mode 100644 index 00000000..bb1dd2ba --- /dev/null +++ b/src/ApproximateTNContraction/networks/3d_classical_ising.jl @@ -0,0 +1,54 @@ +using ITensors + +function ising_mpo( + pairs::Vector{<:Pair{<:Index,<:Index}}, β::Real, J::Real=1.0; sz::Bool=false +) + d = dim(pairs[1].first) + for p in pairs + @assert d == dim(p.first) == dim(p.second) + end + indices = mapreduce(p -> [p.first, p.second], vcat, pairs) + order = length(indices) + T = ITensor(indices...) + for i in 1:d + index = [i for _ in 1:order] + T[index...] = 1.0 + end + if sz + index = [1 for _ in 1:order] + T[index...] = -1.0 + end + simindices = map(sim, indices) + for i in 1:length(indices) + T = T * delta(indices[i], simindices[i]) + end + + f(λ₊, λ₋) = [ + (λ₊ + λ₋)/2 (λ₊ - λ₋)/2 + (λ₊ - λ₋)/2 (λ₊ + λ₋)/2 + ] + λ₊ = √(exp(β * J) + exp(-β * J)) + λ₋ = √(exp(β * J) - exp(-β * J)) + X = f(λ₊, λ₋) + + for i in 1:length(indices) + Xh = itensor(vec(X), simindices[i], indices[i]) + T = T * Xh + end + return T +end + +function isingTensor(inds::Vector) + npairs = floor(Int, length(inds) / 2) + @assert length(inds) == 2 * npairs + βc = 0.5 * log(√2 + 1) + β = 1.0 * βc + pairs = [Pair(inds[2 * i - 1], inds[2 * i]) for i in 1:npairs] + return ising_mpo(pairs, β) +end + +function ising_partition(N, d=2) + tn_inds = inds_network(N...; linkdims=d, periodic=true) + tn = map(inds -> isingTensor(inds), tn_inds) + return project_boundary(tn) +end diff --git a/src/ApproximateTNContraction/networks/inds_network.jl b/src/ApproximateTNContraction/networks/inds_network.jl new file mode 100644 index 00000000..fac65e91 --- /dev/null +++ b/src/ApproximateTNContraction/networks/inds_network.jl @@ -0,0 +1,68 @@ + +function coordinate_tag(n) + str = replace("$n", ")" => "") + str = replace(str, "(" => "") + str = replace(str, " " => "") + if length(n) > 1 + str = replace(str, "," => ".") + else + str = replace(str, "," => "") + end + return str +end + +function link_tag(n1, n2) + link_string = "$(coordinate_tag(n1))↔$(coordinate_tag(n2))" + start_ind = nextind(link_string, 0, 1) + stop_ind = min(ncodeunits(link_string), nextind(link_string, 0, 16)) + link_string = link_string[start_ind:stop_ind] + return TagSet(link_string) +end + +function ITensors.linkinds(lattice::HyperCubic; linkdims, addtags=ts"") + dims = size(lattice) + N = length(dims) + linkinds_dict = Dict{Edge{N},Index{typeof(linkdims)}}() + for n in sites(lattice), edge_n in incident_edges(lattice, n) + l = Index(linkdims; tags=ITensors.addtags(link_tag(edge_n.edge...), addtags)) + get!(linkinds_dict, edge_n, l) + end + return linkinds_dict +end + +function get_link_ind(linkinds_dict::Dict, edge::Edge, site::Tuple) + l = linkinds_dict[edge] + return is_in_edge(site, edge) ? dag(l) : l +end + +# A network of link indices for a HyperCubic lattice, with +# no site indices. +function inds_network(dims::Int...; linkdims, kwargs...) + site_inds = fill(Index{typeof(linkdims)}[], dims) + return inds_network(site_inds; linkdims=linkdims, kwargs...) +end + +function inds_network(site_inds::Array{<:Index,N}; kwargs...) where {N} + return inds_network(map(x -> [x], site_inds); kwargs...) +end + +# A network of link indices for a HyperCubic lattice, with +# site indices specified. +function inds_network( + site_inds::Array{<:Vector{<:Index},N}; linkdims, addtags=ts"", periodic=true +) where {N} + dims = size(site_inds) + lattice = HyperCubic(dims) + linkinds_dict = linkinds(lattice; linkdims=linkdims, addtags=addtags) + inds = Array{Vector{Index{typeof(linkdims)}},N}(undef, dims) + for n in sites(lattice) + if periodic == true + edges = incident_edges(lattice, n) + else + edges = [e for e in incident_edges(lattice, n) if e.boundary == false] + end + inds_n = [get_link_ind(linkinds_dict, edge_n, n) for edge_n in edges] + inds[n...] = append!(inds_n, site_inds[n...]) + end + return inds +end diff --git a/src/ApproximateTNContraction/networks/itensor_network.jl b/src/ApproximateTNContraction/networks/itensor_network.jl new file mode 100644 index 00000000..514a0350 --- /dev/null +++ b/src/ApproximateTNContraction/networks/itensor_network.jl @@ -0,0 +1,303 @@ + +# +# Some general tools for working with networks of ITensors. +# + +""" + itensor_network(dims::Int...; linkdims) + itensor_network(s::Array{<:Index}; linkdims) + itensor_network(s::Array{<:Vector{<:Index}}; linkdims) + +Create a tensor network on a hypercubic lattice of +dimension `dims` with link dimension `linkdims`. + +Alternatively, specify the site indices with an Array `s`, +in which case the lattice will be of dimension `size(s)`. + +The network will have periodic boundary conditions. +To remove the periodic boundary condiitions, use +the function `project_boundary`. +""" +function itensor_network(dims::Int...; linkdims) + return ITensor.(inds_network(dims...; linkdims=linkdims)) +end + +function itensor_network(s::Array; linkdims) + return ITensor.(inds_network(s; linkdims=linkdims)) +end + +""" + boundary_projectors(tn::Matrix{ITensor}, state=1) + +For a 2D tensor network, return the right and bottom boundary projectors onto +the local state `state`. +""" +function boundary_projectors(tn::Matrix{ITensor}, state=1) + top_row = tn[1, :] + bottom_row = tn[end, :] + left_column = tn[:, 1] + right_column = tn[:, end] + bottom_boundary_inds = commonind.(bottom_row, top_row) + right_boundary_inds = commonind.(right_column, left_column) + ψr = ITensors.state.(right_boundary_inds, state) + ψb = ITensors.state.(bottom_boundary_inds, state) + return ψr, ψb +end + +function boundary_projectors(tn::Array{ITensor,3}, state=1) + top = tn[1, :, :] + bottom = tn[end, :, :] + left = tn[:, 1, :] + right = tn[:, end, :] + front = tn[:, :, 1] + back = tn[:, :, end] + top_inds = commonind.(top, bottom) + left_inds = commonind.(left, right) + front_inds = commonind.(front, back) + + psi_top = ITensors.state.(top_inds, state) + psi_left = ITensors.state.(left_inds, state) + psi_front = ITensors.state.(front_inds, state) + return psi_top, psi_left, psi_front +end + +""" + project_boundary(tn::Matrix{ITensor}, state=1) + +Project the boundary of a periodic 2D tensor network onto +the specified state. +""" +function project_boundary(tn::Matrix{ITensor}, state=1) + Nx, Ny = size(tn) + ψr, ψb = boundary_projectors(tn, state) + for n in 1:Nx + tn[n, 1] = tn[n, 1] * ψr[n] + tn[n, end] = tn[n, end] * dag(ψr[n]) + end + for n in 1:Ny + tn[1, n] = tn[1, n] * ψb[n] + tn[end, n] = tn[end, n] * dag(ψb[n]) + end + return tn +end + +function project_boundary(tn::Array{ITensor,3}, state=1) + Nx, Ny, Nz = size(tn) + psi_top, psi_left, psi_front = boundary_projectors(tn, state) + for j in 1:Ny + for k in 1:Nz + tn[1, j, k] = tn[1, j, k] * psi_top[j, k] + tn[end, j, k] = tn[end, j, k] * psi_top[j, k] + end + end + for i in 1:Nx + for k in 1:Nz + tn[i, 1, k] = tn[i, 1, k] * psi_left[i, k] + tn[i, end, k] = tn[i, end, k] * psi_left[i, k] + end + end + for i in 1:Nx + for j in 1:Ny + tn[i, j, 1] = tn[i, j, 1] * psi_front[i, j] + tn[i, j, end] = tn[i, j, end] * psi_front[i, j] + end + end + return tn +end + +function filter_alllinkinds(f, tn) + linkinds = Dict{Tuple{keytype(tn),keytype(tn)},Vector{indtype(tn)}}() + for n in keys(tn), m in keys(tn) + if f(n, m) + is = commoninds(tn[n], tn[m]) + if !isempty(is) + linkinds[(n, m)] = is + end + end + end + return linkinds +end + +""" + alllinkinds(tn) + +Return a dictionary of all of the link indices of the network. +The link indices are determined by searching through the network +for tensors with indices in common with other tensors, and +the keys of the dictionary store a tuple of the sites with +the common indices. + +Notice that this version will return a dictionary containing +repeated link indices, since the +For example: +```julia +i, j, k, l = Index.((2, 2, 2, 2)) +inds_network = [(i, dag(j)), (j, dag(k)), (k, dag(l)), (l, dag(i))] +tn_network = randomITensor.(inds_network) +links = allinkinds(tn_network) +links[(1, 2)] == (dag(j),) +links[(2, 1)] == (j,) +links[(2, 3)] == (dag(l),) +links[(1, 3)] # Error! In the future this may return an empty Tuple +``` + +Use `inlinkinds` and `outlinkinds` to return dictionaries without +repeats (such as only the link `(2, 1)` and not `(1, 2)` or vice versa). +""" +alllinkinds(tn) = filter_alllinkinds(≠, tn) +inlinkinds(tn) = filter_alllinkinds(>, tn) +outlinkinds(tn) = filter_alllinkinds(<, tn) + +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) + +From a tensor network `tn` and a site/node `n`, determine the neighbors +of the specified tensor `tn[n]` by searching for which other +tensors in the network have indices in common with `tn[n]`. + +Use `inneighbors` and `outneighbors` for directed versions. +""" +function neighbors(tn, n) + return filterneighbors(≠, tn, n) +end +inneighbors(tn, n) = filterneighbors(>, tn, n) +outneighbors(tn, n) = filterneighbors(<, tn, n) + +function mapinds(f, ::typeof(linkinds), tn) + tn′ = copy(tn) + inds_dict = Dict() + for n in keys(tn) + for nn in neighbors(tn, n) + commonindsₙ = commoninds(tn[n], tn[nn]) + newinds = [] + for i in commonindsₙ + if !haskey(inds_dict, i) + inds_dict[i] = f(i) + end + newinds = vcat(newinds, [inds_dict[i]]) + end + tn′[n] = replaceinds(tn′[n], commonindsₙ => newinds) + end + end + return tn′ +end + +function ITensors.prime(::typeof(linkinds), tn, args...) + return mapinds(x -> prime(x, args...), linkinds, tn) +end + +function ITensors.sim(::typeof(linkinds), tn, args...) + return mapinds(x -> sim(x, args...), linkinds, tn) +end + +function ITensors.addtags(::typeof(linkinds), tn, args...) + return mapinds(x -> addtags(x, args...), linkinds, tn) +end + +function ITensors.removetags(::typeof(linkinds), tn, args...) + return mapinds(x -> removetags(x, args...), linkinds, tn) +end + +function ITensors.prime(indices::Array{<:Index,1}, network::Array{ITensor}, n::Integer=1) + function primeinds(tensor) + prime_inds = [ind for ind in inds(tensor) if ind in indices] + if (length(prime_inds) == 0) + return tensor + end + return replaceinds(tensor, prime_inds => prime(prime_inds, n)) + end + return map(x -> primeinds(x), network) +end + +function ITensors.replaceinds( + network::Union{Array{ITensor},Array{OrthogonalITensor}}, sim_dict::Dict +) + if length(network) == 0 + return network + end + indices = collect(keys(sim_dict)) + function siminds(tensor) + sim_inds = [ind for ind in inds(tensor) if ind in indices] + if (length(sim_inds) == 0) + return tensor + end + outinds = map(i -> sim_dict[i], sim_inds) + return replaceinds(tensor, sim_inds => outinds) + end + return map(x -> siminds(x), network) +end + +function ITensors.commoninds(n1::Array{ITensor}, n2::Array{ITensor}) + return mapreduce(a -> commoninds(a...), vcat, zip(n1, n2)) +end + +# Compute the sets of combiners that combine the link indices +# of the tensor network so that neighboring tensors only +# share a single larger index. +# Return a dictionary from a site to a combiner. +function combiners(::typeof(linkinds), tn) + Cs = Dict(keys(tn) .=> (ITensor[] for _ in keys(tn))) + for n in keys(tn) + for nn in inneighbors(tn, n) + commonindsₙ = commoninds(tn[n], tn[nn]) + C = combiner(commonindsₙ) + push!(Cs[n], C) + push!(Cs[nn], dag(C)) + end + end + return Cs +end + +# Insert the gauge tensors `gauge` into the links of the tensor +# network `tn`. +function insert_gauge(tn, gauge) + tn′ = copy(tn) + for n in keys(gauge) + for g in gauge[n] + if hascommoninds(tn′[n], g) + tn′[n] *= g + end + end + end + return tn′ +end + +# Insert the gauge tensors `gauge` into the links of the sets +# of tensor networks `tn` stored in a NamedTuple. +# TODO: is this used anywhere? +function insert_gauge(tn::NamedTuple, gauge) + return map(x -> insert_gauge.(x, (gauge,)), tn) +end + +# Split the links of an ITensor network by changing the prime levels +# or tags of pairs of links. +function split_links(H::Union{MPS,MPO}; split_tags=("" => ""), split_plevs=(0 => 1)) + left_tags, right_tags = split_tags + left_plev, right_plev = split_plevs + l = outlinkinds(H) + Hsplit = copy(H) + for bond in keys(l) + n1, n2 = bond + lₙ = l[bond] + left_l_n = prime(addtags(lₙ, left_tags), left_plev) + right_l_n = prime(addtags(lₙ, right_tags), right_plev) + Hsplit[n1] = replaceinds(Hsplit[n1], lₙ => left_l_n) + Hsplit[n2] = replaceinds(Hsplit[n2], lₙ => right_l_n) + end + return Hsplit +end + +function split_links(H::Vector{ITensor}, args...; kwargs...) + return data(split_links(MPS(H), args...; kwargs...)) +end diff --git a/src/ApproximateTNContraction/networks/lattices.jl b/src/ApproximateTNContraction/networks/lattices.jl new file mode 100644 index 00000000..9bbf2bda --- /dev/null +++ b/src/ApproximateTNContraction/networks/lattices.jl @@ -0,0 +1,125 @@ +# +# HyperCubic lattice +# + +# An N-dimensional hypercubic lattice with periodic +# boundary conditions +struct HyperCubic{N} + dims::NTuple{N,Int} +end +Base.size(l::HyperCubic) = l.dims + +const Chain = HyperCubic{1} +const Square = HyperCubic{2} +const Cubic = HyperCubic{3} + +struct Edge{N} + edge::Tuple{NTuple{N,Int},NTuple{N,Int}} + boundary::Bool +end + +Base.getindex(edge::Edge, n::Int) = edge.edge[n] + +Base.reverse(edge::Edge) = Edge(reverse(edge.edge), edge.boundary) + +sites(lattice::HyperCubic) = (Tuple(s) for s in CartesianIndices(axes(lattice))) + +function is_in_edge(site::Tuple, edge::Edge) + if site == edge[1] + return false + elseif site == edge[2] + return true + else + error("Site $site is not incident to edge $edge") + end +end + +function onehot_tuple(n::Integer, length::Val{N}) where {N} + return ntuple(i -> i == n ? 1 : 0, Val(N)) +end + +# Obtain the neighbor in dimension `dim` in direction `dir`, for example: +# neighbor((2, 2), 2, 1) == (2, 3) +# neighbor((2, 2), 2, -1) == (2, 1) +# neighbor((2, 2), 1, -1) == (1, 2) +# neighbor((2, 3), 2, 1, lattice_size=(3, 3)) == (2, 1) +function neighbor( + site::NTuple{N,Int}, dim::Int, dir::Int; lattice_size=typemax(eltype(site)) +) where {N} + return map( + (a, b, d) -> mod1(a + dir * b, d), site, onehot_tuple(dim, Val(N)), lattice_size + ) +end + +# Check if the edge connecting to the specified neighbor of the site +# crosses the boundary of the lattice +function isboundary( + site::NTuple{N,Int}, dim::Int, dir::Int; lattice_size=typemax(eltype(site)) +) where {N} + return ((dir == 1) && (site[dim] == lattice_size[dim])) || ((dir == -1) && site[dim] == 1) +end + +# The neighboring sites of the specified site +function filterneighbors( + f, lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false +) 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) + bc_condition = periodic || !(isboundary(site, dim, dir; lattice_size=lattice_size)) + if f(site, site_neighbor) && bc_condition + push!(site_neighbors, site_neighbor) + end + end + return site_neighbors +end + +function neighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} + return filterneighbors(≠, lattice, site; periodic=periodic) +end + +function inneighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} + return filterneighbors(>, lattice, site; periodic=periodic) +end + +function outneighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} + return filterneighbors(<, lattice, site; periodic=periodic) +end + +# All of the edges connected to the vertex `site` +function incident_edges(lattice::HyperCubic{N}, site::NTuple{N,Int}) where {N} + lattice_size = size(lattice) + site_edges = Vector{Edge{N}}() + for dim in 1:N, dir in (-1, 1) + site_neighbor = neighbor(site, dim, dir; lattice_size=lattice_size) + boundary = false + if isboundary(site, dim, dir; lattice_size=lattice_size) + boundary = true + end + edge = (site, site_neighbor) + if dir == -1 + edge = reverse(edge) + end + push!(site_edges, Edge(edge, boundary)) + end + return site_edges +end + +function bonds(lattice::HyperCubic; periodic=false) + return [ + (s, n) for s in sites(lattice) for n in outneighbors(lattice, s; periodic=periodic) + ] +end + +function bonds(lattice::Square, coord::Tuple{Colon,<:Integer}) + rowsize = lattice.dims[1] + colsites = [(i, coord[2]) for i in 1:(rowsize - 1)] + return [(s, (s[1] + 1, s[2])) for s in colsites] +end + +function bonds(lattice::Square, coord::Tuple{<:Integer,Colon}) + colsize = lattice.dims[2] + rowsites = [(coord[1], i) for i in 1:(colsize - 1)] + return [(s, (s[1], s[2] + 1)) for s in rowsites] +end diff --git a/src/ApproximateTNContraction/orthogonal_tensor.jl b/src/ApproximateTNContraction/orthogonal_tensor.jl new file mode 100644 index 00000000..8f7e7b74 --- /dev/null +++ b/src/ApproximateTNContraction/orthogonal_tensor.jl @@ -0,0 +1,47 @@ +mutable struct OrthogonalITensor + tensor::ITensor + ortho_indices::Vector +end + +function OrthogonalITensor(tensor::ITensor) + return OrthogonalITensor(tensor, []) +end + +function orthogonal_tensors(tensors::Vector{ITensor}) + return [OrthogonalITensor(t) for t in tensors] +end + +function get_tensors(ortho_tensors::Vector{OrthogonalITensor}) + return [t.tensor for t in ortho_tensors] +end + +function ITensors.noncommoninds(ortho_tensors::OrthogonalITensor...) + if length(ortho_tensors) == 1 + return collect(inds(ortho_tensors[1])) + end + @info "ortho_tensors are", ortho_tensors + return noncommoninds(get_tensors([ortho_tensors...])...) +end + +function ITensors.inds(ortho_tensor::OrthogonalITensor) + return inds(ortho_tensor.tensor) +end + +function ITensors.replaceinds(ortho_tensor::OrthogonalITensor, change_inds::Pair) + input_inds = change_inds[1] + output_inds = change_inds[2] + input_to_output = Dict{Index,Index}() + for (i_in, i_out) in zip(input_inds, output_inds) + input_to_output[i_in] = i_out + end + new_ortho_indices = [] + for i in ortho_tensor.ortho_indices + if haskey(input_to_output, i) + push!(new_ortho_indices, input_to_output[i]) + else + push!(new_ortho_indices, i) + end + end + new_tensor = replaceinds(ortho_tensor.tensor, change_inds) + return OrthogonalITensor(new_tensor, new_ortho_indices) +end diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 60bcaba0..9962f421 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -8,6 +8,7 @@ using ITensors.ITensorVisualizationCore using MultiDimDictionaries using NamedGraphs using Requires +using Reexport using Suppressor # TODO: export from ITensors @@ -116,9 +117,15 @@ include(joinpath("treetensornetwork", "treetensornetwork.jl")) include("exports.jl") +include("ApproximateTNContraction/ApproximateTNContraction.jl") + function __init__() - @require KaHyPar="2a6221f6-aa48-11e9-3542-2d9e0ef01880" include(joinpath("requires", "kahypar.jl")) - @require Metis="2679e427-3c69-5b7f-982b-ece356f1e94b" include(joinpath("requires", "metis.jl")) + @require KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" include( + joinpath("requires", "kahypar.jl") + ) + @require Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" include( + joinpath("requires", "metis.jl") + ) end end diff --git a/test/Manifest.toml b/test/Manifest.toml deleted file mode 100644 index bdde109c..00000000 --- a/test/Manifest.toml +++ /dev/null @@ -1,556 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.7.2" -manifest_format = "2.0" - -[[deps.AbstractTrees]] -git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.3.4" - -[[deps.Adapt]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.3.3" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" - -[[deps.ArnoldiMethod]] -deps = ["LinearAlgebra", "Random", "StaticArrays"] -git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae" -uuid = "ec485272-7323-5ecc-a04f-4719b315124d" -version = "0.2.0" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.Blosc]] -deps = ["Blosc_jll"] -git-tree-sha1 = "575bdd70552dd9a7eaeba08ef2533226cdc50779" -uuid = "a74b3585-a348-5f62-a45c-50e91977d574" -version = "0.7.2" - -[[deps.Blosc_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Lz4_jll", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "91d6baa911283650df649d0aea7c28639273ae7b" -uuid = "0b7ba130-8d10-5ba8-a3d6-c5182647fed9" -version = "1.21.1+0" - -[[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "6e39c91fb4b84dcb870813c91674bdebb9145895" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.11.5" - -[[deps.ChangesOfVariables]] -deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1" -uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.2" - -[[deps.Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "44c37b4636bc54afac5c574d2d02b625349d6582" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.41.0" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" - -[[deps.Contour]] -deps = ["StaticArrays"] -git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.5.7" - -[[deps.Crayons]] -git-tree-sha1 = "b618084b49e78985ffa8422f32b9838e397b9fc2" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.0" - -[[deps.DataAPI]] -git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.9.0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "3daef5523dd2e769dad2365274f760ff5f282c7d" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.11" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[deps.Dictionaries]] -deps = ["Indexing", "Random"] -git-tree-sha1 = "66bde31636301f4d217a161cabe42536fa754ec8" -uuid = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" -version = "0.3.17" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" - -[[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" - -[[deps.EarCut_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" -uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.2.3+0" - -[[deps.ExprTools]] -git-tree-sha1 = "24565044e60bc48a7562e75bcf14f084901dc0b6" -uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.7" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "67551df041955cc6ee2ed098718c8fcd7fc7aebe" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.12.0" - -[[deps.GeometryBasics]] -deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c" -uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.1" - -[[deps.Graphs]] -deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "d727758173afef0af878b29ac364a0eca299fc6b" -uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.5.1" - -[[deps.HDF5]] -deps = ["Blosc", "Compat", "HDF5_jll", "Libdl", "Mmap", "Random", "Requires"] -git-tree-sha1 = "698c099c6613d7b7f151832868728f426abe698b" -uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -version = "0.15.7" - -[[deps.HDF5_jll]] -deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "bab67c0d1c4662d2c4be8c6007751b0b6111de5c" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.12.1+0" - -[[deps.ITensorNetworks]] -deps = ["ITensors"] -git-tree-sha1 = "aa46404f09d7107f40cc068b2b2dfcd947f12b48" -repo-rev = "main" -repo-url = "https://github.com/mtfishman/ITensorNetworks.jl" -uuid = "2919e153-833c-4bdc-8836-1ea460a35fc7" -version = "0.1.0" - -[[deps.ITensorUnicodePlots]] -deps = ["Graphs", "ITensorVisualizationBase", "ITensors", "LinearAlgebra", "NetworkLayout", "Reexport", "Statistics", "UnicodePlots"] -git-tree-sha1 = "fd73ee12c4ecf442360719b9ecf02b28c1500657" -uuid = "73163f41-4a9e-479f-8353-73bf94dbd758" -version = "0.1.0" - -[[deps.ITensorVisualizationBase]] -deps = ["AbstractTrees", "Compat", "GeometryBasics", "Graphs", "ITensors", "LinearAlgebra", "MetaGraphs", "NetworkLayout", "SparseArrays", "Statistics"] -git-tree-sha1 = "fb3d48ecaff23a857b2525fed73217d737f92811" -uuid = "cd2553d2-8bef-4d93-8a38-c62f17d5ad23" -version = "0.1.0" - -[[deps.ITensors]] -deps = ["ChainRulesCore", "Compat", "Dictionaries", "HDF5", "KrylovKit", "LinearAlgebra", "LinearMaps", "NDTensors", "PackageCompiler", "Pkg", "Printf", "Random", "Requires", "SerializedElementArrays", "StaticArrays", "Strided", "TimerOutputs", "TupleTools", "Zeros", "ZygoteRules"] -git-tree-sha1 = "8e99d15c467e62cc290e10438c7ebb05052aac74" -uuid = "9136182c-28ba-11e9-034c-db9fb085ebd5" -version = "0.2.12" - -[[deps.Indexing]] -git-tree-sha1 = "ce1566720fd6b19ff3411404d4b977acd4814f9f" -uuid = "313cdc1a-70c2-5d6a-ae34-0150d3930a38" -version = "1.1.1" - -[[deps.Inflate]] -git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c" -uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.2" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "a7254c0acd8e62f1ac75ad24d5db43f5f19f3c65" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.2" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.1" - -[[deps.IterTools]] -git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.4.0" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLD2]] -deps = ["DataStructures", "FileIO", "MacroTools", "Mmap", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"] -git-tree-sha1 = "09ef0c32a26f80b465d808a1ba1e85775a282c97" -uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.17" - -[[deps.JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "22df5b96feef82434b07327e2d3c770a9b21e023" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.0" - -[[deps.KrylovKit]] -deps = ["LinearAlgebra", "Printf"] -git-tree-sha1 = "0328ad9966ae29ccefb4e1b9bfd8c8867e4360df" -uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" -version = "0.5.3" - -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" - -[[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.LinearMaps]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "dbb14c604fc47aa4f2e19d0ebb7b6416f3cfa5f5" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.5.1" - -[[deps.LogExpFunctions]] -deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "e5718a00af0ab9756305a0392832c8952c7426c1" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.6" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.Lz4_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "5d494bc6e85c4c9b626ee0cab05daa4085486ab1" -uuid = "5ced341a-0733-55b8-9ab6-a4889d929147" -version = "1.9.3+0" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.9" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" - -[[deps.MetaGraphs]] -deps = ["Graphs", "JLD2", "Random"] -git-tree-sha1 = "2af69ff3c024d13bde52b34a2a7d6887d4e7b438" -uuid = "626554b9-1ddb-594c-aa3c-2596fe9399a5" -version = "0.7.1" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.2" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" - -[[deps.NDTensors]] -deps = ["Compat", "Dictionaries", "HDF5", "LinearAlgebra", "Random", "Requires", "StaticArrays", "Strided", "TimerOutputs", "TupleTools"] -git-tree-sha1 = "061056112183f05acee6afff89ddf8b77f9c88d8" -uuid = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" -version = "0.1.33" - -[[deps.NetworkLayout]] -deps = ["GeometryBasics", "LinearAlgebra", "Random", "Requires", "SparseArrays"] -git-tree-sha1 = "cac8fc7ba64b699c678094fa630f49b80618f625" -uuid = "46757867-2c16-5918-afeb-47bfcb05e46a" -version = "0.4.4" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "648107615c15d4e09f7eca16307bc821c1f718d8" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.13+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" - -[[deps.PackageCompiler]] -deps = ["Artifacts", "LazyArtifacts", "Libdl", "Pkg", "RelocatableFolders", "UUIDs"] -git-tree-sha1 = "a16924b37299cc7d6106fac255b44a8c79c7c21f" -uuid = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d" -version = "1.7.7" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "2cf929d64681236a2e074ffafb8d568733d2e6af" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.2.3" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA", "Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "0.1.3" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[deps.Scratch]] -deps = ["Dates"] -git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.1.0" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.SerializedElementArrays]] -deps = ["Serialization"] -git-tree-sha1 = "8e73e49eaebf73486446a3c1eede403bff259826" -uuid = "d3ce8812-9567-47e9-a7b5-65a6d70a3065" -version = "0.1.0" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.1" - -[[deps.SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "2ae4fe21e97cd13efd857462c1869b73c9f61be3" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.3.2" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.StatsAPI]] -git-tree-sha1 = "d88665adc9bcf45903013af0982e2fd05ae3d0a6" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.2.0" - -[[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "51383f2d367eb3b444c961d485c565e4c0cf4ba0" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.14" - -[[deps.Strided]] -deps = ["LinearAlgebra", "TupleTools"] -git-tree-sha1 = "4d581938087ca90eab9bd4bb6d270edaefd70dcd" -uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" -version = "1.1.2" - -[[deps.StructArrays]] -deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] -git-tree-sha1 = "d21f2c564b21a202f4677c0fba5b5ee431058544" -uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.4" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] -git-tree-sha1 = "bb1064c9a84c52e277f1096cf41434b675cd368b" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.6.1" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.TimerOutputs]] -deps = ["ExprTools", "Printf"] -git-tree-sha1 = "a5aed757f65c8a1c64503bc4035f704d24c749bf" -uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.14" - -[[deps.TranscodingStreams]] -deps = ["Random", "Test"] -git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.9.6" - -[[deps.TupleTools]] -git-tree-sha1 = "3c712976c47707ff893cf6ba4354aa14db1d8938" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.3.0" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.UnicodePlots]] -deps = ["Contour", "Crayons", "Dates", "SparseArrays", "StatsBase"] -git-tree-sha1 = "0efb50275f42f2795b771a4ad2c339db9760fd98" -uuid = "b8865327-cd53-5732-bb35-84acbb429228" -version = "2.6.0" - -[[deps.Zeros]] -deps = ["Test"] -git-tree-sha1 = "7eb4fd47c304c078425bf57da99a56606150d7d4" -uuid = "bd1ec220-6eb4-527a-9b49-e79c3db6233b" -version = "0.3.0" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" - -[[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.0+0" - -[[deps.ZygoteRules]] -deps = ["MacroTools"] -git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0" -uuid = "700de1a5-db45-46bc-99cf-38207098b444" -version = "0.2.2" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/test/Project.toml b/test/Project.toml index 6aa6fd37..a6fccf1a 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,3 +4,4 @@ ITensorUnicodePlots = "73163f41-4a9e-479f-8353-73bf94dbd758" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" diff --git a/test/approximate_tn_contraction/cache.jl b/test/approximate_tn_contraction/cache.jl new file mode 100644 index 00000000..bcbb0fd6 --- /dev/null +++ b/test/approximate_tn_contraction/cache.jl @@ -0,0 +1,17 @@ +using ITensors +using ITensorNetworks.ApproximateTNContraction: IndexGroup, get_igs_cache_info + +@testset "test get_igs_cache_info" begin + i = IndexGroup([Index(2, "i")]) + j = IndexGroup([Index(3, "j")]) + k = IndexGroup([Index(2, "k")]) + l = IndexGroup([Index(4, "l")]) + m = IndexGroup([Index(5, "m")]) + n = IndexGroup([Index(5, "n")]) + + type = Vector{IndexGroup} + igs_list = [type([l, k, i, m, n]), type([i, j, k]), type([l, j, m, n])] + contract_igs_list = [type([m]), type([j]), type([j])] + out = get_igs_cache_info(igs_list, contract_igs_list) + @test out == ([k, i, m], [l], [n]) +end diff --git a/test/approximate_tn_contraction/indexgroup.jl b/test/approximate_tn_contraction/indexgroup.jl new file mode 100644 index 00000000..8d35d44b --- /dev/null +++ b/test/approximate_tn_contraction/indexgroup.jl @@ -0,0 +1,78 @@ +using ITensors +using ITensorNetworks.ApproximateTNContraction: + IndexGroup, get_index_groups, get_leaves, neighbor_index_groups +using ITensorNetworks.ApproximateTNContraction: + inds_network, + line_network, + IndexAdjacencyTree, + topo_sort, + get_ancestors, + generate_adjacency_tree, + minswap_adjacency_tree!, + minswap_adjacency_tree, + approximate_contract + +# @testset "test generate_adjacency_tree" begin +# N = (3, 3) +# tn_inds = inds_network(N...; linkdims=2, periodic=false) +# tn = vec(map(inds -> randomITensor(inds...), tn_inds)) +# ctree = line_network(tn) +# tn_leaves = get_leaves(ctree) +# ctrees = topo_sort(ctree; leaves=tn_leaves) +# ctree_to_igs = Dict{Vector,Vector{IndexGroup}}() +# index_groups = get_index_groups(ctree) +# for c in vcat(tn_leaves, ctrees) +# ctree_to_igs[c] = neighbor_index_groups(c, index_groups) +# end +# ctree_to_ancestors = get_ancestors(ctree) +# adj_tree1 = generate_adjacency_tree( +# tn_leaves[4], ctree_to_ancestors[tn_leaves[4]], ctree_to_igs +# ) +# adj_tree2 = generate_adjacency_tree( +# ctrees[2], ctree_to_ancestors[ctrees[2]], ctree_to_igs +# ) +# for adj_tree in [adj_tree1, adj_tree1] +# @test length(adj_tree.children) == 3 +# @test adj_tree.fixed_order = true +# c1, c2, c3 = adj_tree.children +# @test length(c1.children) == 1 +# @test length(c2.children) == 2 +# @test length(c3.children) == 1 +# end +# end + +# @testset "test minswap_adjacency_tree!" begin +# i = IndexGroup([Index(2, "i")]) +# j = IndexGroup([Index(3, "j")]) +# k = IndexGroup([Index(2, "k")]) +# l = IndexGroup([Index(4, "l")]) +# m = IndexGroup([Index(5, "m")]) +# n = IndexGroup([Index(5, "n")]) +# I = IndexAdjacencyTree(i) +# J = IndexAdjacencyTree(j) +# K = IndexAdjacencyTree(k) +# L = IndexAdjacencyTree(l) +# M = IndexAdjacencyTree(m) +# N = IndexAdjacencyTree(n) +# JKL = IndexAdjacencyTree([J, K, L], false, false) +# tree = IndexAdjacencyTree([I, JKL, M], false, false) +# tree_copy = copy(tree) +# tree2 = IndexAdjacencyTree([i, k, m, j, l], true, true) +# nswaps = minswap_adjacency_tree!(tree, tree2) +# @test nswaps == 1 +# @test tree.children == [i, m, k, j, l] +# @test tree.fixed_direction && tree.fixed_order +# # test minswap_adjacency_tree +# tree3 = IndexAdjacencyTree([i, k, n, m], true, true) +# tree4 = IndexAdjacencyTree([l, n, j], true, true) +# out = minswap_adjacency_tree(tree_copy, tree3, tree4) +# @test out.children in [[i, m, k, j, l], [i, m, k, l, j], [m, i, k, j, l], [m, i, k, l, j]] +# end + +@testset "test approximate_contract" begin + N = (4, 4) + tn_inds = inds_network(N...; linkdims=2, periodic=false) + tn = vec(map(inds -> randomITensor(inds...), tn_inds)) + ctree = line_network(tn) + approximate_contract(ctree; cutoff=1e-5, maxdim=20) +end diff --git a/test/approximate_tn_contraction/interface.jl b/test/approximate_tn_contraction/interface.jl new file mode 100644 index 00000000..427a0392 --- /dev/null +++ b/test/approximate_tn_contraction/interface.jl @@ -0,0 +1,170 @@ +using ITensors, Random, SweepContractor, ITensorNetworkAD +using ITensorNetworkAD.Profiler +using ITensorNetworkAD.ITensorNetworks: + timer, ITensor_networks, line_network, TreeTensor, approximate_contract +using ITensorNetworkAD.ITensorAutoHOOT: SubNetwork, batch_tensor_contraction + +include("utils.jl") + +@testset "test the interface" begin + LTN = LabelledTensorNetwork{Char}() + LTN['A'] = Tensor(['D', 'B'], [i^2 - 2j for i in 0:2, j in 0:2], 0, 1) + LTN['B'] = Tensor(['A', 'D', 'C'], [-3^i * j + k for i in 0:2, j in 0:2, k in 0:2], 0, 0) + LTN['C'] = Tensor(['B', 'D'], [j for i in 0:2, j in 0:2], 1, 0) + LTN['D'] = Tensor(['A', 'B', 'C'], [i * j * k for i in 0:2, j in 0:2, k in 0:2], 1, 1) + + sweep = sweep_contract(LTN, 100, 100; fast=true) + out = ldexp(sweep...) + @test isapprox(out, contract(ITensor_networks(LTN))[]) + show(timer) +end + +function lattice(row, column, d) + function build_adj(i, j) + adj = Vector{Int64}() + i > 1 && push!(adj, delabel[(i - 1, j)]) + i < row && push!(adj, delabel[(i + 1, j)]) + j > 1 && push!(adj, delabel[(i, j - 1)]) + j < column && push!(adj, delabel[(i, j + 1)]) + return adj + end + TN = TensorNetwork() + delabel = Dict() + index = 1 + for i in 1:row + ranges = iseven(i) ? (column:-1:1) : (1:column) + for j in ranges + delabel[(i, j)] = index + index += 1 + end + end + for i in 1:row + ranges = iseven(i) ? (column:-1:1) : (1:column) + for j in ranges + adj = build_adj(i, j) + push!(TN, Tensor(adj, randn(d * ones(Int, length(adj))...), i, j)) + end + end + return TN +end + +function get_contracted_peps(LTN, rank, N) + tnet = ITensor_networks(LTN) + tnet_mat = reshape(tnet, N...) + # out_mps = peps_contraction_mpomps(tnet_mat; cutoff=1e-15, maxdim=rank, snake=true) + out = contract_w_sweep(LTN, rank) + return out2 = contract_element_group(tnet, rank) + # out3 = contract_line_group(tnet_mat, rank, N) + # return out, out2[1][], ITensor(out3[1])[], out_mps[] +end + +# @testset "test on 2D grid" begin +# Random.seed!(1234) +# ITensors.set_warn_order(100) +# row, column, d = 8, 8, 2 +# LTN = lattice(row, column, d) + +# out_true, out_element, out_line, out_mps = get_contracted_peps( +# LTN, d^(Int(row / 2)), [row, column] +# ) +# @test abs((out_true - out_element) / out_true) < 1e-3 +# @test abs((out_true - out_line) / out_true) < 1e-3 +# @test abs((out_true - out_mps) / out_true) < 1e-3 +# for rank in [2, 3, 4, 6, 8, 10, 12, 14, 15, 16] +# out, out_element, out_line, out_mps = get_contracted_peps(LTN, rank, [row, column]) +# error_sweepcontractor = abs((out - out_true) / out_true) +# error_element = abs((out_element - out_true) / out_true) +# error_line = abs((out_line - out_true) / out_true) +# error_mps = abs((out_mps - out_true) / out_true) +# print( +# "maxdim, ", +# rank, +# ", error_sweepcontractor, ", +# error_sweepcontractor, +# ", error_element, ", +# error_element, +# ", error_line, ", +# error_line, +# ", error_mps, ", +# error_mps, +# "\n", +# ) +# end +# end + +# @testset "benchmark on 2D grid" begin +# Random.seed!(1234) +# ITensors.set_warn_order(100) +# row, column, d, rank = 15, 15, 10, 10 +# LTN = lattice(row, column, d) +# # warm-up +# get_contracted_peps(LTN, rank, [row, column]) +# @info "start benchmark on 2D grid" +# do_profile(true) +# for _ in 1:2 +# LTN = lattice(row, column, d) +# get_contracted_peps(LTN, rank, [row, column]) +# end +# profile_exit() +# end + +function cube_3d(L=3, d=2) + function build_adj(i, j, k) + adj = Vector{Int64}() + i > 1 && push!(adj, delabel[(i - 1, j, k)]) + i < L && push!(adj, delabel[(i + 1, j, k)]) + j > 1 && push!(adj, delabel[(i, j - 1, k)]) + j < L && push!(adj, delabel[(i, j + 1, k)]) + k > 1 && push!(adj, delabel[(i, j, k - 1)]) + k < L && push!(adj, delabel[(i, j, k + 1)]) + return adj + end + TN = TensorNetwork() + delabel = Dict() + index = 1 + for i in 1:L + ranges_j = iseven(i) ? (L:-1:1) : (1:L) + for j in ranges_j + ranges_k = iseven((i - 1) * L + j) ? (L:-1:1) : (1:L) + for k in ranges_k + delabel[(i, j, k)] = index + index += 1 + end + end + end + for i in 1:L + ranges_j = iseven(i) ? (L:-1:1) : (1:L) + for j in ranges_j + ranges_k = iseven((i - 1) * L + j) ? (L:-1:1) : (1:L) + for k in ranges_k + adj = build_adj(i, j, k) + newt = Tensor( + adj, randn(d * ones(Int, length(adj))...), i + 0.01 * randn(), j + 0.01 * randn() + ) + push!(TN, newt) + end + end + end + return TN +end + +# TODO +# @testset "test on 3D cube" begin +# Random.seed!(1234) +# ITensors.set_warn_order(100) +# L, d = 3, 2 +# rank = 16 +# TN = cube_3d(L, d) +# out = contract_w_sweep(TN, rank) +# tnet = ITensor_networks(TN) +# out2 = contract_element_group(tnet, rank) + +# do_profile(true) +# for _ in 1:3 +# TN = cube_3d(L, d) +# out = contract_w_sweep(TN, rank) +# tnet = ITensor_networks(TN) +# out2 = contract_element_group(tnet, rank) +# end +# profile_exit() +# end diff --git a/test/approximate_tn_contraction/itensor_network.jl b/test/approximate_tn_contraction/itensor_network.jl new file mode 100644 index 00000000..f05e123c --- /dev/null +++ b/test/approximate_tn_contraction/itensor_network.jl @@ -0,0 +1,52 @@ +using ITensors +using Test +using ITensorNetworks +using ITensorNetworks.ApproximateTNContraction: itensor_network, neighbors + +@testset "itensor_network.jl" begin + @testset "itensor_network from dims" begin + d = (3, 3) + tn = itensor_network(d...; linkdims=3) + @test tn isa Matrix{ITensor} + @test size(tn) == (3, 3) + @test all(ITensors.isemptystorage, tn) + @test hascommoninds(tn[1, 1], tn[1, 2]) + @test hascommoninds(tn[1, 1], tn[2, 1]) + @test !hascommoninds(tn[1, 1], tn[2, 2]) + @test hascommoninds(tn[1, 1], tn[3, 1]) + @test hascommoninds(tn[1, 1], tn[1, 3]) + @test isempty(uniqueinds(tn[2, 2], tn[2, 1], tn[2, 3], tn[1, 2], tn[3, 2])) + @test issetequal( + neighbors(tn, CartesianIndex(1, 2)), + [ + CartesianIndex(1, 1), + CartesianIndex(2, 2), + CartesianIndex(3, 2), + CartesianIndex(1, 3), + ], + ) + end + @testset "itensor_network from siteinds" begin + d = (3, 3) + s = siteinds("S=1/2", d...) + tn = itensor_network(s; linkdims=3) + @test tn isa Matrix{ITensor} + @test size(tn) == (3, 3) + @test all(ITensors.isemptystorage, tn) + @test hascommoninds(tn[1, 1], tn[1, 2]) + @test hascommoninds(tn[1, 1], tn[2, 1]) + @test !hascommoninds(tn[1, 1], tn[2, 2]) + @test hascommoninds(tn[1, 1], tn[3, 1]) + @test hascommoninds(tn[1, 1], tn[1, 3]) + @test uniqueinds(tn[2, 2], tn[2, 1], tn[2, 3], tn[1, 2], tn[3, 2]) == [s[2, 2]] + @test issetequal( + neighbors(tn, CartesianIndex(1, 2)), + [ + CartesianIndex(1, 1), + CartesianIndex(2, 2), + CartesianIndex(3, 2), + CartesianIndex(1, 3), + ], + ) + end +end diff --git a/test/approximate_tn_contraction/lattice.jl b/test/approximate_tn_contraction/lattice.jl new file mode 100644 index 00000000..a4d696be --- /dev/null +++ b/test/approximate_tn_contraction/lattice.jl @@ -0,0 +1,9 @@ +using ITensors +using ITensorNetworks +using ITensorNetworks.ApproximateTNContraction: Square, bonds + +@testset "test lattice" begin + lattice = Square((2, 3)) + bds = bonds(lattice; periodic=false) + @test length(bds) == 7 +end diff --git a/test/approximate_tn_contraction/models.jl b/test/approximate_tn_contraction/models.jl new file mode 100644 index 00000000..34f583f2 --- /dev/null +++ b/test/approximate_tn_contraction/models.jl @@ -0,0 +1,14 @@ +using ITensors +using ITensorNetworks +using ITensorNetworks.ApproximateTNContraction: Models + +@testset "test local hamiltonian builder" begin + Nx = 2 + Ny = 3 + sites = siteinds("S=1/2", Ny, Nx) + H = Models.mpo(Models.Model("tfim"), sites; h=1.0) + H_local = Models.localham(Models.Model("tfim"), sites; h=1.0) + H_line = Models.lineham(Models.Model("tfim"), sites; h=1.0) + @test Models.checkham(H_local, H, sites) + @test Models.checkham(H_line, H, sites) +end diff --git a/test/approximate_tn_contraction/mpstensor.jl b/test/approximate_tn_contraction/mpstensor.jl new file mode 100644 index 00000000..dafcfb03 --- /dev/null +++ b/test/approximate_tn_contraction/mpstensor.jl @@ -0,0 +1,52 @@ +using ITensorNetworkAD +using AutoHOOT, ITensors, Zygote +using ITensorNetworkAD.ITensorNetworks: GeneralMPSTensor + +const itensorah = ITensorNetworkAD.ITensorAutoHOOT + +@testset "test MPSTensor" begin + i = Index(2, "i") + j = Index(3, "j") + k = Index(2, "k") + l = Index(4, "l") + m = Index(5, "m") + + A = randomITensor(i, j, k) + B = randomITensor(k, l, m) + C = randomITensor(i, j, l, m) + mps_A = GeneralMPSTensor(MPS(A, inds(A))) + mps_B = GeneralMPSTensor(MPS(B, inds(B))) + mps_C = GeneralMPSTensor(MPS(C, inds(C))) + + out = A * B + network = [mps_A, mps_B] + nodes, dict = itensorah.generate_einsum_expr([network]) + out_list = itensorah.compute_graph(nodes, dict; cutoff=1e-15, maxdim=1000) + @test isapprox(out, ITensor(out_list[1])) + + out = A * B * C + out2 = contract(mps_A, mps_B, mps_C; cutoff=1e-15, maxdim=1000) + @test isapprox(out, ITensor(out2)) +end + +@testset "test batch_tensor_contraction" begin + i = Index(2, "i") + j = Index(3, "j") + k = Index(2, "k") + A = randomITensor(i, j) + B = randomITensor(j, k) + C = randomITensor(k, i) + + function network(A) + mps_A = GeneralMPSTensor(A; cutoff=1e-15, maxdim=1000) + mps_B = GeneralMPSTensor(B; cutoff=1e-15, maxdim=1000) + mps_C = GeneralMPSTensor(C; cutoff=1e-15, maxdim=1000) + tensor_network = [mps_A, mps_B, mps_C] + out = itensorah.batch_tensor_contraction( + [tensor_network], mps_A; cutoff=1e-15, maxdim=1000 + ) + return sum(out)[] + end + grad_A = gradient(network, A) + @test isapprox(grad_A[1], B * C) +end diff --git a/test/approximate_tn_contraction/runtests.jl b/test/approximate_tn_contraction/runtests.jl new file mode 100644 index 00000000..ac154b07 --- /dev/null +++ b/test/approximate_tn_contraction/runtests.jl @@ -0,0 +1,18 @@ +using ITensorNetworks +using Test + +@testset "ApproximateTNContraction.jl" begin + for filename in [ + "lattice.jl", + "models.jl", + "itensor_network.jl", + "tree.jl", + "indexgroup.jl", + # "cache.jl" + # "treetensor.jl", + # "interface.jl", + ] + println("Running $filename in ApproximateTNContraction.jl") + include(filename) + end +end diff --git a/test/approximate_tn_contraction/tree.jl b/test/approximate_tn_contraction/tree.jl new file mode 100644 index 00000000..816de6c4 --- /dev/null +++ b/test/approximate_tn_contraction/tree.jl @@ -0,0 +1,26 @@ +using TimerOutputs +using ITensorNetworks.ApproximateTNContraction: + timer, merge_tree, subtree, vectorize, topo_sort, get_leaves + +@testset "test merge tree" begin + t1 = [[1], [2], [3]] + t2 = [4, 5, 6] + @test merge_tree(t1, t2; append=true) == [[1], [2], [3], [4, 5, 6]] + @test merge_tree(t1, t2; append=false) == [[[1], [2], [3]], [4, 5, 6]] + @test merge_tree([], [1, 2, 3]; append=false) == [1, 2, 3] +end + +@testset "test subtree and vectorize" begin + t1 = [[[1, 2], [3]], [4]] + subset = [1] + @test subtree(t1, subset) == [1] + @test vectorize(t1) == [1, 2, 3, 4] +end + +@testset "test find topo sort" begin + reset_timer!(timer) + tn = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] + @test length(topo_sort(tn)) == 7 + @test length(topo_sort(tn; leaves=get_leaves(tn))) == 3 + show(timer) +end diff --git a/test/approximate_tn_contraction/treetensor.jl b/test/approximate_tn_contraction/treetensor.jl new file mode 100644 index 00000000..c9f04c66 --- /dev/null +++ b/test/approximate_tn_contraction/treetensor.jl @@ -0,0 +1,373 @@ +using ITensorNetworkAD +using AutoHOOT, ITensors, Zygote +using ITensorNetworkAD.Profiler +using ITensorNetworkAD.ITensorNetworks: + TreeTensor, + tree_approximation, + tree_approximation_cache, + inds_binary_tree, + tree_embedding, + approximate_contract +using ITensorNetworkAD.ITensorNetworks: + inds_network, project_boundary, Models, ising_partition +using ITensorNetworkAD.ITensorAutoHOOT: SubNetwork, batch_tensor_contraction + +const itensorah = ITensorNetworkAD.ITensorAutoHOOT + +include("utils.jl") + +@testset "test TreeTensor" begin + i = Index(2, "i") + j = Index(3, "j") + k = Index(2, "k") + l = Index(4, "l") + m = Index(5, "m") + + A = randomITensor(i, j, k) + B = randomITensor(k, l, m) + C = randomITensor(i, j, l, m) + tree_A = TreeTensor(A) + tree_B = TreeTensor(B) + tree_C = TreeTensor(C) + + out = A * B + network = [tree_A, tree_B] + nodes, dict = itensorah.generate_einsum_expr([network]) + out_list = itensorah.compute_graph(nodes, dict; cutoff=1e-15, maxdim=1000) + @test isapprox(out, ITensor(out_list[1])) + + out = A * B * C + out2 = contract(tree_A, tree_B, tree_C; cutoff=1e-15, maxdim=1000) + @test isapprox(out, ITensor(out2)) +end + +@testset "test batch_tensor_contraction" begin + i = Index(2, "i") + j = Index(3, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + A = randomITensor(i, j) + B = randomITensor(j, k) + C = randomITensor(k, l) + D = randomITensor(l, m) + E = randomITensor(m, i) + + function network(A) + tensor_network = SubNetwork(SubNetwork(A, B, C), D, E) + out = itensorah.batch_tensor_contraction( + TreeTensor, [tensor_network], A; cutoff=1e-15, maxdim=1000, optimize=false + ) + return sum(out)[] + end + grad_A = gradient(network, A) + @test isapprox(grad_A[1], B * C * D * E) +end + +@testset "test tree approximation" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + n = Index(2, "n") + o = Index(2, "o") + p = Index(2, "p") + q = Index(2, "q") + r = Index(2, "r") + s = Index(2, "s") + t = Index(2, "t") + u = Index(2, "u") + A = randomITensor(i, n) + B = randomITensor(j, o) + AB = randomITensor(n, o, r) + C = randomITensor(k, p) + D = randomITensor(l, q) + E = randomITensor(m, u) + CD = randomITensor(p, q, s) + ABCD = randomITensor(r, s, t) + ABCDE = randomITensor(t, u) + btree = [[[[i], [j]], [[k], [l]]], [m]] + tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] + out = tree_approximation(tensors, btree) + embedding = Dict([ + [i] => [A], + [j] => [B], + [k] => [C], + [l] => [D], + [m] => [E], + [[i], [j]] => [AB], + [[k], [l]] => [CD], + [[[i], [j]], [[k], [l]]] => [ABCD], + [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], + ]) + out2 = tree_approximation_cache(embedding, btree) + @test isapprox(contract(out), contract(out2)) + @test isapprox(contract(out), contract(tensors...)) +end + +@testset "test MPS times MPO" begin + N = (10, 3) + linkdim = 3 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim) + tn = map(inds -> randomITensor(inds...), tn_inds) + state = 1 + tn = project_boundary(tn, state) + x, A = tn[:, 1], tn[:, 2] + out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) + out2 = batch_tensor_contraction( + TreeTensor, + [SubNetwork(SubNetwork(x), SubNetwork(A))]; + cutoff=cutoff, + maxdim=linkdim * linkdim, + ) + tsr_true = contract(out_true...) + tsr_nrmsquare = (tsr_true * tsr_true)[1] + @test isapprox(tsr_true, ITensor(out2[1])) + + maxdims = [2, 4, 6, 8] + for dim in maxdims + out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) + out2 = batch_tensor_contraction( + TreeTensor, [SubNetwork(SubNetwork(x), SubNetwork(A))]; cutoff=cutoff, maxdim=dim + ) + residual1 = tsr_true - contract(out...) + residual2 = tsr_true - ITensor(out2[1]) + error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) + error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) + print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") + end +end + +@testset "test inds_binary_tree" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + n = Index(2, "n") + o = Index(2, "o") + p = Index(2, "p") + + T = randomITensor(i, j, k, l, m, n, o, p) + M = MPS(T, (i, j, k, l, m, n, o, p); cutoff=1e-5, maxdim=500) + network = M[:] + + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") + @test length(out) == 2 + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") + @test length(out) == 2 + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") + @test length(out) == 2 +end + +@testset "test inds_binary_tree of a 2D network" begin + N = (8, 8, 3) + linkdim = 2 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + network = vec(tn[:, :, 1]) + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") + @test length(out) == 2 + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") + @test length(out) == 2 + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") + @test length(out) == 2 +end + +@testset "test tree embedding" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + T = randomITensor(i, j, k, l, m) + M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) + network = M[:] + out1 = contract(network...) + inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") + tnet_dict = tree_embedding(network, inds_btree) + network2 = vcat(collect(values(tnet_dict))...) + out2 = contract(network2...) + i1 = noncommoninds(network...) + i2 = noncommoninds(network2...) + @test (length(i1) == length(i2)) + @test isapprox(out1, out2) +end + +function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) + N = size(tn) + out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) + network = SubNetwork(tn[:, 1]) + for i in 2:(N[2]) + network = SubNetwork(network, SubNetwork(tn[:, i])) + end + out2 = batch_tensor_contraction( + TreeTensor, [network]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, optimize=false + ) + return out[], ITensor(out2[1])[] +end + +@testset "test PEPS" begin + N = (8, 8) #(12, 12) + linkdim = 2 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + # tn = ising_partition(N, linkdim) + + ITensors.set_warn_order(100) + maxdim = linkdim^N[2] + maxsize = maxdim * maxdim * linkdim + out_true, out2 = benchmark_peps_contraction( + tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + ) + print(out_true, out2) + @test abs((out_true - out2) / out_true) < 1e-3 + + maxdims = [i for i in 2:16] + for dim in maxdims + size = dim * dim * linkdim + out, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) + error1 = abs((out - out_true) / out_true) + error2 = abs((out2 - out_true) / out_true) + print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") + end +end + +function benchmark_3D_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) + # TODO: this sequential MPS doesn't give the desired tree when each tn[i] is a slice of the 2D surface + out = contract( + tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" + ) + for i in 2:length(tn) + out = contract( + out, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" + ) + end + out2 = contract(tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps") + for i in 2:length(tn) + out2 = contract( + out2, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps" + ) + end + return out[], out2[] +end + +@testset "test 3-D cube with 2D grouping" begin + do_profile(true) + N = (3, 3, 4) #(12, 12) + linkdim = 2 + nrows = prod([s for s in N[1:(length(N) - 1)]]) + ncols = N[length(N)] + maxdim = linkdim^(floor(nrows)) + cutoff = 1e-15 + # tn = ising_partition(N, linkdim) + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + # snake mapping + for k in 1:N[3] + for j in 1:N[2] + if iseven(j) + tn[:, j, k] = reverse(tn[:, j, k]) + end + end + end + tn = reshape(tn, (nrows, ncols)) + tn = [TreeTensor(tn[:, i]) for i in 1:ncols] + @info size(tn) + ITensors.set_warn_order(100) + maxsize = maxdim * maxdim * linkdim + out1, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize) + profile_exit() + print(out1, out2) + @test abs((out1 - out2) / out1) < 1e-3 + maxdims = [3, 5, 8, 10, 11, 12, 13, 14, 15, 16, 20, 31, 32] + for dim in maxdims + size = dim * dim * linkdim + out, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) + error1 = abs((out - out1) / out1) + error2 = abs((out2 - out1) / out1) + print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") + end +end + +@testset "test 3-D cube with 1D grouping" begin + ITensors.set_warn_order(100) + do_profile(true) + N = (3, 3, 3) # (5, 5, 5) + linkdim = 2 + maxdim = linkdim^(floor(N[1] * N[2])) + cutoff = 1e-15 + # tn = ising_partition(N, linkdim) + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + tn = reshape(tn, (N[1], N[2] * N[3])) + tntree = tn[:, 1] + for i in 2:(N[2] * N[3]) + tntree = [tntree, tn[:, i]] + end + approximate_contract( + tntree; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mincut" + ) + profile_exit() +end + +#TODO +# @testset "test 3-D cube with DMRG-like algorithm" begin +# ITensors.set_warn_order(100) +# do_profile(true) +# N = (5, 5, 3) # (5, 5, 5) +# linkdim = 2 +# maxdim = linkdim^(floor(N[1] * N[2])) +# cutoff = 1e-15 +# tn = ising_partition(N, linkdim) +# function build_tree(i) +# tree = tn[:, 1, i] +# for j in 2:N[2] +# tree = [tree, tn[:, j, i]] +# end +# return tree +# end +# tn1, _ = approximate_contract( +# build_tree(1); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# ) +# tn2, _ = approximate_contract( +# build_tree(2); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# ) +# tn3, _ = approximate_contract( +# build_tree(3); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# ) +# tn12, _ = approximate_contract( +# [tn1..., tn2...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# ) +# out, _ = approximate_contract( +# [tn12..., tn3...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# ) +# profile_exit() +# end + +@testset "benchmark PEPS" begin + N = (8, 8) #(12, 12) + linkdim = 10 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + + dim = 20 + size = dim * dim * linkdim + # warmup + for i in 1:2 + tn = map(inds -> randomITensor(inds...), tn_inds) + ITensors.set_warn_order(100) + benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) + end + + do_profile(true) + for i in 1:3 + tn = map(inds -> randomITensor(inds...), tn_inds) + ITensors.set_warn_order(100) + benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) + end + profile_exit() +end diff --git a/test/approximate_tn_contraction/utils.jl b/test/approximate_tn_contraction/utils.jl new file mode 100644 index 00000000..b3ea8793 --- /dev/null +++ b/test/approximate_tn_contraction/utils.jl @@ -0,0 +1,34 @@ +using ITensorNetworkAD.ITensorNetworks: + ITensor_networks, line_network, TreeTensor, approximate_contract + +@profile function peps_contraction_mpomps(tn; cutoff=1e-15, maxdim=1000, snake=false) + N = size(tn) + x = tn[:, 1] + for i in 2:(N[2] - 1) + A = (iseven(i) && snake) ? reverse(tn[:, i]) : tn[:, i] + x = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=maxdim)[:] + end + return contract(x..., tn[:, N[2]]...) +end + +@profile function contract_w_sweep(tn, rank) + sweep = sweep_contract(tn, rank, rank) + return ldexp(sweep...) +end + +@profile function contract_element_group(tnet, rank) + element_grouping = line_network(tnet) + return approximate_contract( + element_grouping; cutoff=1e-15, maxdim=rank, maxsize=1e15, algorithm="mincut" + ) +end + +@profile function contract_line_group(tnet, rank, N) + line_grouping = SubNetwork(tnet[:, 1]) + for i in 2:N[2] + line_grouping = SubNetwork(line_grouping, tnet[:, i]...) + end + return batch_tensor_contraction( + TreeTensor, [line_grouping]; cutoff=1e-15, maxdim=rank, optimize=false + ) +end diff --git a/test/runtests.jl b/test/runtests.jl index 7c8592bd..f944309e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -72,4 +72,9 @@ using Test ## inner_res = contract(inner_tn; sequence)[] ## @test inner_res isa Float64 + + for filename in ["approximate_tn_contraction/runtests.jl"] + println("Running $filename") + include(filename) + end end From 305027598df5927378c4066c3c25962c8a807eb1 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 25 Oct 2022 17:17:50 -0500 Subject: [PATCH 02/24] Remove some logs, adding back test manifest --- .../contract/contract.jl | 8 - test/Manifest.toml | 556 ++++++++++++++++++ 2 files changed, 556 insertions(+), 8 deletions(-) create mode 100644 test/Manifest.toml diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 366cdbd6..f0555af0 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -669,11 +669,8 @@ function get_tn_cache_sub_info( push!(new_igs, nothing) else binary_tree = Vector{Vector}(binary_tree) - @info "binary_tree is", binary_tree, typeof(binary_tree) nodes = topo_sort(binary_tree; type=Vector{<:Vector}) - @info "nodes is", nodes sub_tn = [tn_tree[n] for n in nodes] - @info "sub_tn is", sub_tn sub_tn_tree = Dict([n => tn_tree[n] for n in nodes]...) index_leaves = vectorize(binary_tree) new_indices = setdiff(noncommoninds(sub_tn...), index_leaves) @@ -694,9 +691,6 @@ function get_tn_cache_info( tn_tree_2::Dict{Vector,OrthogonalITensor}, cache_binary_trees::Vector, ) - @info "tn_tree_1 is", tn_tree_1 - @info "tn_tree_2 is", tn_tree_2 - @info "cache_binary_trees is", cache_binary_trees @timeit timer "get_tn_cache_info" begin cached_tn_tree1, uncached_tn1, new_igs_1 = get_tn_cache_sub_info( tn_tree_1, cache_binary_trees @@ -790,8 +784,6 @@ function approximate_contract(ctree::Vector; kwargs...) cache_binary_tree_right = line_to_tree([ ig_to_ig_tree[ig].data for ig in cache_igs_right ]) - @info "cache_binary_tree_left has type", typeof(cache_binary_tree_left) - @info "cache_binary_tree_right has type", typeof(cache_binary_tree_right) cached_tn_tree, uncached_tn, new_ig_left, new_ig_right = get_tn_cache_info( ctree_to_tn_tree[c[1]], ctree_to_tn_tree[c[2]], diff --git a/test/Manifest.toml b/test/Manifest.toml new file mode 100644 index 00000000..d440143e --- /dev/null +++ b/test/Manifest.toml @@ -0,0 +1,556 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.7.2" +manifest_format = "2.0" + +[[deps.AbstractTrees]] +git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.3.4" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[deps.ArnoldiMethod]] +deps = ["LinearAlgebra", "Random", "StaticArrays"] +git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae" +uuid = "ec485272-7323-5ecc-a04f-4719b315124d" +version = "0.2.0" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.Blosc]] +deps = ["Blosc_jll"] +git-tree-sha1 = "575bdd70552dd9a7eaeba08ef2533226cdc50779" +uuid = "a74b3585-a348-5f62-a45c-50e91977d574" +version = "0.7.2" + +[[deps.Blosc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Lz4_jll", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "91d6baa911283650df649d0aea7c28639273ae7b" +uuid = "0b7ba130-8d10-5ba8-a3d6-c5182647fed9" +version = "1.21.1+0" + +[[deps.ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "6e39c91fb4b84dcb870813c91674bdebb9145895" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.11.5" + +[[deps.ChangesOfVariables]] +deps = ["ChainRulesCore", "LinearAlgebra", "Test"] +git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1" +uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" +version = "0.1.2" + +[[deps.Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "44c37b4636bc54afac5c574d2d02b625349d6582" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.41.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[deps.Contour]] +deps = ["StaticArrays"] +git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.5.7" + +[[deps.Crayons]] +git-tree-sha1 = "b618084b49e78985ffa8422f32b9838e397b9fc2" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.0" + +[[deps.DataAPI]] +git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.9.0" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "3daef5523dd2e769dad2365274f760ff5f282c7d" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.11" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[deps.Dictionaries]] +deps = ["Indexing", "Random"] +git-tree-sha1 = "66bde31636301f4d217a161cabe42536fa754ec8" +uuid = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" +version = "0.3.17" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[deps.DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.6" + +[[deps.Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.3+0" + +[[deps.ExprTools]] +git-tree-sha1 = "24565044e60bc48a7562e75bcf14f084901dc0b6" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.7" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "67551df041955cc6ee2ed098718c8fcd7fc7aebe" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.12.0" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.1" + +[[deps.Graphs]] +deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] +git-tree-sha1 = "d727758173afef0af878b29ac364a0eca299fc6b" +uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" +version = "1.5.1" + +[[deps.HDF5]] +deps = ["Blosc", "Compat", "HDF5_jll", "Libdl", "Mmap", "Random", "Requires"] +git-tree-sha1 = "698c099c6613d7b7f151832868728f426abe698b" +uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +version = "0.15.7" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "bab67c0d1c4662d2c4be8c6007751b0b6111de5c" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.12.1+0" + +[[deps.ITensorNetworks]] +deps = ["ITensors"] +git-tree-sha1 = "aa46404f09d7107f40cc068b2b2dfcd947f12b48" +repo-rev = "main" +repo-url = "https://github.com/mtfishman/ITensorNetworks.jl" +uuid = "2919e153-833c-4bdc-8836-1ea460a35fc7" +version = "0.1.0" + +[[deps.ITensorUnicodePlots]] +deps = ["Graphs", "ITensorVisualizationBase", "ITensors", "LinearAlgebra", "NetworkLayout", "Reexport", "Statistics", "UnicodePlots"] +git-tree-sha1 = "fd73ee12c4ecf442360719b9ecf02b28c1500657" +uuid = "73163f41-4a9e-479f-8353-73bf94dbd758" +version = "0.1.0" + +[[deps.ITensorVisualizationBase]] +deps = ["AbstractTrees", "Compat", "GeometryBasics", "Graphs", "ITensors", "LinearAlgebra", "MetaGraphs", "NetworkLayout", "SparseArrays", "Statistics"] +git-tree-sha1 = "fb3d48ecaff23a857b2525fed73217d737f92811" +uuid = "cd2553d2-8bef-4d93-8a38-c62f17d5ad23" +version = "0.1.0" + +[[deps.ITensors]] +deps = ["ChainRulesCore", "Compat", "Dictionaries", "HDF5", "KrylovKit", "LinearAlgebra", "LinearMaps", "NDTensors", "PackageCompiler", "Pkg", "Printf", "Random", "Requires", "SerializedElementArrays", "StaticArrays", "Strided", "TimerOutputs", "TupleTools", "Zeros", "ZygoteRules"] +git-tree-sha1 = "8e99d15c467e62cc290e10438c7ebb05052aac74" +uuid = "9136182c-28ba-11e9-034c-db9fb085ebd5" +version = "0.2.12" + +[[deps.Indexing]] +git-tree-sha1 = "ce1566720fd6b19ff3411404d4b977acd4814f9f" +uuid = "313cdc1a-70c2-5d6a-ae34-0150d3930a38" +version = "1.1.1" + +[[deps.Inflate]] +git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c" +uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +version = "0.1.2" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.InverseFunctions]] +deps = ["Test"] +git-tree-sha1 = "a7254c0acd8e62f1ac75ad24d5db43f5f19f3c65" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.2" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.1" + +[[deps.IterTools]] +git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.4.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLD2]] +deps = ["DataStructures", "FileIO", "MacroTools", "Mmap", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"] +git-tree-sha1 = "09ef0c32a26f80b465d808a1ba1e85775a282c97" +uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +version = "0.4.17" + +[[deps.JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "22df5b96feef82434b07327e2d3c770a9b21e023" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.4.0" + +[[deps.KrylovKit]] +deps = ["LinearAlgebra", "Printf"] +git-tree-sha1 = "0328ad9966ae29ccefb4e1b9bfd8c8867e4360df" +uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" +version = "0.5.3" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.LinearMaps]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "dbb14c604fc47aa4f2e19d0ebb7b6416f3cfa5f5" +uuid = "7a12625a-238d-50fd-b39a-03d52299707e" +version = "3.5.1" + +[[deps.LogExpFunctions]] +deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "e5718a00af0ab9756305a0392832c8952c7426c1" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.6" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.Lz4_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "5d494bc6e85c4c9b626ee0cab05daa4085486ab1" +uuid = "5ced341a-0733-55b8-9ab6-a4889d929147" +version = "1.9.3+0" + +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.9" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[deps.MetaGraphs]] +deps = ["Graphs", "JLD2", "Random"] +git-tree-sha1 = "2af69ff3c024d13bde52b34a2a7d6887d4e7b438" +uuid = "626554b9-1ddb-594c-aa3c-2596fe9399a5" +version = "0.7.1" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.0.2" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[deps.NDTensors]] +deps = ["Compat", "Dictionaries", "HDF5", "LinearAlgebra", "Random", "Requires", "StaticArrays", "Strided", "TimerOutputs", "TupleTools"] +git-tree-sha1 = "061056112183f05acee6afff89ddf8b77f9c88d8" +uuid = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" +version = "0.1.33" + +[[deps.NetworkLayout]] +deps = ["GeometryBasics", "LinearAlgebra", "Random", "Requires", "SparseArrays"] +git-tree-sha1 = "cac8fc7ba64b699c678094fa630f49b80618f625" +uuid = "46757867-2c16-5918-afeb-47bfcb05e46a" +version = "0.4.4" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "648107615c15d4e09f7eca16307bc821c1f718d8" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "1.1.13+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[deps.PackageCompiler]] +deps = ["Artifacts", "LazyArtifacts", "Libdl", "Pkg", "RelocatableFolders", "UUIDs"] +git-tree-sha1 = "a16924b37299cc7d6106fac255b44a8c79c7c21f" +uuid = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d" +version = "1.7.7" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "2cf929d64681236a2e074ffafb8d568733d2e6af" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.3" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "0.1.3" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.1.0" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.SerializedElementArrays]] +deps = ["Serialization"] +git-tree-sha1 = "8e73e49eaebf73486446a3c1eede403bff259826" +uuid = "d3ce8812-9567-47e9-a7b5-65a6d70a3065" +version = "0.1.0" + +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.0.1" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "2ae4fe21e97cd13efd857462c1869b73c9f61be3" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.3.2" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StatsAPI]] +git-tree-sha1 = "d88665adc9bcf45903013af0982e2fd05ae3d0a6" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.2.0" + +[[deps.StatsBase]] +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "51383f2d367eb3b444c961d485c565e4c0cf4ba0" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.33.14" + +[[deps.Strided]] +deps = ["LinearAlgebra", "TupleTools"] +git-tree-sha1 = "4d581938087ca90eab9bd4bb6d270edaefd70dcd" +uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" +version = "1.1.2" + +[[deps.StructArrays]] +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "d21f2c564b21a202f4677c0fba5b5ee431058544" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.4" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] +git-tree-sha1 = "bb1064c9a84c52e277f1096cf41434b675cd368b" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.6.1" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "a5aed757f65c8a1c64503bc4035f704d24c749bf" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.14" + +[[deps.TranscodingStreams]] +deps = ["Random", "Test"] +git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.9.6" + +[[deps.TupleTools]] +git-tree-sha1 = "3c712976c47707ff893cf6ba4354aa14db1d8938" +uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" +version = "1.3.0" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.UnicodePlots]] +deps = ["Contour", "Crayons", "Dates", "SparseArrays", "StatsBase"] +git-tree-sha1 = "0efb50275f42f2795b771a4ad2c339db9760fd98" +uuid = "b8865327-cd53-5732-bb35-84acbb429228" +version = "2.6.0" + +[[deps.Zeros]] +deps = ["Test"] +git-tree-sha1 = "7eb4fd47c304c078425bf57da99a56606150d7d4" +uuid = "bd1ec220-6eb4-527a-9b49-e79c3db6233b" +version = "0.3.0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.0+0" + +[[deps.ZygoteRules]] +deps = ["MacroTools"] +git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0" +uuid = "700de1a5-db45-46bc-99cf-38207098b444" +version = "0.2.2" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" \ No newline at end of file From 73e080a0a5d3142e1fa54a201f0f32f6e1a7b439 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 25 Oct 2022 17:19:54 -0500 Subject: [PATCH 03/24] Nit --- test/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Manifest.toml b/test/Manifest.toml index d440143e..bdde109c 100644 --- a/test/Manifest.toml +++ b/test/Manifest.toml @@ -553,4 +553,4 @@ uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" \ No newline at end of file +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" From 83e1719fe398ae27f80e357f59d4b3172629332c Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Thu, 10 Nov 2022 11:13:56 -0600 Subject: [PATCH 04/24] [ApproximateContraction] Remove retired functions, get more test cases working --- .../contract/contract.jl | 55 +- .../contract/index_group.jl | 8 + .../contract/mincut_tree.jl | 4 +- .../contract/tensornetwork_graph.jl | 4 + .../networks/itensor_network.jl | 194 ------- .../orthogonal_tensor.jl | 1 - test/approximate_tn_contraction/cache.jl | 2 +- .../itensor_network.jl | 52 -- test/approximate_tn_contraction/mpstensor.jl | 52 -- test/approximate_tn_contraction/runtests.jl | 5 +- test/approximate_tn_contraction/treetensor.jl | 525 ++++++++---------- test/runtests.jl | 6 +- 12 files changed, 290 insertions(+), 618 deletions(-) delete mode 100644 test/approximate_tn_contraction/itensor_network.jl delete mode 100644 test/approximate_tn_contraction/mpstensor.jl diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index f0555af0..ce2f7119 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -376,6 +376,10 @@ function generate_adjacency_tree(ctree, ancestors, ctree_to_igs) return collect(values(igs_to_adjacency_tree))[1] end end + if length(igs_to_adjacency_tree) >= 1 + @info "generate_adjacency_tree has ", length(igs_to_adjacency_tree), "outputs" + return IndexAdjacencyTree([collect(values(igs_to_adjacency_tree))...], false, false) + end end end @@ -580,20 +584,19 @@ function _approximate_contract_pre_process(tn_leaves, ctrees) end for c in ctrees ancestors = ctree_to_ancestors[c] - if ancestors == [] - continue - end adj_tree = generate_adjacency_tree(c, ancestors, ctree_to_igs) - ctree_to_adj_tree[c] = minswap_adjacency_tree( - adj_tree, ctree_to_adj_tree[c[1]], ctree_to_adj_tree[c[2]] - ) + if adj_tree != nothing + ctree_to_adj_tree[c] = minswap_adjacency_tree( + adj_tree, ctree_to_adj_tree[c[1]], ctree_to_adj_tree[c[2]] + ) + end end # mapping each index group to the index group tree ig_to_ig_tree = Dict{IndexGroup,IndexGroup}() for leaf in tn_leaves for ig in ctree_to_igs[leaf] if !haskey(ig_to_ig_tree, ig) - inds_tree = inds_binary_tree(leaf, ig.data; algorithm="mincut") + inds_tree = inds_binary_tree(leaf, ig.data; algorithm="mps") ig_to_ig_tree[ig] = IndexGroup(inds_tree, true) end end @@ -739,6 +742,17 @@ function update_tn_tree_keys!(tn_tree, inds_btree, pairs::Vector{Pair}) end end +function get_child_tn( + ctree_to_tn_tree::Dict{Vector,Dict{Vector,OrthogonalITensor}}, ctree::Vector +) + if !haskey(ctree_to_tn_tree, ctree) + @assert ctree isa Vector{ITensor} + return orthogonal_tensors(ctree) + else + return vcat(collect(values(ctree_to_tn_tree[ctree]))...) + end +end + # ctree: contraction tree # tn: vector of tensors representing a tensor network # tn_tree: a dict maps each index tree in the tn to a tensor @@ -758,24 +772,31 @@ function approximate_contract(ctree::Vector; kwargs...) ctree_to_contract_igs[c[1]] = contract_igs ctree_to_contract_igs[c[2]] = contract_igs end + # special case when the network contains uncontracted inds + ctree_to_contract_igs[ctrees[end]] = ctree_to_igs[ctrees[end]] # mapping each contraction tree to a tensor network ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() - for leaf in tn_leaves - inds_btree = ordered_igs_to_binary_tree( - ctree_to_adj_tree[leaf].children, ctree_to_contract_igs[leaf], ig_to_ig_tree - ) - ctree_to_tn_tree[leaf] = approximate_contract_ctree_to_tensor( - orthogonal_tensors(leaf), inds_btree; kwargs... - ) - end for c in ctrees if ctree_to_igs[c] == [] @assert c == ctrees[end] - tn1 = vcat(collect(values(ctree_to_tn_tree[c[1]]))...) - tn2 = vcat(collect(values(ctree_to_tn_tree[c[2]]))...) + tn1 = get_child_tn(ctree_to_tn_tree, c[1]) + tn2 = get_child_tn(ctree_to_tn_tree, c[2]) tn = vcat(tn1, tn2) return get_tensors([optcontract(tn)]) end + # caching is not needed here + if !haskey(ctree_to_tn_tree, c[1]) || !haskey(ctree_to_tn_tree, c[2]) + tn1 = get_child_tn(ctree_to_tn_tree, c[1]) + tn2 = get_child_tn(ctree_to_tn_tree, c[2]) + inds_btree = ordered_igs_to_binary_tree( + ctree_to_adj_tree[c].children, ctree_to_contract_igs[c], ig_to_ig_tree + ) + ctree_to_tn_tree[c] = approximate_contract_ctree_to_tensor( + [tn1..., tn2...], inds_btree; kwargs... + ) + continue + end + # caching uncache_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( [ctree_to_adj_tree[i].children for i in [c, c[1], c[2]]], [ctree_to_contract_igs[i] for i in [c, c[1], c[2]]], diff --git a/src/ApproximateTNContraction/contract/index_group.jl b/src/ApproximateTNContraction/contract/index_group.jl index 51d34a51..db48ec19 100644 --- a/src/ApproximateTNContraction/contract/index_group.jl +++ b/src/ApproximateTNContraction/contract/index_group.jl @@ -13,7 +13,15 @@ end function get_index_groups(tn_tree::Vector) @timeit timer "get_index_groups" begin tn_leaves = get_leaves(tn_tree) + tn = vcat(tn_leaves...) + uncontract_inds = noncommoninds(tn...) igs = [] + for leaf in tn_leaves + inds = intersect(noncommoninds(leaf...), uncontract_inds) + if length(inds) >= 1 + push!(igs, IndexGroup(inds)) + end + end for (t1, t2) in powerset(tn_leaves, 2, 2) inds = intersect(noncommoninds(t1...), noncommoninds(t2...)) if length(inds) >= 1 diff --git a/src/ApproximateTNContraction/contract/mincut_tree.jl b/src/ApproximateTNContraction/contract/mincut_tree.jl index f24811a9..da784526 100644 --- a/src/ApproximateTNContraction/contract/mincut_tree.jl +++ b/src/ApproximateTNContraction/contract/mincut_tree.jl @@ -5,7 +5,7 @@ MAX_WEIGHT = 100000 function inds_binary_tree( network::Vector{ITensor}, inds_groups::Vector{<:Vector}; kwargs... ) - tng = TensorNetworkGraph(network) + tng = TensorNetworkGraph(network, vectorize(inds_groups)) function get_sub_tree(inds) @assert all(ind -> ind isa Index, inds) if length(inds) == 1 @@ -55,7 +55,7 @@ function inds_binary_tree( end return out_inds end - tng = TensorNetworkGraph(network) + tng = TensorNetworkGraph(network, outinds) grouped_uncontracted_inds = [[i] for i in outinds] return inds_binary_tree!(tng, grouped_uncontracted_inds; algorithm=algorithm) end diff --git a/src/ApproximateTNContraction/contract/tensornetwork_graph.jl b/src/ApproximateTNContraction/contract/tensornetwork_graph.jl index 7dae1078..2de032e6 100644 --- a/src/ApproximateTNContraction/contract/tensornetwork_graph.jl +++ b/src/ApproximateTNContraction/contract/tensornetwork_graph.jl @@ -9,6 +9,10 @@ end function TensorNetworkGraph(network::Vector{ITensor}) uncontract_inds = noncommoninds(network...) + return TensorNetworkGraph(network, uncontract_inds) +end + +function TensorNetworkGraph(network::Vector{ITensor}, uncontract_inds::Vector) graph = Graphs.DiGraph(length(network)) # construct contract_edges contract_edges = [] diff --git a/src/ApproximateTNContraction/networks/itensor_network.jl b/src/ApproximateTNContraction/networks/itensor_network.jl index 514a0350..afd422ed 100644 --- a/src/ApproximateTNContraction/networks/itensor_network.jl +++ b/src/ApproximateTNContraction/networks/itensor_network.jl @@ -1,34 +1,5 @@ - -# -# Some general tools for working with networks of ITensors. -# - -""" - itensor_network(dims::Int...; linkdims) - itensor_network(s::Array{<:Index}; linkdims) - itensor_network(s::Array{<:Vector{<:Index}}; linkdims) - -Create a tensor network on a hypercubic lattice of -dimension `dims` with link dimension `linkdims`. - -Alternatively, specify the site indices with an Array `s`, -in which case the lattice will be of dimension `size(s)`. - -The network will have periodic boundary conditions. -To remove the periodic boundary condiitions, use -the function `project_boundary`. -""" -function itensor_network(dims::Int...; linkdims) - return ITensor.(inds_network(dims...; linkdims=linkdims)) -end - -function itensor_network(s::Array; linkdims) - return ITensor.(inds_network(s; linkdims=linkdims)) -end - """ boundary_projectors(tn::Matrix{ITensor}, state=1) - For a 2D tensor network, return the right and bottom boundary projectors onto the local state `state`. """ @@ -63,7 +34,6 @@ end """ project_boundary(tn::Matrix{ITensor}, state=1) - Project the boundary of a periodic 2D tensor network onto the specified state. """ @@ -105,110 +75,6 @@ function project_boundary(tn::Array{ITensor,3}, state=1) return tn end -function filter_alllinkinds(f, tn) - linkinds = Dict{Tuple{keytype(tn),keytype(tn)},Vector{indtype(tn)}}() - for n in keys(tn), m in keys(tn) - if f(n, m) - is = commoninds(tn[n], tn[m]) - if !isempty(is) - linkinds[(n, m)] = is - end - end - end - return linkinds -end - -""" - alllinkinds(tn) - -Return a dictionary of all of the link indices of the network. -The link indices are determined by searching through the network -for tensors with indices in common with other tensors, and -the keys of the dictionary store a tuple of the sites with -the common indices. - -Notice that this version will return a dictionary containing -repeated link indices, since the -For example: -```julia -i, j, k, l = Index.((2, 2, 2, 2)) -inds_network = [(i, dag(j)), (j, dag(k)), (k, dag(l)), (l, dag(i))] -tn_network = randomITensor.(inds_network) -links = allinkinds(tn_network) -links[(1, 2)] == (dag(j),) -links[(2, 1)] == (j,) -links[(2, 3)] == (dag(l),) -links[(1, 3)] # Error! In the future this may return an empty Tuple -``` - -Use `inlinkinds` and `outlinkinds` to return dictionaries without -repeats (such as only the link `(2, 1)` and not `(1, 2)` or vice versa). -""" -alllinkinds(tn) = filter_alllinkinds(≠, tn) -inlinkinds(tn) = filter_alllinkinds(>, tn) -outlinkinds(tn) = filter_alllinkinds(<, tn) - -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) - -From a tensor network `tn` and a site/node `n`, determine the neighbors -of the specified tensor `tn[n]` by searching for which other -tensors in the network have indices in common with `tn[n]`. - -Use `inneighbors` and `outneighbors` for directed versions. -""" -function neighbors(tn, n) - return filterneighbors(≠, tn, n) -end -inneighbors(tn, n) = filterneighbors(>, tn, n) -outneighbors(tn, n) = filterneighbors(<, tn, n) - -function mapinds(f, ::typeof(linkinds), tn) - tn′ = copy(tn) - inds_dict = Dict() - for n in keys(tn) - for nn in neighbors(tn, n) - commonindsₙ = commoninds(tn[n], tn[nn]) - newinds = [] - for i in commonindsₙ - if !haskey(inds_dict, i) - inds_dict[i] = f(i) - end - newinds = vcat(newinds, [inds_dict[i]]) - end - tn′[n] = replaceinds(tn′[n], commonindsₙ => newinds) - end - end - return tn′ -end - -function ITensors.prime(::typeof(linkinds), tn, args...) - return mapinds(x -> prime(x, args...), linkinds, tn) -end - -function ITensors.sim(::typeof(linkinds), tn, args...) - return mapinds(x -> sim(x, args...), linkinds, tn) -end - -function ITensors.addtags(::typeof(linkinds), tn, args...) - return mapinds(x -> addtags(x, args...), linkinds, tn) -end - -function ITensors.removetags(::typeof(linkinds), tn, args...) - return mapinds(x -> removetags(x, args...), linkinds, tn) -end - function ITensors.prime(indices::Array{<:Index,1}, network::Array{ITensor}, n::Integer=1) function primeinds(tensor) prime_inds = [ind for ind in inds(tensor) if ind in indices] @@ -241,63 +107,3 @@ end function ITensors.commoninds(n1::Array{ITensor}, n2::Array{ITensor}) return mapreduce(a -> commoninds(a...), vcat, zip(n1, n2)) end - -# Compute the sets of combiners that combine the link indices -# of the tensor network so that neighboring tensors only -# share a single larger index. -# Return a dictionary from a site to a combiner. -function combiners(::typeof(linkinds), tn) - Cs = Dict(keys(tn) .=> (ITensor[] for _ in keys(tn))) - for n in keys(tn) - for nn in inneighbors(tn, n) - commonindsₙ = commoninds(tn[n], tn[nn]) - C = combiner(commonindsₙ) - push!(Cs[n], C) - push!(Cs[nn], dag(C)) - end - end - return Cs -end - -# Insert the gauge tensors `gauge` into the links of the tensor -# network `tn`. -function insert_gauge(tn, gauge) - tn′ = copy(tn) - for n in keys(gauge) - for g in gauge[n] - if hascommoninds(tn′[n], g) - tn′[n] *= g - end - end - end - return tn′ -end - -# Insert the gauge tensors `gauge` into the links of the sets -# of tensor networks `tn` stored in a NamedTuple. -# TODO: is this used anywhere? -function insert_gauge(tn::NamedTuple, gauge) - return map(x -> insert_gauge.(x, (gauge,)), tn) -end - -# Split the links of an ITensor network by changing the prime levels -# or tags of pairs of links. -function split_links(H::Union{MPS,MPO}; split_tags=("" => ""), split_plevs=(0 => 1)) - left_tags, right_tags = split_tags - left_plev, right_plev = split_plevs - l = outlinkinds(H) - Hsplit = copy(H) - for bond in keys(l) - n1, n2 = bond - lₙ = l[bond] - left_l_n = prime(addtags(lₙ, left_tags), left_plev) - right_l_n = prime(addtags(lₙ, right_tags), right_plev) - Hsplit[n1] = replaceinds(Hsplit[n1], lₙ => left_l_n) - Hsplit[n2] = replaceinds(Hsplit[n2], lₙ => right_l_n) - end - return Hsplit -end - -function split_links(H::Vector{ITensor}, args...; kwargs...) - return data(split_links(MPS(H), args...; kwargs...)) -end diff --git a/src/ApproximateTNContraction/orthogonal_tensor.jl b/src/ApproximateTNContraction/orthogonal_tensor.jl index 8f7e7b74..5354259c 100644 --- a/src/ApproximateTNContraction/orthogonal_tensor.jl +++ b/src/ApproximateTNContraction/orthogonal_tensor.jl @@ -19,7 +19,6 @@ function ITensors.noncommoninds(ortho_tensors::OrthogonalITensor...) if length(ortho_tensors) == 1 return collect(inds(ortho_tensors[1])) end - @info "ortho_tensors are", ortho_tensors return noncommoninds(get_tensors([ortho_tensors...])...) end diff --git a/test/approximate_tn_contraction/cache.jl b/test/approximate_tn_contraction/cache.jl index bcbb0fd6..b82ec33a 100644 --- a/test/approximate_tn_contraction/cache.jl +++ b/test/approximate_tn_contraction/cache.jl @@ -13,5 +13,5 @@ using ITensorNetworks.ApproximateTNContraction: IndexGroup, get_igs_cache_info igs_list = [type([l, k, i, m, n]), type([i, j, k]), type([l, j, m, n])] contract_igs_list = [type([m]), type([j]), type([j])] out = get_igs_cache_info(igs_list, contract_igs_list) - @test out == ([k, i, m], [l], [n]) + @test out == ([l, k, i, m, n], [], []) end diff --git a/test/approximate_tn_contraction/itensor_network.jl b/test/approximate_tn_contraction/itensor_network.jl deleted file mode 100644 index f05e123c..00000000 --- a/test/approximate_tn_contraction/itensor_network.jl +++ /dev/null @@ -1,52 +0,0 @@ -using ITensors -using Test -using ITensorNetworks -using ITensorNetworks.ApproximateTNContraction: itensor_network, neighbors - -@testset "itensor_network.jl" begin - @testset "itensor_network from dims" begin - d = (3, 3) - tn = itensor_network(d...; linkdims=3) - @test tn isa Matrix{ITensor} - @test size(tn) == (3, 3) - @test all(ITensors.isemptystorage, tn) - @test hascommoninds(tn[1, 1], tn[1, 2]) - @test hascommoninds(tn[1, 1], tn[2, 1]) - @test !hascommoninds(tn[1, 1], tn[2, 2]) - @test hascommoninds(tn[1, 1], tn[3, 1]) - @test hascommoninds(tn[1, 1], tn[1, 3]) - @test isempty(uniqueinds(tn[2, 2], tn[2, 1], tn[2, 3], tn[1, 2], tn[3, 2])) - @test issetequal( - neighbors(tn, CartesianIndex(1, 2)), - [ - CartesianIndex(1, 1), - CartesianIndex(2, 2), - CartesianIndex(3, 2), - CartesianIndex(1, 3), - ], - ) - end - @testset "itensor_network from siteinds" begin - d = (3, 3) - s = siteinds("S=1/2", d...) - tn = itensor_network(s; linkdims=3) - @test tn isa Matrix{ITensor} - @test size(tn) == (3, 3) - @test all(ITensors.isemptystorage, tn) - @test hascommoninds(tn[1, 1], tn[1, 2]) - @test hascommoninds(tn[1, 1], tn[2, 1]) - @test !hascommoninds(tn[1, 1], tn[2, 2]) - @test hascommoninds(tn[1, 1], tn[3, 1]) - @test hascommoninds(tn[1, 1], tn[1, 3]) - @test uniqueinds(tn[2, 2], tn[2, 1], tn[2, 3], tn[1, 2], tn[3, 2]) == [s[2, 2]] - @test issetequal( - neighbors(tn, CartesianIndex(1, 2)), - [ - CartesianIndex(1, 1), - CartesianIndex(2, 2), - CartesianIndex(3, 2), - CartesianIndex(1, 3), - ], - ) - end -end diff --git a/test/approximate_tn_contraction/mpstensor.jl b/test/approximate_tn_contraction/mpstensor.jl deleted file mode 100644 index dafcfb03..00000000 --- a/test/approximate_tn_contraction/mpstensor.jl +++ /dev/null @@ -1,52 +0,0 @@ -using ITensorNetworkAD -using AutoHOOT, ITensors, Zygote -using ITensorNetworkAD.ITensorNetworks: GeneralMPSTensor - -const itensorah = ITensorNetworkAD.ITensorAutoHOOT - -@testset "test MPSTensor" begin - i = Index(2, "i") - j = Index(3, "j") - k = Index(2, "k") - l = Index(4, "l") - m = Index(5, "m") - - A = randomITensor(i, j, k) - B = randomITensor(k, l, m) - C = randomITensor(i, j, l, m) - mps_A = GeneralMPSTensor(MPS(A, inds(A))) - mps_B = GeneralMPSTensor(MPS(B, inds(B))) - mps_C = GeneralMPSTensor(MPS(C, inds(C))) - - out = A * B - network = [mps_A, mps_B] - nodes, dict = itensorah.generate_einsum_expr([network]) - out_list = itensorah.compute_graph(nodes, dict; cutoff=1e-15, maxdim=1000) - @test isapprox(out, ITensor(out_list[1])) - - out = A * B * C - out2 = contract(mps_A, mps_B, mps_C; cutoff=1e-15, maxdim=1000) - @test isapprox(out, ITensor(out2)) -end - -@testset "test batch_tensor_contraction" begin - i = Index(2, "i") - j = Index(3, "j") - k = Index(2, "k") - A = randomITensor(i, j) - B = randomITensor(j, k) - C = randomITensor(k, i) - - function network(A) - mps_A = GeneralMPSTensor(A; cutoff=1e-15, maxdim=1000) - mps_B = GeneralMPSTensor(B; cutoff=1e-15, maxdim=1000) - mps_C = GeneralMPSTensor(C; cutoff=1e-15, maxdim=1000) - tensor_network = [mps_A, mps_B, mps_C] - out = itensorah.batch_tensor_contraction( - [tensor_network], mps_A; cutoff=1e-15, maxdim=1000 - ) - return sum(out)[] - end - grad_A = gradient(network, A) - @test isapprox(grad_A[1], B * C) -end diff --git a/test/approximate_tn_contraction/runtests.jl b/test/approximate_tn_contraction/runtests.jl index ac154b07..18f25a0f 100644 --- a/test/approximate_tn_contraction/runtests.jl +++ b/test/approximate_tn_contraction/runtests.jl @@ -5,11 +5,10 @@ using Test for filename in [ "lattice.jl", "models.jl", - "itensor_network.jl", "tree.jl", "indexgroup.jl", - # "cache.jl" - # "treetensor.jl", + "cache.jl", + "treetensor.jl", # "interface.jl", ] println("Running $filename in ApproximateTNContraction.jl") diff --git a/test/approximate_tn_contraction/treetensor.jl b/test/approximate_tn_contraction/treetensor.jl index c9f04c66..9425000e 100644 --- a/test/approximate_tn_contraction/treetensor.jl +++ b/test/approximate_tn_contraction/treetensor.jl @@ -1,68 +1,15 @@ -using ITensorNetworkAD -using AutoHOOT, ITensors, Zygote -using ITensorNetworkAD.Profiler -using ITensorNetworkAD.ITensorNetworks: - TreeTensor, - tree_approximation, +using ITensors +using ITensorNetworks.ApproximateTNContraction: + get_tensors, + OrthogonalITensor, tree_approximation_cache, inds_binary_tree, tree_embedding, approximate_contract -using ITensorNetworkAD.ITensorNetworks: +using ITensorNetworks.ApproximateTNContraction: inds_network, project_boundary, Models, ising_partition -using ITensorNetworkAD.ITensorAutoHOOT: SubNetwork, batch_tensor_contraction -const itensorah = ITensorNetworkAD.ITensorAutoHOOT - -include("utils.jl") - -@testset "test TreeTensor" begin - i = Index(2, "i") - j = Index(3, "j") - k = Index(2, "k") - l = Index(4, "l") - m = Index(5, "m") - - A = randomITensor(i, j, k) - B = randomITensor(k, l, m) - C = randomITensor(i, j, l, m) - tree_A = TreeTensor(A) - tree_B = TreeTensor(B) - tree_C = TreeTensor(C) - - out = A * B - network = [tree_A, tree_B] - nodes, dict = itensorah.generate_einsum_expr([network]) - out_list = itensorah.compute_graph(nodes, dict; cutoff=1e-15, maxdim=1000) - @test isapprox(out, ITensor(out_list[1])) - - out = A * B * C - out2 = contract(tree_A, tree_B, tree_C; cutoff=1e-15, maxdim=1000) - @test isapprox(out, ITensor(out2)) -end - -@testset "test batch_tensor_contraction" begin - i = Index(2, "i") - j = Index(3, "j") - k = Index(2, "k") - l = Index(2, "l") - m = Index(2, "m") - A = randomITensor(i, j) - B = randomITensor(j, k) - C = randomITensor(k, l) - D = randomITensor(l, m) - E = randomITensor(m, i) - - function network(A) - tensor_network = SubNetwork(SubNetwork(A, B, C), D, E) - out = itensorah.batch_tensor_contraction( - TreeTensor, [tensor_network], A; cutoff=1e-15, maxdim=1000, optimize=false - ) - return sum(out)[] - end - grad_A = gradient(network, A) - @test isapprox(grad_A[1], B * C * D * E) -end +# include("utils.jl") @testset "test tree approximation" begin i = Index(2, "i") @@ -78,18 +25,17 @@ end s = Index(2, "s") t = Index(2, "t") u = Index(2, "u") - A = randomITensor(i, n) - B = randomITensor(j, o) - AB = randomITensor(n, o, r) - C = randomITensor(k, p) - D = randomITensor(l, q) - E = randomITensor(m, u) - CD = randomITensor(p, q, s) - ABCD = randomITensor(r, s, t) - ABCDE = randomITensor(t, u) + A = OrthogonalITensor(randomITensor(i, n)) + B = OrthogonalITensor(randomITensor(j, o)) + AB = OrthogonalITensor(randomITensor(n, o, r)) + C = OrthogonalITensor(randomITensor(k, p)) + D = OrthogonalITensor(randomITensor(l, q)) + E = OrthogonalITensor(randomITensor(m, u)) + CD = OrthogonalITensor(randomITensor(p, q, s)) + ABCD = OrthogonalITensor(randomITensor(r, s, t)) + ABCDE = OrthogonalITensor(randomITensor(t, u)) btree = [[[[i], [j]], [[k], [l]]], [m]] tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] - out = tree_approximation(tensors, btree) embedding = Dict([ [i] => [A], [j] => [B], @@ -101,13 +47,13 @@ end [[[i], [j]], [[k], [l]]] => [ABCD], [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], ]) - out2 = tree_approximation_cache(embedding, btree) - @test isapprox(contract(out), contract(out2)) - @test isapprox(contract(out), contract(tensors...)) + out = tree_approximation_cache(embedding, btree) + out = get_tensors(collect(values(out))) + @test isapprox(contract(out...), contract(get_tensors(tensors)...)) end @testset "test MPS times MPO" begin - N = (10, 3) + N = (5, 3) linkdim = 3 cutoff = 1e-15 tn_inds = inds_network(N...; linkdims=linkdim) @@ -116,258 +62,251 @@ end tn = project_boundary(tn, state) x, A = tn[:, 1], tn[:, 2] out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) - out2 = batch_tensor_contraction( - TreeTensor, - [SubNetwork(SubNetwork(x), SubNetwork(A))]; - cutoff=cutoff, - maxdim=linkdim * linkdim, - ) + out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) tsr_true = contract(out_true...) tsr_nrmsquare = (tsr_true * tsr_true)[1] - @test isapprox(tsr_true, ITensor(out2[1])) + @test isapprox(tsr_true, contract(out2...)) maxdims = [2, 4, 6, 8] for dim in maxdims out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) - out2 = batch_tensor_contraction( - TreeTensor, [SubNetwork(SubNetwork(x), SubNetwork(A))]; cutoff=cutoff, maxdim=dim - ) + out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) residual1 = tsr_true - contract(out...) - residual2 = tsr_true - ITensor(out2[1]) + residual2 = tsr_true - contract(out2...) error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") end end -@testset "test inds_binary_tree" begin - i = Index(2, "i") - j = Index(2, "j") - k = Index(2, "k") - l = Index(2, "l") - m = Index(2, "m") - n = Index(2, "n") - o = Index(2, "o") - p = Index(2, "p") - - T = randomITensor(i, j, k, l, m, n, o, p) - M = MPS(T, (i, j, k, l, m, n, o, p); cutoff=1e-5, maxdim=500) - network = M[:] +# @testset "test inds_binary_tree" begin +# i = Index(2, "i") +# j = Index(2, "j") +# k = Index(2, "k") +# l = Index(2, "l") +# m = Index(2, "m") +# n = Index(2, "n") +# o = Index(2, "o") +# p = Index(2, "p") - out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") - @test length(out) == 2 - out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") - @test length(out) == 2 - out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") - @test length(out) == 2 -end +# T = randomITensor(i, j, k, l, m, n, o, p) +# M = MPS(T, (i, j, k, l, m, n, o, p); cutoff=1e-5, maxdim=500) +# network = M[:] -@testset "test inds_binary_tree of a 2D network" begin - N = (8, 8, 3) - linkdim = 2 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) - network = vec(tn[:, :, 1]) - out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") - @test length(out) == 2 - out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") - @test length(out) == 2 - out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") - @test length(out) == 2 -end +# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") +# @test length(out) == 2 +# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") +# @test length(out) == 2 +# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") +# @test length(out) == 2 +# end -@testset "test tree embedding" begin - i = Index(2, "i") - j = Index(2, "j") - k = Index(2, "k") - l = Index(2, "l") - m = Index(2, "m") - T = randomITensor(i, j, k, l, m) - M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) - network = M[:] - out1 = contract(network...) - inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") - tnet_dict = tree_embedding(network, inds_btree) - network2 = vcat(collect(values(tnet_dict))...) - out2 = contract(network2...) - i1 = noncommoninds(network...) - i2 = noncommoninds(network2...) - @test (length(i1) == length(i2)) - @test isapprox(out1, out2) -end +# @testset "test inds_binary_tree of a 2D network" begin +# N = (8, 8, 3) +# linkdim = 2 +# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +# tn = map(inds -> randomITensor(inds...), tn_inds) +# network = vec(tn[:, :, 1]) +# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") +# @test length(out) == 2 +# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") +# @test length(out) == 2 +# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") +# @test length(out) == 2 +# end -function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) - N = size(tn) - out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) - network = SubNetwork(tn[:, 1]) - for i in 2:(N[2]) - network = SubNetwork(network, SubNetwork(tn[:, i])) - end - out2 = batch_tensor_contraction( - TreeTensor, [network]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, optimize=false - ) - return out[], ITensor(out2[1])[] -end +# @testset "test tree embedding" begin +# i = Index(2, "i") +# j = Index(2, "j") +# k = Index(2, "k") +# l = Index(2, "l") +# m = Index(2, "m") +# T = randomITensor(i, j, k, l, m) +# M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) +# network = M[:] +# out1 = contract(network...) +# inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") +# tnet_dict = tree_embedding(network, inds_btree) +# network2 = vcat(collect(values(tnet_dict))...) +# out2 = contract(network2...) +# i1 = noncommoninds(network...) +# i2 = noncommoninds(network2...) +# @test (length(i1) == length(i2)) +# @test isapprox(out1, out2) +# end -@testset "test PEPS" begin - N = (8, 8) #(12, 12) - linkdim = 2 - cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) - # tn = ising_partition(N, linkdim) +# function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) +# N = size(tn) +# out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) +# network = SubNetwork(tn[:, 1]) +# for i in 2:(N[2]) +# network = SubNetwork(network, SubNetwork(tn[:, i])) +# end +# out2 = batch_tensor_contraction( +# TreeTensor, [network]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, optimize=false +# ) +# return out[], ITensor(out2[1])[] +# end - ITensors.set_warn_order(100) - maxdim = linkdim^N[2] - maxsize = maxdim * maxdim * linkdim - out_true, out2 = benchmark_peps_contraction( - tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize - ) - print(out_true, out2) - @test abs((out_true - out2) / out_true) < 1e-3 +# @testset "test PEPS" begin +# N = (8, 8) #(12, 12) +# linkdim = 2 +# cutoff = 1e-15 +# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +# tn = map(inds -> randomITensor(inds...), tn_inds) +# # tn = ising_partition(N, linkdim) - maxdims = [i for i in 2:16] - for dim in maxdims - size = dim * dim * linkdim - out, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) - error1 = abs((out - out_true) / out_true) - error2 = abs((out2 - out_true) / out_true) - print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") - end -end +# ITensors.set_warn_order(100) +# maxdim = linkdim^N[2] +# maxsize = maxdim * maxdim * linkdim +# out_true, out2 = benchmark_peps_contraction( +# tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize +# ) +# print(out_true, out2) +# @test abs((out_true - out2) / out_true) < 1e-3 -function benchmark_3D_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) - # TODO: this sequential MPS doesn't give the desired tree when each tn[i] is a slice of the 2D surface - out = contract( - tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" - ) - for i in 2:length(tn) - out = contract( - out, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" - ) - end - out2 = contract(tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps") - for i in 2:length(tn) - out2 = contract( - out2, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps" - ) - end - return out[], out2[] -end +# maxdims = [i for i in 2:16] +# for dim in maxdims +# size = dim * dim * linkdim +# out, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) +# error1 = abs((out - out_true) / out_true) +# error2 = abs((out2 - out_true) / out_true) +# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") +# end +# end -@testset "test 3-D cube with 2D grouping" begin - do_profile(true) - N = (3, 3, 4) #(12, 12) - linkdim = 2 - nrows = prod([s for s in N[1:(length(N) - 1)]]) - ncols = N[length(N)] - maxdim = linkdim^(floor(nrows)) - cutoff = 1e-15 - # tn = ising_partition(N, linkdim) - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) - # snake mapping - for k in 1:N[3] - for j in 1:N[2] - if iseven(j) - tn[:, j, k] = reverse(tn[:, j, k]) - end - end - end - tn = reshape(tn, (nrows, ncols)) - tn = [TreeTensor(tn[:, i]) for i in 1:ncols] - @info size(tn) - ITensors.set_warn_order(100) - maxsize = maxdim * maxdim * linkdim - out1, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize) - profile_exit() - print(out1, out2) - @test abs((out1 - out2) / out1) < 1e-3 - maxdims = [3, 5, 8, 10, 11, 12, 13, 14, 15, 16, 20, 31, 32] - for dim in maxdims - size = dim * dim * linkdim - out, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) - error1 = abs((out - out1) / out1) - error2 = abs((out2 - out1) / out1) - print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") - end -end +# function benchmark_3D_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) +# # TODO: this sequential MPS doesn't give the desired tree when each tn[i] is a slice of the 2D surface +# out = contract( +# tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" +# ) +# for i in 2:length(tn) +# out = contract( +# out, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" +# ) +# end +# out2 = contract(tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps") +# for i in 2:length(tn) +# out2 = contract( +# out2, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps" +# ) +# end +# return out[], out2[] +# end -@testset "test 3-D cube with 1D grouping" begin - ITensors.set_warn_order(100) - do_profile(true) - N = (3, 3, 3) # (5, 5, 5) - linkdim = 2 - maxdim = linkdim^(floor(N[1] * N[2])) - cutoff = 1e-15 - # tn = ising_partition(N, linkdim) - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) - tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = tn[:, 1] - for i in 2:(N[2] * N[3]) - tntree = [tntree, tn[:, i]] - end - approximate_contract( - tntree; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mincut" - ) - profile_exit() -end +# @testset "test 3-D cube with 2D grouping" begin +# do_profile(true) +# N = (3, 3, 4) #(12, 12) +# linkdim = 2 +# nrows = prod([s for s in N[1:(length(N) - 1)]]) +# ncols = N[length(N)] +# maxdim = linkdim^(floor(nrows)) +# cutoff = 1e-15 +# # tn = ising_partition(N, linkdim) +# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +# tn = map(inds -> randomITensor(inds...), tn_inds) +# # snake mapping +# for k in 1:N[3] +# for j in 1:N[2] +# if iseven(j) +# tn[:, j, k] = reverse(tn[:, j, k]) +# end +# end +# end +# tn = reshape(tn, (nrows, ncols)) +# tn = [TreeTensor(tn[:, i]) for i in 1:ncols] +# @info size(tn) +# ITensors.set_warn_order(100) +# maxsize = maxdim * maxdim * linkdim +# out1, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize) +# profile_exit() +# print(out1, out2) +# @test abs((out1 - out2) / out1) < 1e-3 +# maxdims = [3, 5, 8, 10, 11, 12, 13, 14, 15, 16, 20, 31, 32] +# for dim in maxdims +# size = dim * dim * linkdim +# out, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) +# error1 = abs((out - out1) / out1) +# error2 = abs((out2 - out1) / out1) +# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") +# end +# end -#TODO -# @testset "test 3-D cube with DMRG-like algorithm" begin +# @testset "test 3-D cube with 1D grouping" begin # ITensors.set_warn_order(100) # do_profile(true) -# N = (5, 5, 3) # (5, 5, 5) +# N = (3, 3, 3) # (5, 5, 5) # linkdim = 2 # maxdim = linkdim^(floor(N[1] * N[2])) # cutoff = 1e-15 -# tn = ising_partition(N, linkdim) -# function build_tree(i) -# tree = tn[:, 1, i] -# for j in 2:N[2] -# tree = [tree, tn[:, j, i]] -# end -# return tree +# # tn = ising_partition(N, linkdim) +# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +# tn = map(inds -> randomITensor(inds...), tn_inds) +# tn = reshape(tn, (N[1], N[2] * N[3])) +# tntree = tn[:, 1] +# for i in 2:(N[2] * N[3]) +# tntree = [tntree, tn[:, i]] # end -# tn1, _ = approximate_contract( -# build_tree(1); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# ) -# tn2, _ = approximate_contract( -# build_tree(2); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# ) -# tn3, _ = approximate_contract( -# build_tree(3); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# approximate_contract( +# tntree; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mincut" # ) -# tn12, _ = approximate_contract( -# [tn1..., tn2...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# ) -# out, _ = approximate_contract( -# [tn12..., tn3...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# ) -# profile_exit() +# profile_exit() # end -@testset "benchmark PEPS" begin - N = (8, 8) #(12, 12) - linkdim = 10 - cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +# #TODO +# # @testset "test 3-D cube with DMRG-like algorithm" begin +# # ITensors.set_warn_order(100) +# # do_profile(true) +# # N = (5, 5, 3) # (5, 5, 5) +# # linkdim = 2 +# # maxdim = linkdim^(floor(N[1] * N[2])) +# # cutoff = 1e-15 +# # tn = ising_partition(N, linkdim) +# # function build_tree(i) +# # tree = tn[:, 1, i] +# # for j in 2:N[2] +# # tree = [tree, tn[:, j, i]] +# # end +# # return tree +# # end +# # tn1, _ = approximate_contract( +# # build_tree(1); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# # ) +# # tn2, _ = approximate_contract( +# # build_tree(2); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# # ) +# # tn3, _ = approximate_contract( +# # build_tree(3); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# # ) +# # tn12, _ = approximate_contract( +# # [tn1..., tn2...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# # ) +# # out, _ = approximate_contract( +# # [tn12..., tn3...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" +# # ) +# # profile_exit() +# # end - dim = 20 - size = dim * dim * linkdim - # warmup - for i in 1:2 - tn = map(inds -> randomITensor(inds...), tn_inds) - ITensors.set_warn_order(100) - benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) - end +# @testset "benchmark PEPS" begin +# N = (8, 8) #(12, 12) +# linkdim = 10 +# cutoff = 1e-15 +# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - do_profile(true) - for i in 1:3 - tn = map(inds -> randomITensor(inds...), tn_inds) - ITensors.set_warn_order(100) - benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) - end - profile_exit() -end +# dim = 20 +# size = dim * dim * linkdim +# # warmup +# for i in 1:2 +# tn = map(inds -> randomITensor(inds...), tn_inds) +# ITensors.set_warn_order(100) +# benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) +# end + +# do_profile(true) +# for i in 1:3 +# tn = map(inds -> randomITensor(inds...), tn_inds) +# ITensors.set_warn_order(100) +# benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) +# end +# profile_exit() +# end diff --git a/test/runtests.jl b/test/runtests.jl index f944309e..f54db9df 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -38,8 +38,8 @@ using Test g = named_grid(dims) s = siteinds("S=1/2", g) ψ = ITensorNetwork(s, v -> "↑") - tn = inner(ψ, sim(dag(ψ), sites=[])) - tn_2 = contract(tn, (2, 1, 2) => (1, 1, 2)) + tn = inner(ψ, sim(dag(ψ); sites=[])) + tn_2 = contract(tn, (2, 1, 2) => (1, 1, 2)) @test !has_vertex(tn_2, (2, 1, 2)) @test tn_2[1, 1, 2] ≈ tn[2, 1, 2] * tn[1, 1, 2] end @@ -50,7 +50,7 @@ using Test s = siteinds("S=1/2", g) ψ = ITensorNetwork(s, v -> "↑") rem_vertex!(ψ, (1, 2)) - tn = inner(ψ, sim(dag(ψ), sites=[])) + tn = inner(ψ, sim(dag(ψ); sites=[])) @test !has_vertex(tn, (1, 1, 2)) @test !has_vertex(tn, (2, 1, 2)) @test has_vertex(tn, (1, 1, 1)) From 1046d00f83a8005ba7bc54274e15b80890986d5f Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Sat, 26 Nov 2022 14:53:38 -0600 Subject: [PATCH 05/24] [ApproxContraction] get 3D cube working --- Project.toml | 3 +- .../contract/contract.jl | 16 +- .../contract/mincut_tree.jl | 3 + src/ITensorNetworks.jl | 4 +- .../{treetensor.jl => contract.jl} | 192 ++++++++++-------- test/approximate_tn_contraction/runtests.jl | 12 +- 6 files changed, 127 insertions(+), 103 deletions(-) rename test/approximate_tn_contraction/{treetensor.jl => contract.jl} (69%) diff --git a/Project.toml b/Project.toml index ed7f6c40..15bd1be5 100644 --- a/Project.toml +++ b/Project.toml @@ -7,14 +7,15 @@ version = "0.1.0" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DataGraphs = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" -GraphRecipes = "bd48cda9-67a9-57be-86fa-5b3c104eda73" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +GraphRecipes = "bd48cda9-67a9-57be-86fa-5b3c104eda73" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MultiDimDictionaries = "87ff4268-a46e-478f-b30a-76b83dd64e3c" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" +OMEinsumContractionOrders = "6f22d1fd-8eed-4bb7-9776-e7d684900715" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Requires = "ae029012-a4dd-5104-9daa-d747884805df" diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index ce2f7119..0e067eb9 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -1,5 +1,7 @@ using Graphs, GraphsFlows, Combinatorics, SimpleWeightedGraphs using GraphRecipes, Plots +using OMEinsumContractionOrders +using ITensorNetworks: contraction_sequence function Base.show(io::IO, tensor::ITensor) return print(io, string(inds(tensor))) @@ -11,8 +13,6 @@ include("tensornetwork_graph.jl") include("mincut_tree.jl") include("tree_embedding.jl") -ITensors.enable_contraction_sequence_optimization() - function optcontract(t_list::Vector) @timeit timer "optcontract" begin # TODO: make this support orthotensor @@ -24,8 +24,12 @@ function optcontract(t_list::Vector) # for t in t_list # @info "size of t is", size(t) # end - # TODO: use optimized contraction path - output = contract(t_list) + @timeit timer "contraction_sequence" begin + seq = contraction_sequence(t_list; alg="greedy") + end + @timeit timer "contract" begin + output = contract(t_list; sequence=seq) + end return OrthogonalITensor(output) end end @@ -766,6 +770,7 @@ function approximate_contract(ctree::Vector; kwargs...) ctree_to_igs, ctree_to_adj_tree, ig_to_ig_tree = _approximate_contract_pre_process( tn_leaves, ctrees ) + # TODO: move these to preprocess ctree_to_contract_igs = Dict{Vector,Vector{IndexGroup}}() for c in ctrees contract_igs = intersect(ctree_to_igs[c[1]], ctree_to_igs[c[2]]) @@ -776,7 +781,8 @@ function approximate_contract(ctree::Vector; kwargs...) ctree_to_contract_igs[ctrees[end]] = ctree_to_igs[ctrees[end]] # mapping each contraction tree to a tensor network ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() - for c in ctrees + for (ii, c) in enumerate(ctrees) + @info ii, "th tree approximation" if ctree_to_igs[c] == [] @assert c == ctrees[end] tn1 = get_child_tn(ctree_to_tn_tree, c[1]) diff --git a/src/ApproximateTNContraction/contract/mincut_tree.jl b/src/ApproximateTNContraction/contract/mincut_tree.jl index da784526..4f196bca 100644 --- a/src/ApproximateTNContraction/contract/mincut_tree.jl +++ b/src/ApproximateTNContraction/contract/mincut_tree.jl @@ -151,6 +151,9 @@ function mps_inds!(tng::TensorNetworkGraph, outinds::Vector) if length(outinds) == 1 return outinds[1] end + if length(outinds) == 2 + return outinds + end new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) outinds = update!(tng, outinds, new_edge, minval) first_ind = new_edge diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 13c9fe77..1ea30443 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -130,8 +130,6 @@ include(joinpath("treetensornetwork", "treetensornetwork.jl")) include("exports.jl") -include("ApproximateTNContraction/ApproximateTNContraction.jl") - function __init__() @require KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" include( joinpath("requires", "kahypar.jl") @@ -144,4 +142,6 @@ function __init__() ) end +include("ApproximateTNContraction/ApproximateTNContraction.jl") + end diff --git a/test/approximate_tn_contraction/treetensor.jl b/test/approximate_tn_contraction/contract.jl similarity index 69% rename from test/approximate_tn_contraction/treetensor.jl rename to test/approximate_tn_contraction/contract.jl index 9425000e..3869c2a3 100644 --- a/test/approximate_tn_contraction/treetensor.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -1,4 +1,4 @@ -using ITensors +using ITensors, TimerOutputs using ITensorNetworks.ApproximateTNContraction: get_tensors, OrthogonalITensor, @@ -7,77 +7,77 @@ using ITensorNetworks.ApproximateTNContraction: tree_embedding, approximate_contract using ITensorNetworks.ApproximateTNContraction: - inds_network, project_boundary, Models, ising_partition + timer, inds_network, project_boundary, Models, ising_partition # include("utils.jl") -@testset "test tree approximation" begin - i = Index(2, "i") - j = Index(2, "j") - k = Index(2, "k") - l = Index(2, "l") - m = Index(2, "m") - n = Index(2, "n") - o = Index(2, "o") - p = Index(2, "p") - q = Index(2, "q") - r = Index(2, "r") - s = Index(2, "s") - t = Index(2, "t") - u = Index(2, "u") - A = OrthogonalITensor(randomITensor(i, n)) - B = OrthogonalITensor(randomITensor(j, o)) - AB = OrthogonalITensor(randomITensor(n, o, r)) - C = OrthogonalITensor(randomITensor(k, p)) - D = OrthogonalITensor(randomITensor(l, q)) - E = OrthogonalITensor(randomITensor(m, u)) - CD = OrthogonalITensor(randomITensor(p, q, s)) - ABCD = OrthogonalITensor(randomITensor(r, s, t)) - ABCDE = OrthogonalITensor(randomITensor(t, u)) - btree = [[[[i], [j]], [[k], [l]]], [m]] - tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] - embedding = Dict([ - [i] => [A], - [j] => [B], - [k] => [C], - [l] => [D], - [m] => [E], - [[i], [j]] => [AB], - [[k], [l]] => [CD], - [[[i], [j]], [[k], [l]]] => [ABCD], - [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], - ]) - out = tree_approximation_cache(embedding, btree) - out = get_tensors(collect(values(out))) - @test isapprox(contract(out...), contract(get_tensors(tensors)...)) -end +# @testset "test tree approximation" begin +# i = Index(2, "i") +# j = Index(2, "j") +# k = Index(2, "k") +# l = Index(2, "l") +# m = Index(2, "m") +# n = Index(2, "n") +# o = Index(2, "o") +# p = Index(2, "p") +# q = Index(2, "q") +# r = Index(2, "r") +# s = Index(2, "s") +# t = Index(2, "t") +# u = Index(2, "u") +# A = OrthogonalITensor(randomITensor(i, n)) +# B = OrthogonalITensor(randomITensor(j, o)) +# AB = OrthogonalITensor(randomITensor(n, o, r)) +# C = OrthogonalITensor(randomITensor(k, p)) +# D = OrthogonalITensor(randomITensor(l, q)) +# E = OrthogonalITensor(randomITensor(m, u)) +# CD = OrthogonalITensor(randomITensor(p, q, s)) +# ABCD = OrthogonalITensor(randomITensor(r, s, t)) +# ABCDE = OrthogonalITensor(randomITensor(t, u)) +# btree = [[[[i], [j]], [[k], [l]]], [m]] +# tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] +# embedding = Dict([ +# [i] => [A], +# [j] => [B], +# [k] => [C], +# [l] => [D], +# [m] => [E], +# [[i], [j]] => [AB], +# [[k], [l]] => [CD], +# [[[i], [j]], [[k], [l]]] => [ABCD], +# [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], +# ]) +# out = tree_approximation_cache(embedding, btree) +# out = get_tensors(collect(values(out))) +# @test isapprox(contract(out...), contract(get_tensors(tensors)...)) +# end -@testset "test MPS times MPO" begin - N = (5, 3) - linkdim = 3 - cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim) - tn = map(inds -> randomITensor(inds...), tn_inds) - state = 1 - tn = project_boundary(tn, state) - x, A = tn[:, 1], tn[:, 2] - out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) - out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) - tsr_true = contract(out_true...) - tsr_nrmsquare = (tsr_true * tsr_true)[1] - @test isapprox(tsr_true, contract(out2...)) +# @testset "test MPS times MPO" begin +# N = (5, 3) +# linkdim = 3 +# cutoff = 1e-15 +# tn_inds = inds_network(N...; linkdims=linkdim) +# tn = map(inds -> randomITensor(inds...), tn_inds) +# state = 1 +# tn = project_boundary(tn, state) +# x, A = tn[:, 1], tn[:, 2] +# out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) +# out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) +# tsr_true = contract(out_true...) +# tsr_nrmsquare = (tsr_true * tsr_true)[1] +# @test isapprox(tsr_true, contract(out2...)) - maxdims = [2, 4, 6, 8] - for dim in maxdims - out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) - out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) - residual1 = tsr_true - contract(out...) - residual2 = tsr_true - contract(out2...) - error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) - error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) - print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") - end -end +# maxdims = [2, 4, 6, 8] +# for dim in maxdims +# out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) +# out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) +# residual1 = tsr_true - contract(out...) +# residual2 = tsr_true - contract(out2...) +# error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) +# error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) +# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") +# end +# end # @testset "test inds_binary_tree" begin # i = Index(2, "i") @@ -195,7 +195,7 @@ end # end # @testset "test 3-D cube with 2D grouping" begin -# do_profile(true) +# reset_timer!(timer) # N = (3, 3, 4) #(12, 12) # linkdim = 2 # nrows = prod([s for s in N[1:(length(N) - 1)]]) @@ -219,7 +219,7 @@ end # ITensors.set_warn_order(100) # maxsize = maxdim * maxdim * linkdim # out1, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize) -# profile_exit() +# show(timer) # print(out1, out2) # @test abs((out1 - out2) / out1) < 1e-3 # maxdims = [3, 5, 8, 10, 11, 12, 13, 14, 15, 16, 20, 31, 32] @@ -232,26 +232,40 @@ end # end # end -# @testset "test 3-D cube with 1D grouping" begin -# ITensors.set_warn_order(100) -# do_profile(true) -# N = (3, 3, 3) # (5, 5, 5) -# linkdim = 2 -# maxdim = linkdim^(floor(N[1] * N[2])) -# cutoff = 1e-15 -# # tn = ising_partition(N, linkdim) -# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) -# tn = map(inds -> randomITensor(inds...), tn_inds) -# tn = reshape(tn, (N[1], N[2] * N[3])) -# tntree = tn[:, 1] -# for i in 2:(N[2] * N[3]) -# tntree = [tntree, tn[:, i]] -# end -# approximate_contract( -# tntree; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mincut" -# ) -# profile_exit() -# end +@testset "test 3-D cube with 1D grouping" begin + ITensors.set_warn_order(100) + reset_timer!(timer) + N = (5, 5, 5) + linkdim = 2 + maxdim = 5 + cutoff = 1e-15 + # tn = ising_partition(N, linkdim) + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + tn = reshape(tn, (N[1], N[2] * N[3])) + tntree = tn[:, 1] + for i in 2:(N[2] * N[3]) + tntree = [tntree, tn[:, i]] + end + out = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + @info "out is", out[1][1] + show(timer) + # after warmup, start to benchmark + reset_timer!(timer) + N = (5, 5, 5) + linkdim = 2 + maxdim = 5 + cutoff = 1e-15 + tn = ising_partition(N, linkdim) + tn = reshape(tn, (N[1], N[2] * N[3])) + tntree = tn[:, 1] + for i in 2:(N[2] * N[3]) + tntree = [tntree, tn[:, i]] + end + out = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + @info "out is", out[1][1] + show(timer) +end # #TODO # # @testset "test 3-D cube with DMRG-like algorithm" begin diff --git a/test/approximate_tn_contraction/runtests.jl b/test/approximate_tn_contraction/runtests.jl index 18f25a0f..9283b6fb 100644 --- a/test/approximate_tn_contraction/runtests.jl +++ b/test/approximate_tn_contraction/runtests.jl @@ -3,12 +3,12 @@ using Test @testset "ApproximateTNContraction.jl" begin for filename in [ - "lattice.jl", - "models.jl", - "tree.jl", - "indexgroup.jl", - "cache.jl", - "treetensor.jl", + # "lattice.jl", + # "models.jl", + # "tree.jl", + # "indexgroup.jl", + # "cache.jl", + "contract.jl", # "interface.jl", ] println("Running $filename in ApproximateTNContraction.jl") From 34421771694328e75240c2fb818bccb065c01ee8 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Sat, 26 Nov 2022 16:25:49 -0600 Subject: [PATCH 06/24] [ApproxContraction] get contract tests working --- test/approximate_tn_contraction/contract.jl | 440 ++++++++------------ test/approximate_tn_contraction/utils.jl | 53 +-- test/runtests.jl | 8 +- 3 files changed, 199 insertions(+), 302 deletions(-) diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 3869c2a3..06ce20ef 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -7,235 +7,166 @@ using ITensorNetworks.ApproximateTNContraction: tree_embedding, approximate_contract using ITensorNetworks.ApproximateTNContraction: - timer, inds_network, project_boundary, Models, ising_partition - -# include("utils.jl") - -# @testset "test tree approximation" begin -# i = Index(2, "i") -# j = Index(2, "j") -# k = Index(2, "k") -# l = Index(2, "l") -# m = Index(2, "m") -# n = Index(2, "n") -# o = Index(2, "o") -# p = Index(2, "p") -# q = Index(2, "q") -# r = Index(2, "r") -# s = Index(2, "s") -# t = Index(2, "t") -# u = Index(2, "u") -# A = OrthogonalITensor(randomITensor(i, n)) -# B = OrthogonalITensor(randomITensor(j, o)) -# AB = OrthogonalITensor(randomITensor(n, o, r)) -# C = OrthogonalITensor(randomITensor(k, p)) -# D = OrthogonalITensor(randomITensor(l, q)) -# E = OrthogonalITensor(randomITensor(m, u)) -# CD = OrthogonalITensor(randomITensor(p, q, s)) -# ABCD = OrthogonalITensor(randomITensor(r, s, t)) -# ABCDE = OrthogonalITensor(randomITensor(t, u)) -# btree = [[[[i], [j]], [[k], [l]]], [m]] -# tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] -# embedding = Dict([ -# [i] => [A], -# [j] => [B], -# [k] => [C], -# [l] => [D], -# [m] => [E], -# [[i], [j]] => [AB], -# [[k], [l]] => [CD], -# [[[i], [j]], [[k], [l]]] => [ABCD], -# [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], -# ]) -# out = tree_approximation_cache(embedding, btree) -# out = get_tensors(collect(values(out))) -# @test isapprox(contract(out...), contract(get_tensors(tensors)...)) -# end - -# @testset "test MPS times MPO" begin -# N = (5, 3) -# linkdim = 3 -# cutoff = 1e-15 -# tn_inds = inds_network(N...; linkdims=linkdim) -# tn = map(inds -> randomITensor(inds...), tn_inds) -# state = 1 -# tn = project_boundary(tn, state) -# x, A = tn[:, 1], tn[:, 2] -# out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) -# out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) -# tsr_true = contract(out_true...) -# tsr_nrmsquare = (tsr_true * tsr_true)[1] -# @test isapprox(tsr_true, contract(out2...)) - -# maxdims = [2, 4, 6, 8] -# for dim in maxdims -# out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) -# out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) -# residual1 = tsr_true - contract(out...) -# residual2 = tsr_true - contract(out2...) -# error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) -# error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) -# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") -# end -# end - -# @testset "test inds_binary_tree" begin -# i = Index(2, "i") -# j = Index(2, "j") -# k = Index(2, "k") -# l = Index(2, "l") -# m = Index(2, "m") -# n = Index(2, "n") -# o = Index(2, "o") -# p = Index(2, "p") - -# T = randomITensor(i, j, k, l, m, n, o, p) -# M = MPS(T, (i, j, k, l, m, n, o, p); cutoff=1e-5, maxdim=500) -# network = M[:] - -# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") -# @test length(out) == 2 -# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") -# @test length(out) == 2 -# out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") -# @test length(out) == 2 -# end - -# @testset "test inds_binary_tree of a 2D network" begin -# N = (8, 8, 3) -# linkdim = 2 -# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) -# tn = map(inds -> randomITensor(inds...), tn_inds) -# network = vec(tn[:, :, 1]) -# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") -# @test length(out) == 2 -# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") -# @test length(out) == 2 -# out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") -# @test length(out) == 2 -# end + timer, inds_network, Models, ising_partition + +include("utils.jl") + +@testset "test tree approximation" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + n = Index(2, "n") + o = Index(2, "o") + p = Index(2, "p") + q = Index(2, "q") + r = Index(2, "r") + s = Index(2, "s") + t = Index(2, "t") + u = Index(2, "u") + A = OrthogonalITensor(randomITensor(i, n)) + B = OrthogonalITensor(randomITensor(j, o)) + AB = OrthogonalITensor(randomITensor(n, o, r)) + C = OrthogonalITensor(randomITensor(k, p)) + D = OrthogonalITensor(randomITensor(l, q)) + E = OrthogonalITensor(randomITensor(m, u)) + CD = OrthogonalITensor(randomITensor(p, q, s)) + ABCD = OrthogonalITensor(randomITensor(r, s, t)) + ABCDE = OrthogonalITensor(randomITensor(t, u)) + btree = [[[[i], [j]], [[k], [l]]], [m]] + tensors = [A, B, C, D, E, AB, CD, ABCD, ABCDE] + embedding = Dict([ + [i] => [A], + [j] => [B], + [k] => [C], + [l] => [D], + [m] => [E], + [[i], [j]] => [AB], + [[k], [l]] => [CD], + [[[i], [j]], [[k], [l]]] => [ABCD], + [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], + ]) + out = tree_approximation_cache(embedding, btree) + out = get_tensors(collect(values(out))) + @test isapprox(contract(out...), contract(get_tensors(tensors)...)) +end -# @testset "test tree embedding" begin -# i = Index(2, "i") -# j = Index(2, "j") -# k = Index(2, "k") -# l = Index(2, "l") -# m = Index(2, "m") -# T = randomITensor(i, j, k, l, m) -# M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) -# network = M[:] -# out1 = contract(network...) -# inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") -# tnet_dict = tree_embedding(network, inds_btree) -# network2 = vcat(collect(values(tnet_dict))...) -# out2 = contract(network2...) -# i1 = noncommoninds(network...) -# i2 = noncommoninds(network2...) -# @test (length(i1) == length(i2)) -# @test isapprox(out1, out2) -# end +@testset "test MPS times MPO" begin + N = (5, 3) + linkdim = 3 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + x, A = tn[:, 1], tn[:, 2] + out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) + out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) + tsr_true = contract(out_true...) + tsr_nrmsquare = (tsr_true * tsr_true)[1] + @test isapprox(tsr_true, contract(out2...)) + + maxdims = [2, 4, 6, 8] + for dim in maxdims + out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) + out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) + residual1 = tsr_true - contract(out...) + residual2 = tsr_true - contract(out2...) + error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) + error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) + print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") + end +end -# function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) -# N = size(tn) -# out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) -# network = SubNetwork(tn[:, 1]) -# for i in 2:(N[2]) -# network = SubNetwork(network, SubNetwork(tn[:, i])) -# end -# out2 = batch_tensor_contraction( -# TreeTensor, [network]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, optimize=false -# ) -# return out[], ITensor(out2[1])[] -# end +@testset "test inds_binary_tree" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + n = Index(2, "n") + o = Index(2, "o") + p = Index(2, "p") + + T = randomITensor(i, j, k, l, m, n, o, p) + M = MPS(T, (i, j, k, l, m, n, o, p); cutoff=1e-5, maxdim=500) + network = M[:] + + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") + @test length(out) == 2 + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") + @test length(out) == 2 + out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") + @test length(out) == 2 +end -# @testset "test PEPS" begin -# N = (8, 8) #(12, 12) -# linkdim = 2 -# cutoff = 1e-15 -# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) -# tn = map(inds -> randomITensor(inds...), tn_inds) -# # tn = ising_partition(N, linkdim) +@testset "test inds_binary_tree of a 2D network" begin + N = (8, 8, 3) + linkdim = 2 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + network = vec(tn[:, :, 1]) + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") + @test length(out) == 2 + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") + @test length(out) == 2 + out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") + @test length(out) == 2 +end -# ITensors.set_warn_order(100) -# maxdim = linkdim^N[2] -# maxsize = maxdim * maxdim * linkdim -# out_true, out2 = benchmark_peps_contraction( -# tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize -# ) -# print(out_true, out2) -# @test abs((out_true - out2) / out_true) < 1e-3 +@testset "test tree embedding" begin + i = Index(2, "i") + j = Index(2, "j") + k = Index(2, "k") + l = Index(2, "l") + m = Index(2, "m") + T = randomITensor(i, j, k, l, m) + M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) + network = M[:] + out1 = contract(network...) + inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") + tnet_dict = tree_embedding(network, inds_btree) + network2 = vcat(collect(values(tnet_dict))...) + out2 = contract(network2...) + i1 = noncommoninds(network...) + i2 = noncommoninds(network2...) + @test (length(i1) == length(i2)) + @test isapprox(out1, out2) +end -# maxdims = [i for i in 2:16] -# for dim in maxdims -# size = dim * dim * linkdim -# out, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) -# error1 = abs((out - out_true) / out_true) -# error2 = abs((out2 - out_true) / out_true) -# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") -# end -# end +function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000) + out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) + out2 = contract_line_group(tn; cutoff=cutoff, maxdim=maxdim) + return out[], out2[1][1] +end -# function benchmark_3D_contraction(tn; cutoff=1e-15, maxdim=1000, maxsize=10^15) -# # TODO: this sequential MPS doesn't give the desired tree when each tn[i] is a slice of the 2D surface -# out = contract( -# tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" -# ) -# for i in 2:length(tn) -# out = contract( -# out, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="sequential-mps" -# ) -# end -# out2 = contract(tn[1]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps") -# for i in 2:length(tn) -# out2 = contract( -# out2, tn[i]; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize, algorithm="mps" -# ) -# end -# return out[], out2[] -# end +@testset "test PEPS" begin + N = (8, 8) + linkdim = 2 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + tn = map(inds -> randomITensor(inds...), tn_inds) + # tn = ising_partition(N, linkdim) -# @testset "test 3-D cube with 2D grouping" begin -# reset_timer!(timer) -# N = (3, 3, 4) #(12, 12) -# linkdim = 2 -# nrows = prod([s for s in N[1:(length(N) - 1)]]) -# ncols = N[length(N)] -# maxdim = linkdim^(floor(nrows)) -# cutoff = 1e-15 -# # tn = ising_partition(N, linkdim) -# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) -# tn = map(inds -> randomITensor(inds...), tn_inds) -# # snake mapping -# for k in 1:N[3] -# for j in 1:N[2] -# if iseven(j) -# tn[:, j, k] = reverse(tn[:, j, k]) -# end -# end -# end -# tn = reshape(tn, (nrows, ncols)) -# tn = [TreeTensor(tn[:, i]) for i in 1:ncols] -# @info size(tn) -# ITensors.set_warn_order(100) -# maxsize = maxdim * maxdim * linkdim -# out1, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize) -# show(timer) -# print(out1, out2) -# @test abs((out1 - out2) / out1) < 1e-3 -# maxdims = [3, 5, 8, 10, 11, 12, 13, 14, 15, 16, 20, 31, 32] -# for dim in maxdims -# size = dim * dim * linkdim -# out, out2 = benchmark_3D_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) -# error1 = abs((out - out1) / out1) -# error2 = abs((out2 - out1) / out1) -# print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") -# end -# end + ITensors.set_warn_order(100) + maxdim = linkdim^N[2] + out_true, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=maxdim) + print(out_true, out2) + @test abs((out_true - out2) / out_true) < 1e-3 + + maxdims = [i for i in 2:16] + for dim in maxdims + size = dim * dim * linkdim + out, out2 = benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim) + error1 = abs((out - out_true) / out_true) + error2 = abs((out2 - out_true) / out_true) + print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") + end +end @testset "test 3-D cube with 1D grouping" begin ITensors.set_warn_order(100) reset_timer!(timer) - N = (5, 5, 5) + N = (3, 3, 3) linkdim = 2 maxdim = 5 cutoff = 1e-15 @@ -252,7 +183,7 @@ using ITensorNetworks.ApproximateTNContraction: show(timer) # after warmup, start to benchmark reset_timer!(timer) - N = (5, 5, 5) + N = (3, 3, 3) linkdim = 2 maxdim = 5 cutoff = 1e-15 @@ -267,60 +198,21 @@ using ITensorNetworks.ApproximateTNContraction: show(timer) end -# #TODO -# # @testset "test 3-D cube with DMRG-like algorithm" begin -# # ITensors.set_warn_order(100) -# # do_profile(true) -# # N = (5, 5, 3) # (5, 5, 5) -# # linkdim = 2 -# # maxdim = linkdim^(floor(N[1] * N[2])) -# # cutoff = 1e-15 -# # tn = ising_partition(N, linkdim) -# # function build_tree(i) -# # tree = tn[:, 1, i] -# # for j in 2:N[2] -# # tree = [tree, tn[:, j, i]] -# # end -# # return tree -# # end -# # tn1, _ = approximate_contract( -# # build_tree(1); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# # ) -# # tn2, _ = approximate_contract( -# # build_tree(2); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# # ) -# # tn3, _ = approximate_contract( -# # build_tree(3); cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# # ) -# # tn12, _ = approximate_contract( -# # [tn1..., tn2...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# # ) -# # out, _ = approximate_contract( -# # [tn12..., tn3...]; cutoff=cutoff, maxdim=maxdim, maxsize=1e15, algorithm="mps" -# # ) -# # profile_exit() -# # end - -# @testset "benchmark PEPS" begin -# N = (8, 8) #(12, 12) -# linkdim = 10 -# cutoff = 1e-15 -# tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) +@testset "benchmark PEPS" begin + N = (8, 8) + linkdim = 10 + cutoff = 1e-15 + tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) -# dim = 20 -# size = dim * dim * linkdim -# # warmup -# for i in 1:2 -# tn = map(inds -> randomITensor(inds...), tn_inds) -# ITensors.set_warn_order(100) -# benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) -# end + dim = 20 + # warmup + tn = map(inds -> randomITensor(inds...), tn_inds) + ITensors.set_warn_order(100) + benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim) -# do_profile(true) -# for i in 1:3 -# tn = map(inds -> randomITensor(inds...), tn_inds) -# ITensors.set_warn_order(100) -# benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim, maxsize=size) -# end -# profile_exit() -# end + reset_timer!(timer) + tn = map(inds -> randomITensor(inds...), tn_inds) + ITensors.set_warn_order(100) + benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim) + show(timer) +end diff --git a/test/approximate_tn_contraction/utils.jl b/test/approximate_tn_contraction/utils.jl index b3ea8793..3e0123ab 100644 --- a/test/approximate_tn_contraction/utils.jl +++ b/test/approximate_tn_contraction/utils.jl @@ -1,34 +1,39 @@ -using ITensorNetworkAD.ITensorNetworks: - ITensor_networks, line_network, TreeTensor, approximate_contract +using TimerOutputs +using ITensorNetworks.ApproximateTNContraction: timer, line_network, approximate_contract -@profile function peps_contraction_mpomps(tn; cutoff=1e-15, maxdim=1000, snake=false) - N = size(tn) - x = tn[:, 1] - for i in 2:(N[2] - 1) - A = (iseven(i) && snake) ? reverse(tn[:, i]) : tn[:, i] - x = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=maxdim)[:] +function peps_contraction_mpomps(tn; cutoff=1e-15, maxdim=1000, snake=false) + @timeit timer "peps_contraction_mpomps" begin + N = size(tn) + x = tn[:, 1] + for i in 2:(N[2] - 1) + A = (iseven(i) && snake) ? reverse(tn[:, i]) : tn[:, i] + x = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=maxdim)[:] + end + return contract(x..., tn[:, N[2]]...) end - return contract(x..., tn[:, N[2]]...) end -@profile function contract_w_sweep(tn, rank) - sweep = sweep_contract(tn, rank, rank) - return ldexp(sweep...) +function contract_w_sweep(tn, rank) + @timeit timer "contract_w_sweep" begin + sweep = sweep_contract(tn, rank, rank) + return ldexp(sweep...) + end end -@profile function contract_element_group(tnet, rank) - element_grouping = line_network(tnet) - return approximate_contract( - element_grouping; cutoff=1e-15, maxdim=rank, maxsize=1e15, algorithm="mincut" - ) +function contract_element_group(tnet; cutoff, maxdim) + @timeit timer "contract_element_group" begin + element_grouping = line_network(tnet) + return approximate_contract(element_grouping; cutoff=cutoff, maxdim=maxdim) + end end -@profile function contract_line_group(tnet, rank, N) - line_grouping = SubNetwork(tnet[:, 1]) - for i in 2:N[2] - line_grouping = SubNetwork(line_grouping, tnet[:, i]...) +function contract_line_group(tnet; cutoff, maxdim) + N = size(tnet) + @timeit timer "contract_line_group" begin + tntree = tnet[:, 1] + for i in 2:N[2] + tntree = [tntree, tnet[:, i]] + end + return approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) end - return batch_tensor_contraction( - TreeTensor, [line_grouping]; cutoff=1e-15, maxdim=rank, optimize=false - ) end diff --git a/test/runtests.jl b/test/runtests.jl index 67d09ce7..68417396 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,10 +7,10 @@ test_files = filter( ) @testset "ITensorNetworks.jl" begin - @testset "$filename" for filename in test_files - println("Running $filename") - include(joinpath(test_path, filename)) - end + # @testset "$filename" for filename in test_files + # println("Running $filename") + # include(joinpath(test_path, filename)) + # end for filename in ["approximate_tn_contraction/runtests.jl"] println("Running $filename") From 8088e981709e0af609a87f34ec184be7d4c11538 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Sat, 26 Nov 2022 22:23:14 -0600 Subject: [PATCH 07/24] [ApproxContraction] correct all test functions --- .../contract/contract.jl | 1 + src/requires/omeinsumcontractionorders.jl | 4 +- test/approximate_tn_contraction/contract.jl | 3 +- test/approximate_tn_contraction/interface.jl | 143 +++++++++--------- test/approximate_tn_contraction/runtests.jl | 12 +- test/runtests.jl | 8 +- test/test_contraction_sequence.jl | 6 +- 7 files changed, 82 insertions(+), 95 deletions(-) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 0e067eb9..3d53d5cc 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -318,6 +318,7 @@ function update_igs_to_adjacency_tree!( for r in root_igs delete!(igs_to_adjacency_tree, r) end + return nothing end # if 2: assign adjacent_igs to boundary of root_igs (if possible), then concatenate igs1, igs2 = collect(keys(root_igs_to_adjacent_igs)) diff --git a/src/requires/omeinsumcontractionorders.jl b/src/requires/omeinsumcontractionorders.jl index d1452cf5..f9a96af1 100644 --- a/src/requires/omeinsumcontractionorders.jl +++ b/src/requires/omeinsumcontractionorders.jl @@ -70,9 +70,7 @@ end """ Convert NestedEinsum to contraction sequence, such as `[[1, 2], [3, 4]]`. """ -function convert_to_contraction_sequence( - net::OMEinsumContractionOrders.NestedEinsum -) +function convert_to_contraction_sequence(net::OMEinsumContractionOrders.NestedEinsum) if OMEinsumContractionOrders.isleaf(net) return net.tensorindex else diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 06ce20ef..94f57f75 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -6,8 +6,7 @@ using ITensorNetworks.ApproximateTNContraction: inds_binary_tree, tree_embedding, approximate_contract -using ITensorNetworks.ApproximateTNContraction: - timer, inds_network, Models, ising_partition +using ITensorNetworks.ApproximateTNContraction: timer, inds_network, Models, ising_partition include("utils.jl") diff --git a/test/approximate_tn_contraction/interface.jl b/test/approximate_tn_contraction/interface.jl index 427a0392..dfda6b6e 100644 --- a/test/approximate_tn_contraction/interface.jl +++ b/test/approximate_tn_contraction/interface.jl @@ -1,8 +1,6 @@ -using ITensors, Random, SweepContractor, ITensorNetworkAD -using ITensorNetworkAD.Profiler -using ITensorNetworkAD.ITensorNetworks: - timer, ITensor_networks, line_network, TreeTensor, approximate_contract -using ITensorNetworkAD.ITensorAutoHOOT: SubNetwork, batch_tensor_contraction +using ITensors, Random, SweepContractor +using ITensorNetworks.ApproximateTNContraction: + timer, ITensor_networks, line_network, approximate_contract include("utils.jl") @@ -51,62 +49,60 @@ end function get_contracted_peps(LTN, rank, N) tnet = ITensor_networks(LTN) tnet_mat = reshape(tnet, N...) - # out_mps = peps_contraction_mpomps(tnet_mat; cutoff=1e-15, maxdim=rank, snake=true) + out_mps = peps_contraction_mpomps(tnet_mat; cutoff=1e-15, maxdim=rank, snake=true) out = contract_w_sweep(LTN, rank) - return out2 = contract_element_group(tnet, rank) - # out3 = contract_line_group(tnet_mat, rank, N) - # return out, out2[1][], ITensor(out3[1])[], out_mps[] + out2 = contract_element_group(tnet; cutoff=1e-15, maxdim=rank) + out3 = contract_line_group(tnet_mat; cutoff=1e-15, maxdim=rank) + return out, out2[1][1], out3[1][1], out_mps[] end -# @testset "test on 2D grid" begin -# Random.seed!(1234) -# ITensors.set_warn_order(100) -# row, column, d = 8, 8, 2 -# LTN = lattice(row, column, d) +@testset "test on 2D grid" begin + Random.seed!(1234) + ITensors.set_warn_order(100) + row, column, d = 8, 8, 2 + LTN = lattice(row, column, d) -# out_true, out_element, out_line, out_mps = get_contracted_peps( -# LTN, d^(Int(row / 2)), [row, column] -# ) -# @test abs((out_true - out_element) / out_true) < 1e-3 -# @test abs((out_true - out_line) / out_true) < 1e-3 -# @test abs((out_true - out_mps) / out_true) < 1e-3 -# for rank in [2, 3, 4, 6, 8, 10, 12, 14, 15, 16] -# out, out_element, out_line, out_mps = get_contracted_peps(LTN, rank, [row, column]) -# error_sweepcontractor = abs((out - out_true) / out_true) -# error_element = abs((out_element - out_true) / out_true) -# error_line = abs((out_line - out_true) / out_true) -# error_mps = abs((out_mps - out_true) / out_true) -# print( -# "maxdim, ", -# rank, -# ", error_sweepcontractor, ", -# error_sweepcontractor, -# ", error_element, ", -# error_element, -# ", error_line, ", -# error_line, -# ", error_mps, ", -# error_mps, -# "\n", -# ) -# end -# end + out_true, out_element, out_line, out_mps = get_contracted_peps( + LTN, d^(Int(row / 2)), [row, column] + ) + @test abs((out_true - out_element) / out_true) < 1e-3 + @test abs((out_true - out_line) / out_true) < 1e-3 + @test abs((out_true - out_mps) / out_true) < 1e-3 + for rank in [2, 3, 4, 6, 8, 10, 12, 14, 15, 16] + out, out_element, out_line, out_mps = get_contracted_peps(LTN, rank, [row, column]) + error_sweepcontractor = abs((out - out_true) / out_true) + error_element = abs((out_element - out_true) / out_true) + error_line = abs((out_line - out_true) / out_true) + error_mps = abs((out_mps - out_true) / out_true) + print( + "maxdim, ", + rank, + ", error_sweepcontractor, ", + error_sweepcontractor, + ", error_element, ", + error_element, + ", error_line, ", + error_line, + ", error_mps, ", + error_mps, + "\n", + ) + end +end -# @testset "benchmark on 2D grid" begin -# Random.seed!(1234) -# ITensors.set_warn_order(100) -# row, column, d, rank = 15, 15, 10, 10 -# LTN = lattice(row, column, d) -# # warm-up -# get_contracted_peps(LTN, rank, [row, column]) -# @info "start benchmark on 2D grid" -# do_profile(true) -# for _ in 1:2 -# LTN = lattice(row, column, d) -# get_contracted_peps(LTN, rank, [row, column]) -# end -# profile_exit() -# end +@testset "benchmark on 2D grid" begin + Random.seed!(1234) + ITensors.set_warn_order(100) + row, column, d, rank = 10, 10, 2, 10 + LTN = lattice(row, column, d) + # warm-up + get_contracted_peps(LTN, rank, [row, column]) + @info "start benchmark on 2D grid" + reset_timer!(timer) + LTN = lattice(row, column, d) + get_contracted_peps(LTN, rank, [row, column]) + show(timer) +end function cube_3d(L=3, d=2) function build_adj(i, j, k) @@ -148,23 +144,20 @@ function cube_3d(L=3, d=2) return TN end -# TODO -# @testset "test on 3D cube" begin -# Random.seed!(1234) -# ITensors.set_warn_order(100) -# L, d = 3, 2 -# rank = 16 -# TN = cube_3d(L, d) -# out = contract_w_sweep(TN, rank) -# tnet = ITensor_networks(TN) -# out2 = contract_element_group(tnet, rank) +@testset "test on 3D cube with element grouping" begin + Random.seed!(1234) + ITensors.set_warn_order(100) + L, d = 3, 2 + rank = 16 + TN = cube_3d(L, d) + out = contract_w_sweep(TN, rank) + tnet = ITensor_networks(TN) + out2 = contract_element_group(tnet; cutoff=1e-15, maxdim=rank) -# do_profile(true) -# for _ in 1:3 -# TN = cube_3d(L, d) -# out = contract_w_sweep(TN, rank) -# tnet = ITensor_networks(TN) -# out2 = contract_element_group(tnet, rank) -# end -# profile_exit() -# end + reset_timer!(timer) + TN = cube_3d(L, d) + out = contract_w_sweep(TN, rank) + tnet = ITensor_networks(TN) + out2 = contract_element_group(tnet; cutoff=1e-15, maxdim=rank) + show(timer) +end diff --git a/test/approximate_tn_contraction/runtests.jl b/test/approximate_tn_contraction/runtests.jl index 9283b6fb..4a782168 100644 --- a/test/approximate_tn_contraction/runtests.jl +++ b/test/approximate_tn_contraction/runtests.jl @@ -3,13 +3,13 @@ using Test @testset "ApproximateTNContraction.jl" begin for filename in [ - # "lattice.jl", - # "models.jl", - # "tree.jl", - # "indexgroup.jl", - # "cache.jl", + "lattice.jl", + "models.jl", + "tree.jl", + "indexgroup.jl", + "cache.jl", "contract.jl", - # "interface.jl", + "interface.jl", ] println("Running $filename in ApproximateTNContraction.jl") include(filename) diff --git a/test/runtests.jl b/test/runtests.jl index 68417396..67d09ce7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,10 +7,10 @@ test_files = filter( ) @testset "ITensorNetworks.jl" begin - # @testset "$filename" for filename in test_files - # println("Running $filename") - # include(joinpath(test_path, filename)) - # end + @testset "$filename" for filename in test_files + println("Running $filename") + include(joinpath(test_path, filename)) + end for filename in ["approximate_tn_contraction/runtests.jl"] println("Running $filename") diff --git a/test/test_contraction_sequence.jl b/test/test_contraction_sequence.jl index b817e415..a585cfa1 100644 --- a/test/test_contraction_sequence.jl +++ b/test/test_contraction_sequence.jl @@ -25,18 +25,14 @@ ITensors.disable_warn_order() res_sa_bipartite = contract(tn; sequence=seq_sa_bipartite)[] @test res_optimal ≈ res_greedy ≈ res_tree_sa ≈ res_sa_bipartite - if !Sys.iswindows() # KaHyPar doesn't work on Windows # https://github.com/kahypar/KaHyPar.jl/issues/9 using Pkg Pkg.add("KaHyPar") using KaHyPar - seq_kahypar_bipartite = contraction_sequence( - tn; alg="kahypar_bipartite", sc_target=200 - ) + seq_kahypar_bipartite = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=200) res_kahypar_bipartite = contract(tn; sequence=seq_kahypar_bipartite)[] @test res_optimal ≈ res_kahypar_bipartite end end - From fcf427949fe914f8e1119cc0e16ce4b6c65deeee Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 6 Dec 2022 12:28:39 -0600 Subject: [PATCH 08/24] [Approximate contract] split norm out in the output of approximate_contract, remove project_boundary --- examples/approximate_contract/3dcube.jl | 50 ++++++++++++ .../contract/contract.jl | 39 +++++++--- .../networks/3d_classical_ising.jl | 25 +++--- .../networks/itensor_network.jl | 77 ------------------- test/Project.toml | 1 + test/approximate_tn_contraction/contract.jl | 48 ++---------- test/approximate_tn_contraction/interface.jl | 2 +- test/approximate_tn_contraction/utils.jl | 6 +- 8 files changed, 99 insertions(+), 149 deletions(-) create mode 100644 examples/approximate_contract/3dcube.jl diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl new file mode 100644 index 00000000..8f3b5e1e --- /dev/null +++ b/examples/approximate_contract/3dcube.jl @@ -0,0 +1,50 @@ +using ITensors, TimerOutputs +using ITensorNetworks.ApproximateTNContraction: approximate_contract +using ITensorNetworks.ApproximateTNContraction: timer, inds_network, ising_partition + +function bench_3d_cube(N) + ITensors.set_warn_order(100) + reset_timer!(timer) + linkdim = 2 + maxdim = 1000000 + cutoff = 1e-8 + tn = ising_partition(N, linkdim) + # tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) + # tn = map(inds -> randomITensor(inds...), tn_inds) + # tntree = nothing + # for k in 1:N[3] + # rangej = iseven(k) ? reverse(1:N[2]) : 1:N[2] + # for j in rangej + # @info j, k + # if tntree == nothing + # tntree = tn[:, j, k] + # else + # tntree = [tntree, tn[:, j, k]] + # end + # end + # end + tn = reshape(tn, (N[1], N[2] * N[3])) + tntree = tn[:, 1] + for i in 2:(N[2] * N[3]) + tntree = [tntree, tn[:, i]] + end + out, log_acc_norm = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + @info "out is", log(out[1][1]) + log_acc_norm + show(timer) + # after warmup, start to benchmark + reset_timer!(timer) + linkdim = 2 + maxdim = 1000000 + cutoff = 1e-8 + tn = ising_partition(N, linkdim) + tn = reshape(tn, (N[1], N[2] * N[3])) + tntree = tn[:, 1] + for i in 2:(N[2] * N[3]) + tntree = [tntree, tn[:, i]] + end + out, log_acc_norm = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + @info "out is", log(out[1][1]) + log_acc_norm + return show(timer) +end + +bench_3d_cube((4, 4, 4)) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 3d53d5cc..e0696ada 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -34,18 +34,20 @@ function optcontract(t_list::Vector) end end -approximate_contract(tn::ITensor, inds_groups; kwargs...) = [tn] +approximate_contract(tn::ITensor, inds_groups; kwargs...) = [tn], 0.0 -approximate_contract(tn::OrthogonalITensor, inds_groups; kwargs...) = [tn] +approximate_contract(tn::OrthogonalITensor, inds_groups; kwargs...) = [tn], 0.0 function approximate_contract(tn::Vector{ITensor}, inds_btree=nothing; kwargs...) - out = approximate_contract(orthogonal_tensors(tn), inds_btree; kwargs...) - return get_tensors(out) + out, log_norm = approximate_contract(orthogonal_tensors(tn), inds_btree; kwargs...) + return get_tensors(out), log_norm end function approximate_contract(tn::Vector{OrthogonalITensor}, inds_btree=nothing; kwargs...) - ctree_to_tensor = approximate_contract_ctree_to_tensor(tn, inds_btree; kwargs...) - return Vector{OrthogonalITensor}(vcat(collect(values(ctree_to_tensor))...)) + ctree_to_tensor, log_root_norm = approximate_contract_ctree_to_tensor( + tn, inds_btree; kwargs... + ) + return Vector{OrthogonalITensor}(vcat(collect(values(ctree_to_tensor))...)), log_root_norm end function approximate_contract_ctree_to_tensor( @@ -63,7 +65,7 @@ function approximate_contract_ctree_to_tensor( if inds_btree == nothing inds_btree = [[i] for i in uncontract_inds] end - return Dict{Vector,OrthogonalITensor}(inds_btree => optcontract(tn)) + return Dict{Vector,OrthogonalITensor}(inds_btree => optcontract(tn)), 0.0 end # # cases where tn is a tree, or contains 2 disconnected trees # if length(innerinds) <= length(tn) - 1 @@ -782,6 +784,8 @@ function approximate_contract(ctree::Vector; kwargs...) ctree_to_contract_igs[ctrees[end]] = ctree_to_igs[ctrees[end]] # mapping each contraction tree to a tensor network ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() + # accumulate norm + log_accumulated_norm = 0.0 for (ii, c) in enumerate(ctrees) @info ii, "th tree approximation" if ctree_to_igs[c] == [] @@ -789,7 +793,7 @@ function approximate_contract(ctree::Vector; kwargs...) tn1 = get_child_tn(ctree_to_tn_tree, c[1]) tn2 = get_child_tn(ctree_to_tn_tree, c[2]) tn = vcat(tn1, tn2) - return get_tensors([optcontract(tn)]) + return get_tensors([optcontract(tn)]), log_accumulated_norm end # caching is not needed here if !haskey(ctree_to_tn_tree, c[1]) || !haskey(ctree_to_tn_tree, c[2]) @@ -798,9 +802,10 @@ function approximate_contract(ctree::Vector; kwargs...) inds_btree = ordered_igs_to_binary_tree( ctree_to_adj_tree[c].children, ctree_to_contract_igs[c], ig_to_ig_tree ) - ctree_to_tn_tree[c] = approximate_contract_ctree_to_tensor( + ctree_to_tn_tree[c], log_root_norm = approximate_contract_ctree_to_tensor( [tn1..., tn2...], inds_btree; kwargs... ) + log_accumulated_norm += log_root_norm continue end # caching @@ -833,18 +838,22 @@ function approximate_contract(ctree::Vector; kwargs...) inds_btree = ordered_igs_to_binary_tree( new_igs, ctree_to_contract_igs[c], new_ig_to_ig_tree ) - new_tn_tree = approximate_contract_ctree_to_tensor(uncached_tn, inds_btree; kwargs...) + new_tn_tree, log_root_norm = approximate_contract_ctree_to_tensor( + uncached_tn, inds_btree; kwargs... + ) + log_accumulated_norm += log_root_norm update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) end tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) - return get_tensors(tn) + return get_tensors(tn), log_accumulated_norm end # interlaced HOSVD using caching function tree_approximation_cache( embedding::Dict, inds_btree::Vector; cutoff=1e-15, maxdim=10000, maxsize=10000 ) + @info "start tree_approximation_cache", inds_btree ctree_to_tensor = Dict{Vector,OrthogonalITensor}() # initialize sim_dict network = vcat(collect(values(embedding))...) @@ -890,11 +899,15 @@ function tree_approximation_cache( tnormal = optcontract(net) dim2 = floor(maxsize / (space(ind1_pair[1]) * space(ind2_pair[1]))) dim = min(maxdim, dim2) + t00 = time() + @info "eigen input size", size(tnormal.tensor) @timeit timer "eigen" begin diag, U = eigen( tnormal.tensor, linds, rinds; cutoff=cutoff, maxdim=dim, ishermitian=true ) end + t11 = time() - t00 + @info "size of U", size(U), "size of diag", size(diag), "costs", t11 dr = commonind(diag, U) Usim = replaceinds(U, rinds => linds) ortho_U = OrthogonalITensor(U) @@ -922,6 +935,8 @@ function tree_approximation_cache( # last tensor envnet = [n1[1], n2[1], bra...] last_tensor = optcontract(envnet) + root_norm = norm(last_tensor.tensor) + last_tensor.tensor /= root_norm ctree_to_tensor[inds_btree] = last_tensor - return ctree_to_tensor + return ctree_to_tensor, log(root_norm) end diff --git a/src/ApproximateTNContraction/networks/3d_classical_ising.jl b/src/ApproximateTNContraction/networks/3d_classical_ising.jl index bb1dd2ba..30df3767 100644 --- a/src/ApproximateTNContraction/networks/3d_classical_ising.jl +++ b/src/ApproximateTNContraction/networks/3d_classical_ising.jl @@ -1,13 +1,10 @@ using ITensors -function ising_mpo( - pairs::Vector{<:Pair{<:Index,<:Index}}, β::Real, J::Real=1.0; sz::Bool=false -) - d = dim(pairs[1].first) - for p in pairs - @assert d == dim(p.first) == dim(p.second) +function ising_mpo(indices::Vector, β::Real, J::Real=1.0; sz::Bool=false) + d = dim(indices[1]) + for ind in indices + @assert d == dim(ind) end - indices = mapreduce(p -> [p.first, p.second], vcat, pairs) order = length(indices) T = ITensor(indices...) for i in 1:d @@ -39,16 +36,12 @@ function ising_mpo( end function isingTensor(inds::Vector) - npairs = floor(Int, length(inds) / 2) - @assert length(inds) == 2 * npairs - βc = 0.5 * log(√2 + 1) - β = 1.0 * βc - pairs = [Pair(inds[2 * i - 1], inds[2 * i]) for i in 1:npairs] - return ising_mpo(pairs, β) + # βc = 0.5 * log(√2 + 1) + β = 0.3 # 1.0 * βc + return ising_mpo(inds, β) end function ising_partition(N, d=2) - tn_inds = inds_network(N...; linkdims=d, periodic=true) - tn = map(inds -> isingTensor(inds), tn_inds) - return project_boundary(tn) + tn_inds = inds_network(N...; linkdims=d, periodic=false) + return map(inds -> isingTensor(inds), tn_inds) end diff --git a/src/ApproximateTNContraction/networks/itensor_network.jl b/src/ApproximateTNContraction/networks/itensor_network.jl index afd422ed..152bc7fa 100644 --- a/src/ApproximateTNContraction/networks/itensor_network.jl +++ b/src/ApproximateTNContraction/networks/itensor_network.jl @@ -1,80 +1,3 @@ -""" - boundary_projectors(tn::Matrix{ITensor}, state=1) -For a 2D tensor network, return the right and bottom boundary projectors onto -the local state `state`. -""" -function boundary_projectors(tn::Matrix{ITensor}, state=1) - top_row = tn[1, :] - bottom_row = tn[end, :] - left_column = tn[:, 1] - right_column = tn[:, end] - bottom_boundary_inds = commonind.(bottom_row, top_row) - right_boundary_inds = commonind.(right_column, left_column) - ψr = ITensors.state.(right_boundary_inds, state) - ψb = ITensors.state.(bottom_boundary_inds, state) - return ψr, ψb -end - -function boundary_projectors(tn::Array{ITensor,3}, state=1) - top = tn[1, :, :] - bottom = tn[end, :, :] - left = tn[:, 1, :] - right = tn[:, end, :] - front = tn[:, :, 1] - back = tn[:, :, end] - top_inds = commonind.(top, bottom) - left_inds = commonind.(left, right) - front_inds = commonind.(front, back) - - psi_top = ITensors.state.(top_inds, state) - psi_left = ITensors.state.(left_inds, state) - psi_front = ITensors.state.(front_inds, state) - return psi_top, psi_left, psi_front -end - -""" - project_boundary(tn::Matrix{ITensor}, state=1) -Project the boundary of a periodic 2D tensor network onto -the specified state. -""" -function project_boundary(tn::Matrix{ITensor}, state=1) - Nx, Ny = size(tn) - ψr, ψb = boundary_projectors(tn, state) - for n in 1:Nx - tn[n, 1] = tn[n, 1] * ψr[n] - tn[n, end] = tn[n, end] * dag(ψr[n]) - end - for n in 1:Ny - tn[1, n] = tn[1, n] * ψb[n] - tn[end, n] = tn[end, n] * dag(ψb[n]) - end - return tn -end - -function project_boundary(tn::Array{ITensor,3}, state=1) - Nx, Ny, Nz = size(tn) - psi_top, psi_left, psi_front = boundary_projectors(tn, state) - for j in 1:Ny - for k in 1:Nz - tn[1, j, k] = tn[1, j, k] * psi_top[j, k] - tn[end, j, k] = tn[end, j, k] * psi_top[j, k] - end - end - for i in 1:Nx - for k in 1:Nz - tn[i, 1, k] = tn[i, 1, k] * psi_left[i, k] - tn[i, end, k] = tn[i, end, k] * psi_left[i, k] - end - end - for i in 1:Nx - for j in 1:Ny - tn[i, j, 1] = tn[i, j, 1] * psi_front[i, j] - tn[i, j, end] = tn[i, j, end] * psi_front[i, j] - end - end - return tn -end - function ITensors.prime(indices::Array{<:Index,1}, network::Array{ITensor}, n::Integer=1) function primeinds(tensor) prime_inds = [ind for ind in inds(tensor) if ind in indices] diff --git a/test/Project.toml b/test/Project.toml index 38cb8dbd..20dc46d8 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,5 +5,6 @@ ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" OMEinsumContractionOrders = "6f22d1fd-8eed-4bb7-9776-e7d684900715" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SweepContractor = "75a5deae-e917-4509-af32-a989148c8d5f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 94f57f75..cba6aacc 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -46,7 +46,8 @@ include("utils.jl") [[[i], [j]], [[k], [l]]] => [ABCD], [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], ]) - out = tree_approximation_cache(embedding, btree) + out, log_norm = tree_approximation_cache(embedding, btree) + out[btree].tensor *= exp(log_norm) out = get_tensors(collect(values(out))) @test isapprox(contract(out...), contract(get_tensors(tensors)...)) end @@ -59,17 +60,17 @@ end tn = map(inds -> randomITensor(inds...), tn_inds) x, A = tn[:, 1], tn[:, 2] out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) - out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) + out2, log_norm = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) tsr_true = contract(out_true...) tsr_nrmsquare = (tsr_true * tsr_true)[1] - @test isapprox(tsr_true, contract(out2...)) + @test isapprox(tsr_true, contract(out2...) * exp(log_norm)) maxdims = [2, 4, 6, 8] for dim in maxdims out = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=dim) - out2 = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) + out2, log_norm = approximate_contract([A, x]; cutoff=cutoff, maxdim=dim) residual1 = tsr_true - contract(out...) - residual2 = tsr_true - contract(out2...) + residual2 = tsr_true - contract(out2...) * exp(log_norm) error1 = sqrt((residual1 * residual1)[1] / tsr_nrmsquare) error2 = sqrt((residual2 * residual2)[1] / tsr_nrmsquare) print("maxdim, ", dim, ", error1, ", error1, ", error2, ", error2, "\n") @@ -135,7 +136,7 @@ end function benchmark_peps_contraction(tn; cutoff=1e-15, maxdim=1000) out = peps_contraction_mpomps(tn; cutoff=cutoff, maxdim=maxdim, snake=false) out2 = contract_line_group(tn; cutoff=cutoff, maxdim=maxdim) - return out[], out2[1][1] + return out[], out2 end @testset "test PEPS" begin @@ -162,41 +163,6 @@ end end end -@testset "test 3-D cube with 1D grouping" begin - ITensors.set_warn_order(100) - reset_timer!(timer) - N = (3, 3, 3) - linkdim = 2 - maxdim = 5 - cutoff = 1e-15 - # tn = ising_partition(N, linkdim) - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) - tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = tn[:, 1] - for i in 2:(N[2] * N[3]) - tntree = [tntree, tn[:, i]] - end - out = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) - @info "out is", out[1][1] - show(timer) - # after warmup, start to benchmark - reset_timer!(timer) - N = (3, 3, 3) - linkdim = 2 - maxdim = 5 - cutoff = 1e-15 - tn = ising_partition(N, linkdim) - tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = tn[:, 1] - for i in 2:(N[2] * N[3]) - tntree = [tntree, tn[:, i]] - end - out = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) - @info "out is", out[1][1] - show(timer) -end - @testset "benchmark PEPS" begin N = (8, 8) linkdim = 10 diff --git a/test/approximate_tn_contraction/interface.jl b/test/approximate_tn_contraction/interface.jl index dfda6b6e..db5ed69d 100644 --- a/test/approximate_tn_contraction/interface.jl +++ b/test/approximate_tn_contraction/interface.jl @@ -53,7 +53,7 @@ function get_contracted_peps(LTN, rank, N) out = contract_w_sweep(LTN, rank) out2 = contract_element_group(tnet; cutoff=1e-15, maxdim=rank) out3 = contract_line_group(tnet_mat; cutoff=1e-15, maxdim=rank) - return out, out2[1][1], out3[1][1], out_mps[] + return out, out2, out3, out_mps[] end @testset "test on 2D grid" begin diff --git a/test/approximate_tn_contraction/utils.jl b/test/approximate_tn_contraction/utils.jl index 3e0123ab..0df755fc 100644 --- a/test/approximate_tn_contraction/utils.jl +++ b/test/approximate_tn_contraction/utils.jl @@ -23,7 +23,8 @@ end function contract_element_group(tnet; cutoff, maxdim) @timeit timer "contract_element_group" begin element_grouping = line_network(tnet) - return approximate_contract(element_grouping; cutoff=cutoff, maxdim=maxdim) + out, log_norm = approximate_contract(element_grouping; cutoff=cutoff, maxdim=maxdim) + return out[1][1] * exp(log_norm) end end @@ -34,6 +35,7 @@ function contract_line_group(tnet; cutoff, maxdim) for i in 2:N[2] tntree = [tntree, tnet[:, i]] end - return approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + out, log_norm = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + return out[1][1] * exp(log_norm) end end From 1a1fc1f500f433c72d89d4172a2aa7637bbeca8a Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Wed, 7 Dec 2022 18:07:35 -0600 Subject: [PATCH 09/24] [Approximate contraction] Add MPS ansatz, fix multiple caching bugs --- examples/approximate_contract/3dcube.jl | 10 +- .../contract/contract.jl | 193 +++++++++++++----- .../contract/mincut_tree.jl | 70 ++++--- .../contract/tree_utils.jl | 22 +- 4 files changed, 195 insertions(+), 100 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 8f3b5e1e..23bef1ee 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -28,7 +28,9 @@ function bench_3d_cube(N) for i in 2:(N[2] * N[3]) tntree = [tntree, tn[:, i]] end - out, log_acc_norm = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + out, log_acc_norm = approximate_contract( + tntree; cutoff=cutoff, maxdim=maxdim, ansatz="mps", use_cache=true + ) @info "out is", log(out[1][1]) + log_acc_norm show(timer) # after warmup, start to benchmark @@ -42,9 +44,11 @@ function bench_3d_cube(N) for i in 2:(N[2] * N[3]) tntree = [tntree, tn[:, i]] end - out, log_acc_norm = approximate_contract(tntree; cutoff=cutoff, maxdim=maxdim) + out, log_acc_norm = approximate_contract( + tntree; cutoff=cutoff, maxdim=maxdim, ansatz="mps", use_cache=true + ) @info "out is", log(out[1][1]) + log_acc_norm return show(timer) end -bench_3d_cube((4, 4, 4)) +bench_3d_cube((5, 5, 5)) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index e0696ada..87885e37 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -162,13 +162,11 @@ function IndexAdjacencyTree(index_group::IndexGroup) end function get_adj_tree_leaves(tree::IndexAdjacencyTree) - @timeit timer "get_adj_tree_leaves" begin - if tree.children isa Vector{IndexGroup} - return tree.children - end - leaves = [get_adj_tree_leaves(c) for c in tree.children] - return vcat(leaves...) + if tree.children isa Vector{IndexGroup} + return tree.children end + leaves = [get_adj_tree_leaves(c) for c in tree.children] + return vcat(leaves...) end function Base.contains(adj_tree::IndexAdjacencyTree, adj_igs::Set{IndexGroup}) @@ -598,29 +596,79 @@ function _approximate_contract_pre_process(tn_leaves, ctrees) ) end end - # mapping each index group to the index group tree - ig_to_ig_tree = Dict{IndexGroup,IndexGroup}() + # mapping each contraction tree to its contract igs + ctree_to_contract_igs = Dict{Vector,Vector{IndexGroup}}() + for c in ctrees + contract_igs = intersect(ctree_to_igs[c[1]], ctree_to_igs[c[2]]) + ctree_to_contract_igs[c[1]] = contract_igs + ctree_to_contract_igs[c[2]] = contract_igs + end + # special case when the network contains uncontracted inds + ctree_to_contract_igs[ctrees[end]] = ctree_to_igs[ctrees[end]] + # mapping each index group to a linear ordering + ig_to_linear_order = Dict{IndexGroup,Vector}() for leaf in tn_leaves for ig in ctree_to_igs[leaf] - if !haskey(ig_to_ig_tree, ig) - inds_tree = inds_binary_tree(leaf, ig.data; algorithm="mps") - ig_to_ig_tree[ig] = IndexGroup(inds_tree, true) + if !haskey(ig_to_linear_order, ig) + ig_to_linear_order[ig] = inds_linear_order(leaf, ig.data) end end end - return ctree_to_igs, ctree_to_adj_tree, ig_to_ig_tree + return ctree_to_igs, ctree_to_adj_tree, ctree_to_contract_igs, ig_to_linear_order end end -function ordered_igs_to_binary_tree(ordered_igs, contract_igs, ig_to_ig_tree) +function ordered_igs_to_binary_tree(ordered_igs, contract_igs, ig_to_linear_order; ansatz) + @assert ansatz in ["comb", "mps"] @timeit timer "ordered_igs_to_binary_tree" begin @assert contract_igs != [] left_igs, right_igs = split_igs(ordered_igs, contract_igs) - tree_1 = line_to_tree([ig_to_ig_tree[ig].data for ig in left_igs]) - tree_contract = line_to_tree([ig_to_ig_tree[ig].data for ig in contract_igs]) - tree_2 = line_to_tree([ig_to_ig_tree[ig].data for ig in reverse(right_igs)]) + if ansatz == "comb" + return ordered_igs_to_binary_tree_comb( + left_igs, right_igs, contract_igs, ig_to_linear_order + ) + elseif ansatz == "mps" + return ordered_igs_to_binary_tree_mps( + left_igs, right_igs, contract_igs, ig_to_linear_order + ) + end + end +end + +function ordered_igs_to_binary_tree_mps( + left_igs, right_igs, contract_igs, ig_to_linear_order +) + left_order = vcat([ig_to_linear_order[ig] for ig in left_igs]...) + right_order = vcat([ig_to_linear_order[ig] for ig in right_igs]...) + contract_order = vcat([ig_to_linear_order[ig] for ig in contract_igs]...) + if length(left_order) <= length(right_order) + left_order = [left_order..., contract_order...] + else + right_order = [contract_order..., right_order...] + end + return merge_tree(line_to_tree(left_order), line_to_tree(reverse(right_order))) +end + +function ordered_igs_to_binary_tree_comb( + left_igs, right_igs, contract_igs, ig_to_linear_order +) + tree_1 = line_to_tree([line_to_tree(ig_to_linear_order[ig]) for ig in left_igs]) + tree_contract = line_to_tree([ + line_to_tree(ig_to_linear_order[ig]) for ig in contract_igs + ]) + tree_2 = line_to_tree([line_to_tree(ig_to_linear_order[ig]) for ig in reverse(right_igs)]) + # make the binary tree more balanced to save tree approximation cost + if tree_1 == [] return merge_tree(merge_tree(tree_1, tree_contract), tree_2) end + if tree_2 == [] + return merge_tree(tree_1, merge_tree(tree_contract, tree_2)) + end + if length(vectorize(tree_1)) <= length(vectorize(tree_2)) + return merge_tree(merge_tree(tree_1, tree_contract), tree_2) + else + return merge_tree(tree_1, merge_tree(tree_contract, tree_2)) + end end function get_igs_cache_info(igs_list, contract_igs_list) @@ -697,17 +745,32 @@ function get_tn_cache_sub_info( end function get_tn_cache_info( - tn_tree_1::Dict{Vector,OrthogonalITensor}, - tn_tree_2::Dict{Vector,OrthogonalITensor}, + ctree_to_tn_tree::Dict{Vector,Dict{Vector,OrthogonalITensor}}, + ctree_1::Vector, + ctree_2::Vector, cache_binary_trees::Vector, ) @timeit timer "get_tn_cache_info" begin - cached_tn_tree1, uncached_tn1, new_igs_1 = get_tn_cache_sub_info( - tn_tree_1, cache_binary_trees - ) - cached_tn_tree2, uncached_tn2, new_igs_2 = get_tn_cache_sub_info( - tn_tree_2, cache_binary_trees - ) + if haskey(ctree_to_tn_tree, ctree_1) + tn_tree_1 = ctree_to_tn_tree[ctree_1] + cached_tn_tree1, uncached_tn1, new_igs_1 = get_tn_cache_sub_info( + tn_tree_1, cache_binary_trees + ) + else + cached_tn_tree1 = Dict{Vector,OrthogonalITensor}() + uncached_tn1 = get_child_tn(ctree_to_tn_tree, ctree_1) + new_igs_1 = [nothing, nothing] + end + if haskey(ctree_to_tn_tree, ctree_2) + tn_tree_2 = ctree_to_tn_tree[ctree_2] + cached_tn_tree2, uncached_tn2, new_igs_2 = get_tn_cache_sub_info( + tn_tree_2, cache_binary_trees + ) + else + cached_tn_tree2 = Dict{Vector,OrthogonalITensor}() + uncached_tn2 = get_child_tn(ctree_to_tn_tree, ctree_2) + new_igs_2 = [nothing, nothing] + end uncached_tn = [uncached_tn1..., uncached_tn2...] new_igs_left = [i for i in [new_igs_1[1], new_igs_2[1]] if i != nothing] @assert length(new_igs_left) <= 1 @@ -767,21 +830,12 @@ end # ig: index group # contract_ig: the index group to be contracted next # ig_tree: an index group with a tree hierarchy -function approximate_contract(ctree::Vector; kwargs...) +function approximate_contract(ctree::Vector; cutoff, maxdim, ansatz="mps", use_cache=true) tn_leaves = get_leaves(ctree) ctrees = topo_sort(ctree; leaves=tn_leaves) - ctree_to_igs, ctree_to_adj_tree, ig_to_ig_tree = _approximate_contract_pre_process( + ctree_to_igs, ctree_to_adj_tree, ctree_to_contract_igs, ig_to_linear_order = _approximate_contract_pre_process( tn_leaves, ctrees ) - # TODO: move these to preprocess - ctree_to_contract_igs = Dict{Vector,Vector{IndexGroup}}() - for c in ctrees - contract_igs = intersect(ctree_to_igs[c[1]], ctree_to_igs[c[2]]) - ctree_to_contract_igs[c[1]] = contract_igs - ctree_to_contract_igs[c[2]] = contract_igs - end - # special case when the network contains uncontracted inds - ctree_to_contract_igs[ctrees[end]] = ctree_to_igs[ctrees[end]] # mapping each contraction tree to a tensor network ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() # accumulate norm @@ -795,54 +849,81 @@ function approximate_contract(ctree::Vector; kwargs...) tn = vcat(tn1, tn2) return get_tensors([optcontract(tn)]), log_accumulated_norm end - # caching is not needed here - if !haskey(ctree_to_tn_tree, c[1]) || !haskey(ctree_to_tn_tree, c[2]) + # caching is not used here + if use_cache == false tn1 = get_child_tn(ctree_to_tn_tree, c[1]) tn2 = get_child_tn(ctree_to_tn_tree, c[2]) inds_btree = ordered_igs_to_binary_tree( - ctree_to_adj_tree[c].children, ctree_to_contract_igs[c], ig_to_ig_tree + ctree_to_adj_tree[c].children, + ctree_to_contract_igs[c], + ig_to_linear_order; + ansatz=ansatz, ) ctree_to_tn_tree[c], log_root_norm = approximate_contract_ctree_to_tensor( - [tn1..., tn2...], inds_btree; kwargs... + [tn1..., tn2...], inds_btree; cutoff=cutoff, maxdim=maxdim ) log_accumulated_norm += log_root_norm continue end # caching - uncache_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( + # Note: cache_igs_right has a reversed ordering + center_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( [ctree_to_adj_tree[i].children for i in [c, c[1], c[2]]], [ctree_to_contract_igs[i] for i in [c, c[1], c[2]]], ) - cache_binary_tree_left = line_to_tree([ig_to_ig_tree[ig].data for ig in cache_igs_left]) - cache_binary_tree_right = line_to_tree([ - ig_to_ig_tree[ig].data for ig in cache_igs_right - ]) + if ansatz == "comb" + cache_binary_tree_left = line_to_tree([ + line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_left + ]) + cache_binary_tree_right = line_to_tree([ + line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_right + ]) + elseif ansatz == "mps" + left_order = vcat([ig_to_linear_order[ig] for ig in cache_igs_left]...) + cache_binary_tree_left = line_to_tree(left_order) + right_order = vcat([ig_to_linear_order[ig] for ig in reverse(cache_igs_right)]...) + cache_binary_tree_right = line_to_tree(reverse(right_order)) + end cached_tn_tree, uncached_tn, new_ig_left, new_ig_right = get_tn_cache_info( - ctree_to_tn_tree[c[1]], - ctree_to_tn_tree[c[2]], - [cache_binary_tree_left, cache_binary_tree_right], + ctree_to_tn_tree, c[1], c[2], [cache_binary_tree_left, cache_binary_tree_right] ) + if new_ig_right == nothing && new_ig_left == nothing + @info "Caching is not used in this approximation" + @assert length(cached_tn_tree) == 0 + else + @info "Caching is used in this approximation", new_ig_left, new_ig_right + end new_ig_to_binary_tree_pairs = Vector{Pair}() - new_igs = uncache_igs - new_ig_to_ig_tree = ig_to_ig_tree - if new_ig_left != nothing - new_ig_to_ig_tree = merge(new_ig_to_ig_tree, Dict(new_ig_left => new_ig_left)) + new_igs = center_igs + new_ig_to_linear_order = ig_to_linear_order + if new_ig_left == nothing + new_igs = [cache_igs_left..., new_igs...] + else + new_ig_to_linear_order = merge( + new_ig_to_linear_order, Dict(new_ig_left => [new_ig_left.data]) + ) new_igs = [new_ig_left, new_igs...] push!(new_ig_to_binary_tree_pairs, new_ig_left.data => cache_binary_tree_left) end - if new_ig_right != nothing - new_ig_to_ig_tree = merge(new_ig_to_ig_tree, Dict(new_ig_right => new_ig_right)) + if new_ig_right == nothing + new_igs = [new_igs..., cache_igs_right...] + else + new_ig_to_linear_order = merge( + new_ig_to_linear_order, Dict(new_ig_right => [new_ig_right.data]) + ) new_igs = [new_igs..., new_ig_right] push!(new_ig_to_binary_tree_pairs, new_ig_right.data => cache_binary_tree_right) end inds_btree = ordered_igs_to_binary_tree( - new_igs, ctree_to_contract_igs[c], new_ig_to_ig_tree + new_igs, ctree_to_contract_igs[c], new_ig_to_linear_order; ansatz=ansatz ) new_tn_tree, log_root_norm = approximate_contract_ctree_to_tensor( - uncached_tn, inds_btree; kwargs... + uncached_tn, inds_btree; cutoff=cutoff, maxdim=maxdim ) log_accumulated_norm += log_root_norm - update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) + if length(new_ig_to_binary_tree_pairs) != 0 + update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) + end ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) end tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) diff --git a/src/ApproximateTNContraction/contract/mincut_tree.jl b/src/ApproximateTNContraction/contract/mincut_tree.jl index 4f196bca..57f31b40 100644 --- a/src/ApproximateTNContraction/contract/mincut_tree.jl +++ b/src/ApproximateTNContraction/contract/mincut_tree.jl @@ -35,10 +35,51 @@ function inds_binary_tree!(tng::TensorNetworkGraph, outinds::Vector; algorithm=" end return out_inds elseif algorithm == "mps" - return mps_inds!(tng, outinds) + return line_to_tree(inds_linear_order!(tng, outinds)) end end +function inds_linear_order( + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}} +) + if outinds == nothing + outinds = noncommoninds(network...) + end + if length(outinds) == 1 + return outinds + end + tng = TensorNetworkGraph(network, outinds) + grouped_uncontracted_inds = [[i] for i in outinds] + return inds_linear_order!(tng, grouped_uncontracted_inds) +end + +function inds_linear_order!(tng::TensorNetworkGraph, outinds::Vector) + @assert length(outinds) >= 1 + # base case here, for the case length(outinds) == 2, we still need to do the update + if length(outinds) == 1 + return outinds[1] + end + if length(outinds) == 2 + return outinds + end + new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 1, 1))) + new_edge = new_edge[1] + # outinds = update!(tng, outinds, new_edge, minval) + # first_ind = new_edge + linear_order = [new_edge] + while length(outinds) > 2 + splitinds = [[new_edge, i] for i in outinds if i != new_edge] + new_edge, minval = new_edge_mincut(tng, splitinds) + outinds = update!(tng, outinds, new_edge, minval) + # first_ind = new_edge + push!(linear_order, new_edge[2]) + end + splitinds = [[new_edge, i] for i in outinds if i != new_edge] + @assert length(splitinds) == 1 + push!(linear_order, splitinds[1][2]) + return linear_order +end + function inds_binary_tree( network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" ) @@ -145,33 +186,6 @@ function mincut_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) end end -function mps_inds!(tng::TensorNetworkGraph, outinds::Vector) - @assert length(outinds) >= 1 - # base case here, for the case length(outinds) == 2, we still need to do the update - if length(outinds) == 1 - return outinds[1] - end - if length(outinds) == 2 - return outinds - end - new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) - outinds = update!(tng, outinds, new_edge, minval) - first_ind = new_edge - while length(outinds) > 2 - splitinds = [[first_ind, i] for i in outinds if i != first_ind] - new_edge, minval = new_edge_mincut(tng, splitinds) - outinds = update!(tng, outinds, new_edge, minval) - first_ind = new_edge - end - return outinds -end - -function mps_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) - tng = copy(tng) - uncontract_inds = copy(uncontract_inds) - return mps_inds!(tng, uncontract_inds) -end - # update the graph function update!(tng::TensorNetworkGraph, uncontract_inds::Vector, new_edge::Vector, minval) add_vertex!(tng.graph) diff --git a/src/ApproximateTNContraction/contract/tree_utils.jl b/src/ApproximateTNContraction/contract/tree_utils.jl index 196a45d7..692186b7 100644 --- a/src/ApproximateTNContraction/contract/tree_utils.jl +++ b/src/ApproximateTNContraction/contract/tree_utils.jl @@ -64,24 +64,20 @@ end # example: [[[1,2], [3,4]], [[5,6], [7,8]]] = [[1,2], [3,4], [5,6], [7,8]] function get_leaves(tree::Vector) - @timeit timer "get_leaves" begin - if !(tree isa Vector{<:Vector}) - return [tree] - end - return mapreduce(get_leaves, vcat, tree) + if !(tree isa Vector{<:Vector}) + return [tree] end + return mapreduce(get_leaves, vcat, tree) end function line_to_tree(line::Vector) - @timeit timer "line_to_tree" begin - if length(line) == 1 && line[1] isa Vector - return line[1] - end - if length(line) <= 2 - return line - end - return [line_to_tree(line[1:(end - 1)]), line[end]] + if length(line) == 1 && line[1] isa Vector + return line[1] + end + if length(line) <= 2 + return line end + return [line_to_tree(line[1:(end - 1)]), line[end]] end function topo_sort(tn; type=Vector, leaves=[]) From dc102ef594abba7c011d700ea9756ec4c7b9b663 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 27 Dec 2022 17:52:30 -0800 Subject: [PATCH 10/24] Update 3d cube benchmark file --- examples/approximate_contract/3dcube.jl | 30 +-- .../contract/contract.jl | 184 +++++++++--------- 2 files changed, 109 insertions(+), 105 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 23bef1ee..8baab479 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -2,12 +2,10 @@ using ITensors, TimerOutputs using ITensorNetworks.ApproximateTNContraction: approximate_contract using ITensorNetworks.ApproximateTNContraction: timer, inds_network, ising_partition -function bench_3d_cube(N) +function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache) ITensors.set_warn_order(100) reset_timer!(timer) linkdim = 2 - maxdim = 1000000 - cutoff = 1e-8 tn = ising_partition(N, linkdim) # tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) # tn = map(inds -> randomITensor(inds...), tn_inds) @@ -28,27 +26,31 @@ function bench_3d_cube(N) for i in 2:(N[2] * N[3]) tntree = [tntree, tn[:, i]] end - out, log_acc_norm = approximate_contract( - tntree; cutoff=cutoff, maxdim=maxdim, ansatz="mps", use_cache=true - ) - @info "out is", log(out[1][1]) + log_acc_norm + for _ in 1:num_iter + out, log_acc_norm = approximate_contract( + tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache + ) + @info "out is", log(out[1][1]) + log_acc_norm + end show(timer) # after warmup, start to benchmark reset_timer!(timer) linkdim = 2 - maxdim = 1000000 - cutoff = 1e-8 tn = ising_partition(N, linkdim) tn = reshape(tn, (N[1], N[2] * N[3])) tntree = tn[:, 1] for i in 2:(N[2] * N[3]) tntree = [tntree, tn[:, i]] end - out, log_acc_norm = approximate_contract( - tntree; cutoff=cutoff, maxdim=maxdim, ansatz="mps", use_cache=true - ) - @info "out is", log(out[1][1]) + log_acc_norm + for _ in 1:num_iter + out, log_acc_norm = approximate_contract( + tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache + ) + @info "out is", log(out[1][1]) + log_acc_norm + end return show(timer) end -bench_3d_cube((5, 5, 5)) +bench_3d_cube( + (4, 6, 6); num_iter=1, cutoff=1e-8, maxdim=1e6, ansatz="comb", use_cache=false +) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 87885e37..c6a9efdc 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -831,103 +831,105 @@ end # contract_ig: the index group to be contracted next # ig_tree: an index group with a tree hierarchy function approximate_contract(ctree::Vector; cutoff, maxdim, ansatz="mps", use_cache=true) - tn_leaves = get_leaves(ctree) - ctrees = topo_sort(ctree; leaves=tn_leaves) - ctree_to_igs, ctree_to_adj_tree, ctree_to_contract_igs, ig_to_linear_order = _approximate_contract_pre_process( - tn_leaves, ctrees - ) - # mapping each contraction tree to a tensor network - ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() - # accumulate norm - log_accumulated_norm = 0.0 - for (ii, c) in enumerate(ctrees) - @info ii, "th tree approximation" - if ctree_to_igs[c] == [] - @assert c == ctrees[end] - tn1 = get_child_tn(ctree_to_tn_tree, c[1]) - tn2 = get_child_tn(ctree_to_tn_tree, c[2]) - tn = vcat(tn1, tn2) - return get_tensors([optcontract(tn)]), log_accumulated_norm - end - # caching is not used here - if use_cache == false - tn1 = get_child_tn(ctree_to_tn_tree, c[1]) - tn2 = get_child_tn(ctree_to_tn_tree, c[2]) - inds_btree = ordered_igs_to_binary_tree( - ctree_to_adj_tree[c].children, - ctree_to_contract_igs[c], - ig_to_linear_order; - ansatz=ansatz, + @timeit timer "approximate_contract" begin + tn_leaves = get_leaves(ctree) + ctrees = topo_sort(ctree; leaves=tn_leaves) + ctree_to_igs, ctree_to_adj_tree, ctree_to_contract_igs, ig_to_linear_order = _approximate_contract_pre_process( + tn_leaves, ctrees + ) + # mapping each contraction tree to a tensor network + ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() + # accumulate norm + log_accumulated_norm = 0.0 + for (ii, c) in enumerate(ctrees) + @info ii, "th tree approximation" + if ctree_to_igs[c] == [] + @assert c == ctrees[end] + tn1 = get_child_tn(ctree_to_tn_tree, c[1]) + tn2 = get_child_tn(ctree_to_tn_tree, c[2]) + tn = vcat(tn1, tn2) + return get_tensors([optcontract(tn)]), log_accumulated_norm + end + # caching is not used here + if use_cache == false + tn1 = get_child_tn(ctree_to_tn_tree, c[1]) + tn2 = get_child_tn(ctree_to_tn_tree, c[2]) + inds_btree = ordered_igs_to_binary_tree( + ctree_to_adj_tree[c].children, + ctree_to_contract_igs[c], + ig_to_linear_order; + ansatz=ansatz, + ) + ctree_to_tn_tree[c], log_root_norm = approximate_contract_ctree_to_tensor( + [tn1..., tn2...], inds_btree; cutoff=cutoff, maxdim=maxdim + ) + log_accumulated_norm += log_root_norm + continue + end + # caching + # Note: cache_igs_right has a reversed ordering + center_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( + [ctree_to_adj_tree[i].children for i in [c, c[1], c[2]]], + [ctree_to_contract_igs[i] for i in [c, c[1], c[2]]], ) - ctree_to_tn_tree[c], log_root_norm = approximate_contract_ctree_to_tensor( - [tn1..., tn2...], inds_btree; cutoff=cutoff, maxdim=maxdim + if ansatz == "comb" + cache_binary_tree_left = line_to_tree([ + line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_left + ]) + cache_binary_tree_right = line_to_tree([ + line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_right + ]) + elseif ansatz == "mps" + left_order = vcat([ig_to_linear_order[ig] for ig in cache_igs_left]...) + cache_binary_tree_left = line_to_tree(left_order) + right_order = vcat([ig_to_linear_order[ig] for ig in reverse(cache_igs_right)]...) + cache_binary_tree_right = line_to_tree(reverse(right_order)) + end + cached_tn_tree, uncached_tn, new_ig_left, new_ig_right = get_tn_cache_info( + ctree_to_tn_tree, c[1], c[2], [cache_binary_tree_left, cache_binary_tree_right] ) - log_accumulated_norm += log_root_norm - continue - end - # caching - # Note: cache_igs_right has a reversed ordering - center_igs, cache_igs_left, cache_igs_right = get_igs_cache_info( - [ctree_to_adj_tree[i].children for i in [c, c[1], c[2]]], - [ctree_to_contract_igs[i] for i in [c, c[1], c[2]]], - ) - if ansatz == "comb" - cache_binary_tree_left = line_to_tree([ - line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_left - ]) - cache_binary_tree_right = line_to_tree([ - line_to_tree(ig_to_linear_order[ig]) for ig in cache_igs_right - ]) - elseif ansatz == "mps" - left_order = vcat([ig_to_linear_order[ig] for ig in cache_igs_left]...) - cache_binary_tree_left = line_to_tree(left_order) - right_order = vcat([ig_to_linear_order[ig] for ig in reverse(cache_igs_right)]...) - cache_binary_tree_right = line_to_tree(reverse(right_order)) - end - cached_tn_tree, uncached_tn, new_ig_left, new_ig_right = get_tn_cache_info( - ctree_to_tn_tree, c[1], c[2], [cache_binary_tree_left, cache_binary_tree_right] - ) - if new_ig_right == nothing && new_ig_left == nothing - @info "Caching is not used in this approximation" - @assert length(cached_tn_tree) == 0 - else - @info "Caching is used in this approximation", new_ig_left, new_ig_right - end - new_ig_to_binary_tree_pairs = Vector{Pair}() - new_igs = center_igs - new_ig_to_linear_order = ig_to_linear_order - if new_ig_left == nothing - new_igs = [cache_igs_left..., new_igs...] - else - new_ig_to_linear_order = merge( - new_ig_to_linear_order, Dict(new_ig_left => [new_ig_left.data]) + if new_ig_right == nothing && new_ig_left == nothing + @info "Caching is not used in this approximation" + @assert length(cached_tn_tree) == 0 + else + @info "Caching is used in this approximation", new_ig_left, new_ig_right + end + new_ig_to_binary_tree_pairs = Vector{Pair}() + new_igs = center_igs + new_ig_to_linear_order = ig_to_linear_order + if new_ig_left == nothing + new_igs = [cache_igs_left..., new_igs...] + else + new_ig_to_linear_order = merge( + new_ig_to_linear_order, Dict(new_ig_left => [new_ig_left.data]) + ) + new_igs = [new_ig_left, new_igs...] + push!(new_ig_to_binary_tree_pairs, new_ig_left.data => cache_binary_tree_left) + end + if new_ig_right == nothing + new_igs = [new_igs..., cache_igs_right...] + else + new_ig_to_linear_order = merge( + new_ig_to_linear_order, Dict(new_ig_right => [new_ig_right.data]) + ) + new_igs = [new_igs..., new_ig_right] + push!(new_ig_to_binary_tree_pairs, new_ig_right.data => cache_binary_tree_right) + end + inds_btree = ordered_igs_to_binary_tree( + new_igs, ctree_to_contract_igs[c], new_ig_to_linear_order; ansatz=ansatz ) - new_igs = [new_ig_left, new_igs...] - push!(new_ig_to_binary_tree_pairs, new_ig_left.data => cache_binary_tree_left) - end - if new_ig_right == nothing - new_igs = [new_igs..., cache_igs_right...] - else - new_ig_to_linear_order = merge( - new_ig_to_linear_order, Dict(new_ig_right => [new_ig_right.data]) + new_tn_tree, log_root_norm = approximate_contract_ctree_to_tensor( + uncached_tn, inds_btree; cutoff=cutoff, maxdim=maxdim ) - new_igs = [new_igs..., new_ig_right] - push!(new_ig_to_binary_tree_pairs, new_ig_right.data => cache_binary_tree_right) - end - inds_btree = ordered_igs_to_binary_tree( - new_igs, ctree_to_contract_igs[c], new_ig_to_linear_order; ansatz=ansatz - ) - new_tn_tree, log_root_norm = approximate_contract_ctree_to_tensor( - uncached_tn, inds_btree; cutoff=cutoff, maxdim=maxdim - ) - log_accumulated_norm += log_root_norm - if length(new_ig_to_binary_tree_pairs) != 0 - update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) + log_accumulated_norm += log_root_norm + if length(new_ig_to_binary_tree_pairs) != 0 + update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) + end + ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) end - ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) + tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) + return get_tensors(tn), log_accumulated_norm end - tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) - return get_tensors(tn), log_accumulated_norm end # interlaced HOSVD using caching From da09971089f72ed846c38978aa5086d82033fc89 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 3 Jan 2023 17:35:14 -0600 Subject: [PATCH 11/24] Modify approximate contract 3dcube example --- examples/approximate_contract/3dcube.jl | 100 ++++++++++++++++-- .../contract/contract.jl | 2 + 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 8baab479..3d092c2f 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -1,12 +1,84 @@ using ITensors, TimerOutputs -using ITensorNetworks.ApproximateTNContraction: approximate_contract +using NamedGraphs +using ITensorNetworks: contraction_sequence, ITensorNetwork +using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree using ITensorNetworks.ApproximateTNContraction: timer, inds_network, ising_partition -function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache) +function contract_log_norm(tn, seq) + if seq isa Vector + t1 = contract_log_norm(tn, seq[1]) + t2 = contract_log_norm(tn, seq[2]) + @info size(t1[1]), size(t2[1]) + out = t1[1] * t2[1] + nrm = norm(out) + out /= nrm + lognrm = log(nrm) + t1[2] + t2[2] + return (out, lognrm) + else + return tn[seq] + end +end + +function exact_contract(N) + ITensors.set_warn_order(100) + reset_timer!(timer) + linkdim = 2 + tn = vec(ising_partition(N, linkdim)) + # contraction_sequence(tn; alg="kahypar_bipartite", sc_target=30) + seq = line_to_tree([i for i in 1:prod(N)]) + tn = [(i, 0.0) for i in tn] + return contract_log_norm(tn, seq) +end + +function build_tntree(tn, N; env_line_size) + line_index = 1 + num_lines = N[2] * N[3] + tntree = nothing + while line_index <= N[2] * N[3] + partition = Vector{ITensor}() + @info "partition" + for _ in 1:env_line_size + if line_index <= num_lines + @info "line_index", line_index + push!(partition, tn[:, line_index]...) + line_index += 1 + end + end + if tntree == nothing + tntree = partition + else + tntree = [tntree, partition] + end + end + return tntree +end + +function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache, ortho, env_line_size) ITensors.set_warn_order(100) reset_timer!(timer) linkdim = 2 tn = ising_partition(N, linkdim) + if ortho == true + @info "orthogonalize tn towards the first vertex" + itn = ITensorNetwork(named_grid(N); link_space=2) + for i in 1:N[1] + for j in 1:N[2] + for k in 1:N[3] + itn[i, j, k] = tn[i, j, k] + end + end + end + itn = orthogonalize(itn, (1, 1, 1)) + @info itn[1, 1, 1] + @info itn[1, 1, 1].tensor + for i in 1:N[1] + for j in 1:N[2] + for k in 1:N[3] + tn[i, j, k] = itn[i, j, k] + end + end + end + end # tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) # tn = map(inds -> randomITensor(inds...), tn_inds) # tntree = nothing @@ -22,15 +94,14 @@ function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache) # end # end tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = tn[:, 1] - for i in 2:(N[2] * N[3]) - tntree = [tntree, tn[:, i]] - end + tntree = build_tntree(tn, N; env_line_size=env_line_size) + out_list = [] for _ in 1:num_iter out, log_acc_norm = approximate_contract( tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache ) @info "out is", log(out[1][1]) + log_acc_norm + push!(out_list, log(out[1][1]) + log_acc_norm) end show(timer) # after warmup, start to benchmark @@ -38,19 +109,26 @@ function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache) linkdim = 2 tn = ising_partition(N, linkdim) tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = tn[:, 1] - for i in 2:(N[2] * N[3]) - tntree = [tntree, tn[:, i]] - end + tntree = build_tntree(tn, N; env_line_size=env_line_size) for _ in 1:num_iter out, log_acc_norm = approximate_contract( tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache ) @info "out is", log(out[1][1]) + log_acc_norm + push!(out_list, log(out[1][1]) + log_acc_norm) end + @info "lnZ results are", out_list, "mean is", sum(out_list) / (num_iter * 2) return show(timer) end +# exact_contract((5, 5, 5)) bench_3d_cube( - (4, 6, 6); num_iter=1, cutoff=1e-8, maxdim=1e6, ansatz="comb", use_cache=false + (3, 10, 10); + num_iter=2, + cutoff=1e-8, + maxdim=256, + ansatz="mps", + use_cache=true, + ortho=false, + env_line_size=1, ) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index c6a9efdc..c0295449 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -21,6 +21,7 @@ function optcontract(t_list::Vector) end @assert t_list isa Vector{OrthogonalITensor} t_list = get_tensors(t_list) + # @info "start contract, size is", size(t_list) # for t in t_list # @info "size of t is", size(t) # end @@ -30,6 +31,7 @@ function optcontract(t_list::Vector) @timeit timer "contract" begin output = contract(t_list; sequence=seq) end + # @info "finish contract" return OrthogonalITensor(output) end end From 4422333bf958f772c6730f26f498621a8272f7ea Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 15:40:50 -0600 Subject: [PATCH 12/24] [ApproxContraction] Retire tensornetwork_graph.jl, rewrite mincut_tree --- .../contract/contract.jl | 8 +- .../contract/mincut_tree.jl | 337 +++++++----------- .../contract/tensornetwork_graph.jl | 141 -------- .../contract/tree_utils.jl | 3 + test/approximate_tn_contraction/contract.jl | 6 +- test/approximate_tn_contraction/indexgroup.jl | 59 +-- 6 files changed, 166 insertions(+), 388 deletions(-) delete mode 100644 src/ApproximateTNContraction/contract/tensornetwork_graph.jl diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index c0295449..5971a915 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -58,7 +58,7 @@ function approximate_contract_ctree_to_tensor( cutoff, maxdim, maxsize=10^15, - algorithm="mincut-mps", + algorithm="mps", ) uncontract_inds = noncommoninds(tn...) allinds = collect(Set(mapreduce(t -> collect(inds(t)), vcat, tn))) @@ -640,9 +640,9 @@ end function ordered_igs_to_binary_tree_mps( left_igs, right_igs, contract_igs, ig_to_linear_order ) - left_order = vcat([ig_to_linear_order[ig] for ig in left_igs]...) - right_order = vcat([ig_to_linear_order[ig] for ig in right_igs]...) - contract_order = vcat([ig_to_linear_order[ig] for ig in contract_igs]...) + left_order = get_leaves([ig_to_linear_order[ig] for ig in left_igs]) + right_order = get_leaves([ig_to_linear_order[ig] for ig in right_igs]) + contract_order = get_leaves([ig_to_linear_order[ig] for ig in contract_igs]) if length(left_order) <= length(right_order) left_order = [left_order..., contract_order...] else diff --git a/src/ApproximateTNContraction/contract/mincut_tree.jl b/src/ApproximateTNContraction/contract/mincut_tree.jl index 57f31b40..6d51bd42 100644 --- a/src/ApproximateTNContraction/contract/mincut_tree.jl +++ b/src/ApproximateTNContraction/contract/mincut_tree.jl @@ -1,41 +1,49 @@ +using ITensorNetworks: ITensorNetwork # a large number to prevent this edge being a cut -MAX_WEIGHT = 100000 +MAX_WEIGHT = 1e32 + +function _build_pseudo_network( + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}} +) + # create a pseudo_tn (a tn without any data inside) whose outinds weights are MAX_WEIGHT + out_to_pseudo_ind = Dict{Index,Index}() + for ind in outinds + out_to_pseudo_ind[ind] = Index(MAX_WEIGHT, ind.tags) + end + pseudo_network = Vector{ITensor}() + for t in network + inds1 = [i for i in inds(t) if !(i in outinds)] + inds2 = [out_to_pseudo_ind[i] for i in inds(t) if i in outinds] + newt = ITensor(inds1..., inds2...) + push!(pseudo_network, newt) + end + return pseudo_network, out_to_pseudo_ind +end function inds_binary_tree( - network::Vector{ITensor}, inds_groups::Vector{<:Vector}; kwargs... + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" ) - tng = TensorNetworkGraph(network, vectorize(inds_groups)) - function get_sub_tree(inds) - @assert all(ind -> ind isa Index, inds) - if length(inds) == 1 - return inds - end - inds = [[i] for i in inds] - return inds_binary_tree!(tng, inds; kwargs...) + if outinds == nothing + outinds = noncommoninds(network...) end - inds_groups = [get_sub_tree(inds) for inds in inds_groups] - if length(inds_groups) <= 2 - return inds_groups + if length(outinds) == 1 + return outinds end - return inds_binary_tree!(tng, inds_groups; kwargs...) + p_network, out_to_pseudo_ind = _build_pseudo_network(network, outinds) + return _inds_binary_tree(network => p_network, out_to_pseudo_ind; algorithm=algorithm) end -function inds_binary_tree!(tng::TensorNetworkGraph, outinds::Vector; algorithm="mincut") - @assert algorithm in ["mincut", "mincut-mps", "mps"] - @assert all(ind -> ind in keys(tng.out_edge_dict), outinds) +function _inds_binary_tree( + network_pair::Pair{Vector{ITensor},Vector{ITensor}}, + out_to_pseudo_ind::Dict{Index,Index}; + algorithm="mincut", +) + @assert algorithm in ["mincut", "mps"] if algorithm == "mincut" - return mincut_inds!(tng, outinds) - elseif algorithm == "mincut-mps" - inds_tree = mincut_inds!(tng, outinds) - linear_tree = linearize(inds_tree, tng) - out_inds = linear_tree[1] - for i in 2:length(linear_tree) - out_inds = [out_inds, linear_tree[i]] - end - return out_inds + return _mincut_inds(network_pair, out_to_pseudo_ind) elseif algorithm == "mps" - return line_to_tree(inds_linear_order!(tng, outinds)) + return line_to_tree(_inds_linear_order(network_pair, out_to_pseudo_ind)) end end @@ -48,219 +56,130 @@ function inds_linear_order( if length(outinds) == 1 return outinds end - tng = TensorNetworkGraph(network, outinds) - grouped_uncontracted_inds = [[i] for i in outinds] - return inds_linear_order!(tng, grouped_uncontracted_inds) + p_network, out_to_pseudo_ind = _build_pseudo_network(network, outinds) + return _inds_linear_order(network => p_network, out_to_pseudo_ind) end -function inds_linear_order!(tng::TensorNetworkGraph, outinds::Vector) +function _inds_linear_order( + network_pair::Pair{Vector{ITensor},Vector{ITensor}}, out_to_pseudo_ind::Dict{Index,Index} +) + outinds = collect(keys(out_to_pseudo_ind)) + outinds = [[i] for i in outinds] @assert length(outinds) >= 1 - # base case here, for the case length(outinds) == 2, we still need to do the update - if length(outinds) == 1 - return outinds[1] - end - if length(outinds) == 2 + if length(outinds) <= 2 return outinds end - new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 1, 1))) + new_edge = new_edge_mincut( + network_pair, out_to_pseudo_ind, collect(powerset(outinds, 1, 1)) + ) new_edge = new_edge[1] - # outinds = update!(tng, outinds, new_edge, minval) - # first_ind = new_edge linear_order = [new_edge] while length(outinds) > 2 splitinds = [[new_edge, i] for i in outinds if i != new_edge] - new_edge, minval = new_edge_mincut(tng, splitinds) - outinds = update!(tng, outinds, new_edge, minval) - # first_ind = new_edge + new_edge = new_edge_mincut(network_pair, out_to_pseudo_ind, splitinds) push!(linear_order, new_edge[2]) + outinds = setdiff(outinds, new_edge) + outinds = vcat([new_edge], outinds) end - splitinds = [[new_edge, i] for i in outinds if i != new_edge] - @assert length(splitinds) == 1 - push!(linear_order, splitinds[1][2]) + last_index = [i for i in outinds if i != new_edge] + @assert length(last_index) == 1 + push!(linear_order, last_index[1]) return linear_order end -function inds_binary_tree( - network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" +function _mincut_inds( + network_pair::Pair{Vector{ITensor},Vector{ITensor}}, out_to_pseudo_ind::Dict{Index,Index} ) - if outinds == nothing - outinds = noncommoninds(network...) - end - if length(outinds) == 1 - return outinds - end - if algorithm == "sequential-mps" - out_inds = [outinds[1]] - for i in 2:length(outinds) - out_inds = [out_inds, [outinds[i]]] - end - return out_inds - end - tng = TensorNetworkGraph(network, outinds) - grouped_uncontracted_inds = [[i] for i in outinds] - return inds_binary_tree!(tng, grouped_uncontracted_inds; algorithm=algorithm) + outinds = collect(keys(out_to_pseudo_ind)) + outinds = [[i] for i in outinds] + return __mincut_inds(network_pair, out_to_pseudo_ind, outinds) end -#TODO: rewrite this function -#TODO: pick one end deterministically -function linearize(inds_tree::Vector, tng::TensorNetworkGraph) - get_dist(edge, distances) = distances.dists[tng.inner_edge_dict[edge][1]] - function get_boundary_dists(line, source) - first, last = line[1], line[end] - ds = dijkstra_shortest_paths(tng.graph, source, tng.weights) - return get_dist(first, ds), get_dist(last, ds) - end - - if length(inds_tree) == 1 - return inds_tree - end - left = linearize(inds_tree[1], tng) - right = linearize(inds_tree[2], tng) - if length(left) == 1 && length(right) == 1 - return [left, right] - end - if length(left) == 1 - source = tng.inner_edge_dict[left][1] - dist_first, dist_last = get_boundary_dists(right, source) - if dist_last < dist_first - right = reverse(right) - end - return [left, right...] - end - if length(right) == 1 - source = tng.inner_edge_dict[right][1] - dist_first, dist_last = get_boundary_dists(left, source) - if dist_last > dist_first - left = reverse(left) - end - return [left..., right] - end - s1, s2 = tng.inner_edge_dict[left[1]][1], tng.inner_edge_dict[left[end]][1] - dist1_first, dist1_last = get_boundary_dists(right, s1) - dist2_first, dist2_last = get_boundary_dists(right, s2) - if min(dist1_first, dist1_last) < min(dist2_first, dist2_last) - left = reverse(left) - if dist1_last < dist1_first - right = reverse(right) - end - else - if dist2_last < dist2_first - right = reverse(right) - end - end - return [left..., right...] -end - -function mincut_subnetwork( - network::Vector{ITensor}, sourceinds::Vector, uncontract_inds::Vector +function __mincut_inds( + network_pair::Pair{Vector{ITensor},Vector{ITensor}}, + out_to_pseudo_ind::Dict{Index,Index}, + outinds::Vector{<:Vector}, ) - @timeit timer "mincut_subnetwork" begin - if length(sourceinds) == length(uncontract_inds) - return network - end - tng = TensorNetworkGraph(network) - grouped_sourceinds = [[ind] for ind in sourceinds] - part1, part2, mincut = mincut_value(tng, grouped_sourceinds) - @assert length(part1) > 1 - @assert length(part2) > 1 - return [network[i] for i in part1 if i <= length(network)] - end -end - -function mincut_inds!(tng::TensorNetworkGraph, outinds::Vector) @assert length(outinds) >= 1 - # base case here, for the case length(outinds) == 2, we still need to do the update if length(outinds) == 1 return outinds[1] end - new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) - outinds = update!(tng, outinds, new_edge, minval) - return mincut_inds!(tng, outinds) -end - -function mincut_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) - @timeit timer "mincut_inds" begin - tng = copy(tng) - uncontract_inds = copy(uncontract_inds) - return mincut_inds!(tng, uncontract_inds) + if length(outinds) == 2 + return outinds end + new_edge = new_edge_mincut( + network_pair, out_to_pseudo_ind, collect(powerset(outinds, 2, 2)) + ) + outinds = setdiff(outinds, new_edge) + outinds = vcat([new_edge], outinds) + return __mincut_inds(network_pair, out_to_pseudo_ind, outinds) end -# update the graph -function update!(tng::TensorNetworkGraph, uncontract_inds::Vector, new_edge::Vector, minval) - add_vertex!(tng.graph) - last_vertex = size(tng.graph)[1] - u1, w_u1 = tng.out_edge_dict[new_edge[1]] - u2, w_u2 = tng.out_edge_dict[new_edge[2]] - Graphs.add_edge!(tng.graph, u1, last_vertex) - Graphs.add_edge!(tng.graph, u2, last_vertex) - Graphs.add_edge!(tng.graph, last_vertex, u1) - Graphs.add_edge!(tng.graph, last_vertex, u2) - new_weights = zeros(last_vertex, last_vertex) - new_weights[1:(last_vertex - 1), 1:(last_vertex - 1)] = tng.weights - #if not setting to MAX_WEIGHT would affect later tree selections - new_weights[u1, last_vertex] = MAX_WEIGHT - new_weights[u2, last_vertex] = MAX_WEIGHT - new_weights[last_vertex, u1] = MAX_WEIGHT - new_weights[last_vertex, u2] = MAX_WEIGHT - # update the dict - tng.inner_edge_dict[new_edge[1]] = (u1, last_vertex, MAX_WEIGHT)#w_u1) - tng.inner_edge_dict[new_edge[2]] = (u2, last_vertex, MAX_WEIGHT)#w_u2) - delete!(tng.out_edge_dict, new_edge[1]) - delete!(tng.out_edge_dict, new_edge[2]) - tng.out_edge_dict[new_edge] = (last_vertex, minval) - # update uncontract_inds - uncontract_inds = setdiff(uncontract_inds, new_edge) - uncontract_inds = vcat([new_edge], uncontract_inds) - tng.weights = new_weights - return uncontract_inds -end - -# TODO: rewrite this function -function new_edge_mincut(tng::TensorNetworkGraph, split_inds_list::Vector) - mincuts = [mincut_value(tng, split_inds)[3] for split_inds in split_inds_list] - split_sizes = [ - sum([tng.out_edge_dict[ind][2] for ind in split_inds]) for split_inds in split_inds_list +function new_edge_mincut( + network_pair::Pair{Vector{ITensor},Vector{ITensor}}, + out_to_pseudo_ind::Dict{Index,Index}, + split_inds_list::Vector, +) + outinds = collect(keys(out_to_pseudo_ind)) + p_outinds = collect(values(out_to_pseudo_ind)) + mincuts, pseudo_mincuts, dists = [], [], [] + for split_inds in split_inds_list + if length(split_inds) == 2 + push!( + dists, + distance(network_pair.first, vectorize(split_inds[1]), vectorize(split_inds[2])), + ) + else + push!(dists, 0.0) + end + split_inds = vectorize(split_inds) + p_split_inds = [out_to_pseudo_ind[i] for i in split_inds] + push!(mincuts, mincut_value(network_pair.first, split_inds, outinds)[3]) + push!(pseudo_mincuts, mincut_value(network_pair.second, p_split_inds, p_outinds)[3]) + end + indices_min = [i for i in 1:length(mincuts) if mincuts[i] == min(mincuts...)] + selected_pseudo_mincuts = [pseudo_mincuts[i] for i in indices_min] + indices_min = [ + i for i in indices_min if pseudo_mincuts[i] == min(selected_pseudo_mincuts...) ] - dists = [distance(tng, inds...) for inds in split_inds_list] - weights = [min(mincuts[i], split_sizes[i]) for i in 1:length(mincuts)] - indices_min = [i for i in 1:length(mincuts) if weights[i] == min(weights...)] - cuts_min = [mincuts[i] for i in indices_min] - indices_min = [i for i in indices_min if mincuts[i] == min(cuts_min...)] dists_min = [dists[i] for i in indices_min] _, index = findmin(dists_min) i = indices_min[index] - minval = weights[i] new_edge = split_inds_list[i] - return new_edge, minval + return new_edge end -function mincut_value(tng::TensorNetworkGraph, split_inds::Vector) - tng = copy(tng) - # add two vertices to the graph to model the s and t - add_vertices!(tng.graph, 2) - t = size(tng.graph)[1] - s = t - 1 - new_weights = zeros(t, t) - new_weights[1:(t - 2), 1:(t - 2)] = tng.weights - for ind in split_inds - u, _ = tng.out_edge_dict[ind] - Graphs.add_edge!(tng.graph, u, s) - Graphs.add_edge!(tng.graph, s, u) - new_weights[u, s] = MAX_WEIGHT - new_weights[s, u] = MAX_WEIGHT - end - terminal_inds = setdiff(noncommoninds(tng), split_inds) - for ind in terminal_inds - u, _ = tng.out_edge_dict[ind] - Graphs.add_edge!(tng.graph, u, t) - Graphs.add_edge!(tng.graph, t, u) - new_weights[u, t] = MAX_WEIGHT - new_weights[t, u] = MAX_WEIGHT +function distance(network::Vector{ITensor}, inds1::Vector{<:Index}, inds2::Vector{<:Index}) + new_t1 = ITensor(inds1...) + new_t2 = ITensor(inds2...) + tn = ITensorNetwork([network..., new_t1, new_t2]) + ds = dijkstra_shortest_paths(tn, length(network) + 1, weights(tn)) + return ds.dists[length(network) + 2] +end + +function mincut_value( + network::Vector{ITensor}, source_inds::Vector{<:Index}, out_inds::Vector{<:Index} +) + terminal_inds = setdiff(out_inds, source_inds) + new_t1 = ITensor(source_inds...) + new_t2 = ITensor(terminal_inds...) + tn = ITensorNetwork([network..., new_t1, new_t2]) + return GraphsFlows.mincut(tn, length(network) + 1, length(network) + 2, weights(tn)) +end + +function mincut_subnetwork( + network::Vector{ITensor}, source_inds::Vector{<:Index}, out_inds::Vector{<:Index} +) + @timeit timer "mincut_subnetwork" begin + if length(source_inds) == length(out_inds) + return network + end + p_network, out_to_pseudo_ind = _build_pseudo_network(network, out_inds) + p_source_inds = [out_to_pseudo_ind[i] for i in source_inds] + p_out_inds = [out_to_pseudo_ind[i] for i in out_inds] + part1, part2, val = mincut_value(p_network, p_source_inds, p_out_inds) + @assert length(part1) > 1 + @assert length(part2) > 1 + return [network[i] for i in part1 if i <= length(network)] end - # this t and s sequence makes sure part1 is the largest subgraph yielding mincut - part2, part1, flow = GraphsFlows.mincut( - tng.graph, t, s, new_weights, EdmondsKarpAlgorithm() - ) - return part1, part2, flow end diff --git a/src/ApproximateTNContraction/contract/tensornetwork_graph.jl b/src/ApproximateTNContraction/contract/tensornetwork_graph.jl deleted file mode 100644 index 2de032e6..00000000 --- a/src/ApproximateTNContraction/contract/tensornetwork_graph.jl +++ /dev/null @@ -1,141 +0,0 @@ -mutable struct TensorNetworkGraph - network::Vector{ITensor} - graph::SimpleDiGraph - weights::Matrix - #a dict that maps uncontracted Index to the adjacent vertices pair (i,j) - out_edge_dict::Dict - inner_edge_dict::Dict -end - -function TensorNetworkGraph(network::Vector{ITensor}) - uncontract_inds = noncommoninds(network...) - return TensorNetworkGraph(network, uncontract_inds) -end - -function TensorNetworkGraph(network::Vector{ITensor}, uncontract_inds::Vector) - graph = Graphs.DiGraph(length(network)) - # construct contract_edges - contract_edges = [] - inner_edge_dict = Dict() - for (i, t) in enumerate(network) - for ind in setdiff(inds(t), uncontract_inds) - if !haskey(inner_edge_dict, [ind]) - inner_edge_dict[[ind]] = (i, log2(space(ind))) - else - @assert(length(inner_edge_dict[[ind]]) == 2) - inner_edge_dict[[ind]] = (inner_edge_dict[[ind]][1], i, inner_edge_dict[[ind]][2]) - push!(contract_edges, inner_edge_dict[[ind]]) - end - end - end - weights = zeros(length(network), length(network)) - for e in contract_edges - u, v, f = e - Graphs.add_edge!(graph, u, v) - Graphs.add_edge!(graph, v, u) - weights[u, v] = f - weights[v, u] = f - end - # construct out_edge_dict - out_edge_dict = Dict() - for (i, t) in enumerate(network) - ucinds = intersect(inds(t), uncontract_inds) - if length(ucinds) == 0 - continue - end - for ind in ucinds - out_edge_dict[[ind]] = (i, log2(space(ind))) - end - end - return TensorNetworkGraph(network, graph, weights, out_edge_dict, inner_edge_dict) -end - -Base.show(io::IO, tng::TensorNetworkGraph) = print(io, tng.out_edge_dict) - -function Base.copy(tng::TensorNetworkGraph) - return TensorNetworkGraph( - tng.network, - copy(tng.graph), - copy(tng.weights), - copy(tng.out_edge_dict), - copy(tng.inner_edge_dict), - ) -end - -function ITensors.noncommoninds(tng::TensorNetworkGraph) - return keys(tng.out_edge_dict) -end - -function distance(tng::TensorNetworkGraph, s, t) - sindex = tng.out_edge_dict[s][1] - ds = dijkstra_shortest_paths(tng.graph, sindex, tng.weights) - get_dist(edge) = ds.dists[tng.out_edge_dict[edge][1]] - return get_dist(t) -end - -#TODO: delete this -distance(tng::TensorNetworkGraph, s) = 0.0 - -function insert_outedge_vertex!(tng::TensorNetworkGraph) - if length(tng.out_edge_dict) == 0 - return nothing - end - add_vertices!(tng.graph, 1) - t = size(tng.graph)[1] - new_weights = zeros(t, t) - new_weights[1:(t - 1), 1:(t - 1)] = tng.weights - for (inds, edge) in tng.out_edge_dict - u, wu = edge - Graphs.add_edge!(tng.graph, u, t) - Graphs.add_edge!(tng.graph, t, u) - new_weights[u, t] = wu - new_weights[t, u] = wu - tng.out_edge_dict[inds] = (u, t, wu) - end - return tng.weights = new_weights -end - -function indsname(inds::Vector) - if length(inds) == 1 - return string(inds[1].tags) - end - return "" -end - -function visualize(tng::TensorNetworkGraph) - tng = copy(tng) - insert_outedge_vertex!(tng::TensorNetworkGraph) - wg = SimpleWeightedGraph(tng.graph) - for e in edges(tng.graph) - add_edge!(wg, src(e), dst(e), tng.weights[src(e), dst(e)]) - end - edgelabel_dict = Dict{Tuple{Int,Int},String}() - edgecolor_dict = Dict() - for (inds, edge) in tng.inner_edge_dict - if indsname(inds) == "" - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :blue - else - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :black - end - end - for (inds, edge) in tng.out_edge_dict - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :red - end - return graphplot( - wg; - markersize=0.3, - # names=names, - edgelabel=edgelabel_dict, - curves=false, - edgecolor=edgecolor_dict, - linewidth=20, - fontsize=30, - size=(7000, 7000), - ) -end diff --git a/src/ApproximateTNContraction/contract/tree_utils.jl b/src/ApproximateTNContraction/contract/tree_utils.jl index 692186b7..211c7804 100644 --- a/src/ApproximateTNContraction/contract/tree_utils.jl +++ b/src/ApproximateTNContraction/contract/tree_utils.jl @@ -64,6 +64,9 @@ end # example: [[[1,2], [3,4]], [[5,6], [7,8]]] = [[1,2], [3,4], [5,6], [7,8]] function get_leaves(tree::Vector) + if tree == [] + return [] + end if !(tree isa Vector{<:Vector}) return [tree] end diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index cba6aacc..69325205 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -93,22 +93,18 @@ end out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut") @test length(out) == 2 - out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mincut-mps") - @test length(out) == 2 out = inds_binary_tree(network, [i, j, k, l, m, n, o, p]; algorithm="mps") @test length(out) == 2 end @testset "test inds_binary_tree of a 2D network" begin - N = (8, 8, 3) + N = (3, 3, 3) linkdim = 2 tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) tn = map(inds -> randomITensor(inds...), tn_inds) network = vec(tn[:, :, 1]) out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") @test length(out) == 2 - out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut-mps") - @test length(out) == 2 out = inds_binary_tree(network, noncommoninds(network...); algorithm="mps") @test length(out) == 2 end diff --git a/test/approximate_tn_contraction/indexgroup.jl b/test/approximate_tn_contraction/indexgroup.jl index 8d35d44b..24480583 100644 --- a/test/approximate_tn_contraction/indexgroup.jl +++ b/test/approximate_tn_contraction/indexgroup.jl @@ -12,34 +12,34 @@ using ITensorNetworks.ApproximateTNContraction: minswap_adjacency_tree, approximate_contract -# @testset "test generate_adjacency_tree" begin -# N = (3, 3) -# tn_inds = inds_network(N...; linkdims=2, periodic=false) -# tn = vec(map(inds -> randomITensor(inds...), tn_inds)) -# ctree = line_network(tn) -# tn_leaves = get_leaves(ctree) -# ctrees = topo_sort(ctree; leaves=tn_leaves) -# ctree_to_igs = Dict{Vector,Vector{IndexGroup}}() -# index_groups = get_index_groups(ctree) -# for c in vcat(tn_leaves, ctrees) -# ctree_to_igs[c] = neighbor_index_groups(c, index_groups) -# end -# ctree_to_ancestors = get_ancestors(ctree) -# adj_tree1 = generate_adjacency_tree( -# tn_leaves[4], ctree_to_ancestors[tn_leaves[4]], ctree_to_igs -# ) -# adj_tree2 = generate_adjacency_tree( -# ctrees[2], ctree_to_ancestors[ctrees[2]], ctree_to_igs -# ) -# for adj_tree in [adj_tree1, adj_tree1] -# @test length(adj_tree.children) == 3 -# @test adj_tree.fixed_order = true -# c1, c2, c3 = adj_tree.children -# @test length(c1.children) == 1 -# @test length(c2.children) == 2 -# @test length(c3.children) == 1 -# end -# end +@testset "test generate_adjacency_tree" begin + N = (3, 3) + tn_inds = inds_network(N...; linkdims=2, periodic=false) + tn = vec(map(inds -> randomITensor(inds...), tn_inds)) + ctree = line_network(tn) + tn_leaves = get_leaves(ctree) + ctrees = topo_sort(ctree; leaves=tn_leaves) + ctree_to_igs = Dict{Vector,Vector{IndexGroup}}() + index_groups = get_index_groups(ctree) + for c in vcat(tn_leaves, ctrees) + ctree_to_igs[c] = neighbor_index_groups(c, index_groups) + end + ctree_to_ancestors = get_ancestors(ctree) + adj_tree1 = generate_adjacency_tree( + tn_leaves[4], ctree_to_ancestors[tn_leaves[4]], ctree_to_igs + ) + adj_tree2 = generate_adjacency_tree( + ctrees[2], ctree_to_ancestors[ctrees[2]], ctree_to_igs + ) + for adj_tree in [adj_tree1, adj_tree1] + @test length(adj_tree.children) == 3 + @test adj_tree.fixed_order = true + c1, c2, c3 = adj_tree.children + @test length(c1.children) == 1 + @test length(c2.children) == 2 + @test length(c3.children) == 1 + end +end # @testset "test minswap_adjacency_tree!" begin # i = IndexGroup([Index(2, "i")]) @@ -74,5 +74,6 @@ using ITensorNetworks.ApproximateTNContraction: tn_inds = inds_network(N...; linkdims=2, periodic=false) tn = vec(map(inds -> randomITensor(inds...), tn_inds)) ctree = line_network(tn) - approximate_contract(ctree; cutoff=1e-5, maxdim=20) + approximate_contract(ctree; cutoff=1e-5, maxdim=20, ansatz="mps") + approximate_contract(ctree; cutoff=1e-5, maxdim=20, ansatz="comb") end From 9a99007ff0579b5ad15f0b470f5e7e71f033f67c Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 15:45:26 -0600 Subject: [PATCH 13/24] Remove unused examples/peps/utils.jl --- examples/peps/utils.jl | 222 ----------------------------------------- 1 file changed, 222 deletions(-) delete mode 100644 examples/peps/utils.jl diff --git a/examples/peps/utils.jl b/examples/peps/utils.jl deleted file mode 100644 index 74b5a7b7..00000000 --- a/examples/peps/utils.jl +++ /dev/null @@ -1,222 +0,0 @@ -using ITensors -using ITensorNetworks -using Dictionaries -using Graphs -using NamedGraphs -using ITensors.ContractionSequenceOptimization - -function cartesian_to_linear(dims::Tuple) - return Dictionary(vec(Tuple.(CartesianIndices(dims))), 1:prod(dims)) -end - -NamedGraphs.NamedDimGraph(vertices::Vector) = NamedDimGraph(tuple.(vertices)) -function NamedGraphs.NamedDimGraph(vertices::Vector{<:Tuple}) - return NamedDimGraph(Graph(length(vertices)); vertices) -end - -function rename_vertices(e::AbstractEdge, name_map::Dictionary) - return typeof(e)(name_map[src(e)], name_map[dst(e)]) -end - -function rename_vertices(g::NamedDimGraph, name_map::Dictionary) - original_vertices = vertices(g) - new_vertices = getindices(name_map, original_vertices) - new_g = NamedDimGraph(new_vertices) - for e in edges(g) - add_edge!(new_g, rename_vertices(e, name_map)) - end - return new_g -end - -function rename_vertices(g::NamedDimGraph, name_map::Function) - original_vertices = vertices(g) - return rename_vertices(g, Dictionary(original_vertices, name_map.(original_vertices))) -end - -# Convert to real if possible -maybe_real(x::Real) = x -maybe_real(x::Complex) = iszero(imag(x)) ? real(x) : x - -function ITensors.ITensor(o::Op, s::IndsNetwork) - s⃗ = [only(s[nᵢ]) for nᵢ in Ops.sites(o)] - return op(Ops.which_op(o), s⃗; Ops.params(o)...) -end - -function ITensors.ITensor(∏o::Prod, s::IndsNetwork) - T = ITensor(1.0) - for oᵢ in Ops.terms(∏o) - Tᵢ = ITensor(oᵢ, s) - # For now, only support operators on distinct - # sites. - @assert !hascommoninds(T, Tᵢ) - T *= Tᵢ - end - return T -end - -function ITensors.ITensor(o::Scaled, s::IndsNetwork) - return maybe_real(Ops.coefficient(o)) * ITensor(Ops.argument(o), s) -end - -function ITensors.ITensor(o::Ops.Exp, s::IndsNetwork) - return exp(ITensor(Ops.argument(o), s)) -end - -function Base.Vector{ITensor}(o::Union{Sum,Prod}, s::IndsNetwork) - T⃗ = ITensor[] - for oᵢ in Ops.terms(o) - Tᵢ = ITensor(oᵢ, s) - T⃗ = [T⃗; Tᵢ] - end - return T⃗ -end - -using ITensorNetworks: ⊔ - -function neighbor_vertices(ψ::ITensorNetwork, T::ITensor) - ψT = ψ ⊔ ITensorNetwork([T]) - v⃗ = neighbors(ψT, (2, 1)) - return Base.tail.(v⃗) -end - -function ITensors.apply(o::ITensor, ψ::ITensorNetwork; cutoff, maxdim, normalize=false) - ψ = copy(ψ) - v⃗ = neighbor_vertices(ψ, o) - if length(v⃗) == 1 - oψᵥ = apply(o, ψ[v⃗[1]]) - if normalize - oψᵥ ./= norm(oψᵥ) - end - ψ[v⃗[1]] = oψᵥ - elseif length(v⃗) == 2 - e = v⃗[1] => v⃗[2] - if !has_edge(ψ, e) - error("Vertices where the gates are being applied must be neighbors for now.") - end - oψᵥ = apply(o, ψ[v⃗[1]] * ψ[v⃗[2]]) - ψᵥ₁, ψᵥ₂ = factorize( - oψᵥ, inds(ψ[v⃗[1]]); cutoff, maxdim, tags=ITensorNetworks.edge_tag(e) - ) - if normalize - ψᵥ₁ ./= norm(ψᵥ₁) - ψᵥ₂ ./= norm(ψᵥ₂) - end - ψ[v⃗[1]] = ψᵥ₁ - ψ[v⃗[2]] = ψᵥ₂ - elseif length(v⃗) < 1 - error("Gate being applied does not share indices with tensor network.") - elseif length(v⃗) > 2 - error("Gates with more than 2 sites is not supported yet.") - end - return ψ -end - -function ITensors.apply( - o⃗::Vector{ITensor}, ψ::ITensorNetwork; cutoff, maxdim, normalize=false -) - o⃗ψ = ψ - for oᵢ in o⃗ - o⃗ψ = apply(oᵢ, o⃗ψ; cutoff, maxdim, normalize) - end - return o⃗ψ -end - -function flattened_inner_network(ϕ::ITensorNetwork, ψ::ITensorNetwork) - tn = inner(ϕ, ψ) - for v in vertices(ψ) - tn = ITensors.contract(tn, (2, v...) => (1, v...)) - end - return tn -end - -function contract_inner(ϕ::ITensorNetwork, ψ::ITensorNetwork; sequence=nothing) - tn = inner(ϕ, ψ) - # TODO: convert to an IndsNetwork and compute the contraction sequence - for v in vertices(ψ) - tn = ITensors.contract(tn, (2, v...) => (1, v...)) - end - if isnothing(sequence) - sequence = optimal_contraction_sequence(tn) - end - return ITensors.contract(tn; sequence)[] -end - -norm2(ψ::ITensorNetwork; sequence) = contract_inner(ψ, ψ; sequence) - -function ITensors.expect(op::String, ψ::ITensorNetwork; cutoff, maxdim) - res = Dictionary(vertices(ψ), Vector{Float64}(undef, nv(ψ))) - sequence = optimal_contraction_sequence(flattened_inner_network(ψ, ψ)) - normψ² = norm2(ψ; sequence) - for v in vertices(ψ) - O = ITensor(Op(op, v), s) - Oψ = apply(O, ψ; cutoff, maxdim) - res[v] = contract_inner(ψ, Oψ; sequence) / normψ² - end - return res -end - -function ITensors.expect(ℋ::OpSum, ψ::ITensorNetwork; cutoff, maxdim) - s = siteinds(ψ) - h⃗ = Vector{ITensor}(ℋ, s) - sequence = optimal_contraction_sequence(flattened_inner_network(ψ, ψ)) - h⃗ψ = [apply(hᵢ, ψ; cutoff, maxdim) for hᵢ in h⃗] - ψhᵢψ = [contract_inner(ψ, hᵢψ; sequence) for hᵢψ in h⃗ψ] - ψh⃗ψ = sum(ψhᵢψ) - ψψ = norm2(ψ; sequence) - return ψh⃗ψ / ψψ -end - -function randomITensorNetwork(s; link_space) - ψ = ITensorNetwork(s; link_space) - for v in vertices(ψ) - ψᵥ = copy(ψ[v]) - randn!(ψᵥ) - ψᵥ ./= norm(ψᵥ) - ψ[v] = ψᵥ - end - return ψ -end - -function ITensors.MPO(opsum::OpSum, s::IndsNetwork) - s_linear = [only(s[v]) for v in 1:nv(s)] - return MPO(opsum, s_linear) -end - -function ITensors.randomMPS(s::IndsNetwork, args...; kwargs...) - s_linear = [only(s[v]) for v in 1:nv(s)] - return randomMPS(s_linear, args...; kwargs...) -end - -function ITensors.MPS(s::IndsNetwork, args...; kwargs...) - s_linear = [only(s[v]) for v in 1:nv(s)] - return MPS(s_linear, args...; kwargs...) -end - -maybe_only(x) = x -maybe_only(x::Tuple{T}) where {T} = only(x) - -function ising(g::AbstractGraph; h) - ℋ = OpSum() - for e in edges(g) - ℋ -= "Z", maybe_only(src(e)), "Z", maybe_only(dst(e)) - end - for v in vertices(g) - ℋ += h, "X", maybe_only(v) - end - return ℋ -end - -function tebd(ℋ::OpSum, ψ::ITensorNetwork; β, Δβ, maxdim, cutoff) - 𝒰 = exp(-Δβ * ℋ; alg=Trotter{2}()) - # Imaginary time evolution terms - u⃗ = Vector{ITensor}(𝒰, s) - nsteps = Int(β ÷ Δβ) - for step in 1:nsteps - if step % 10 == 0 - @show step, (step - 1) * Δβ, β - end - ψ = ITensorNetworks.insert_links(ψ) - ψ = apply(u⃗, ψ; cutoff, maxdim, normalize=true) - end - return ψ -end From 1d2ffe3ca9e23e31aac590726bb825534a8f8ff2 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 21:45:22 -0600 Subject: [PATCH 14/24] [ApproxContraction] Remove old files --- examples/approximate_contract/3dcube.jl | 4 +- .../ApproximateTNContraction.jl | 3 - .../contract/contract.jl | 1 - .../contract/old_files/mincut_tree__.jl | 208 ++++++++++++++++++ .../contract/old_files/tensornetwork_graph.jl | 142 ++++++++++++ .../models/hamiltonians.jl | 144 ------------ .../models/ising_classical_2d.jl | 95 -------- src/ApproximateTNContraction/models/models.jl | 19 -- .../networks/3d_classical_ising.jl | 7 +- .../networks/inds_network.jl | 68 ------ .../networks/lattices.jl | 125 ----------- test/approximate_tn_contraction/contract.jl | 32 ++- test/approximate_tn_contraction/indexgroup.jl | 8 +- test/approximate_tn_contraction/lattice.jl | 9 - test/approximate_tn_contraction/models.jl | 14 -- test/approximate_tn_contraction/runtests.jl | 10 +- 16 files changed, 382 insertions(+), 507 deletions(-) create mode 100644 src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl create mode 100644 src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl delete mode 100644 src/ApproximateTNContraction/models/hamiltonians.jl delete mode 100644 src/ApproximateTNContraction/models/ising_classical_2d.jl delete mode 100644 src/ApproximateTNContraction/models/models.jl delete mode 100644 src/ApproximateTNContraction/networks/inds_network.jl delete mode 100644 src/ApproximateTNContraction/networks/lattices.jl delete mode 100644 test/approximate_tn_contraction/lattice.jl delete mode 100644 test/approximate_tn_contraction/models.jl diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 3d092c2f..aab7efe8 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -123,10 +123,10 @@ end # exact_contract((5, 5, 5)) bench_3d_cube( - (3, 10, 10); + (3, 3, 3); num_iter=2, cutoff=1e-8, - maxdim=256, + maxdim=16, ansatz="mps", use_cache=true, ortho=false, diff --git a/src/ApproximateTNContraction/ApproximateTNContraction.jl b/src/ApproximateTNContraction/ApproximateTNContraction.jl index e1492d5d..7893c166 100644 --- a/src/ApproximateTNContraction/ApproximateTNContraction.jl +++ b/src/ApproximateTNContraction/ApproximateTNContraction.jl @@ -10,11 +10,8 @@ const timer = TimerOutput() include("ITensors.jl") include("orthogonal_tensor.jl") -include("networks/lattices.jl") -include("networks/inds_network.jl") include("networks/itensor_network.jl") include("networks/3d_classical_ising.jl") -include("models/models.jl") include("interfaces/sweep_contractor.jl") include("contract/contract.jl") diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 5971a915..725f0a74 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -9,7 +9,6 @@ end include("tree_utils.jl") include("index_group.jl") -include("tensornetwork_graph.jl") include("mincut_tree.jl") include("tree_embedding.jl") diff --git a/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl b/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl new file mode 100644 index 00000000..8e91f471 --- /dev/null +++ b/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl @@ -0,0 +1,208 @@ + +# a large number to prevent this edge being a cut +MAX_WEIGHT = 1e32 + +function inds_binary_tree( + network::Vector{ITensor}, inds_groups::Vector{<:Vector}; kwargs... +) + tng = TensorNetworkGraph(network, vectorize(inds_groups)) + function get_sub_tree(inds) + @assert all(ind -> ind isa Index, inds) + if length(inds) == 1 + return inds + end + inds = [[i] for i in inds] + return inds_binary_tree!(tng, inds; kwargs...) + end + inds_groups = [get_sub_tree(inds) for inds in inds_groups] + if length(inds_groups) <= 2 + return inds_groups + end + return inds_binary_tree!(tng, inds_groups; kwargs...) +end + +function inds_binary_tree!(tng::TensorNetworkGraph, outinds::Vector; algorithm="mincut") + @assert algorithm in ["mincut", "mps"] + @assert all(ind -> ind in keys(tng.out_edge_dict), outinds) + if algorithm == "mincut" + return mincut_inds!(tng, outinds) + elseif algorithm == "mps" + return line_to_tree(inds_linear_order!(tng, outinds)) + end +end + +function inds_linear_order( + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}} +) + if outinds == nothing + outinds = noncommoninds(network...) + end + if length(outinds) == 1 + return outinds + end + tng = TensorNetworkGraph(network, outinds) + grouped_uncontracted_inds = [[i] for i in outinds] + return inds_linear_order!(tng, grouped_uncontracted_inds) +end + +function inds_linear_order!(tng::TensorNetworkGraph, outinds::Vector) + @assert length(outinds) >= 1 + # base case here, for the case length(outinds) == 2, we still need to do the update + if length(outinds) == 1 + return outinds[1] + end + if length(outinds) = 2 + return outinds + end + new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 1, 1))) + new_edge = new_edge[1] + # outinds = update!(tng, outinds, new_edge, minval) + # first_ind = new_edge + linear_order = [new_edge] + while length(outinds) > 2 + splitinds = [[new_edge, i] for i in outinds if i != new_edge] + new_edge, minval = new_edge_mincut(tng, splitinds) + outinds = update!(tng, outinds, new_edge, minval) + # first_ind = new_edge + push!(linear_order, new_edge[2]) + end + splitinds = [[new_edge, i] for i in outinds if i != new_edge] + @assert length(splitinds) == 1 + push!(linear_order, splitinds[1][2]) + return linear_order +end + +function inds_binary_tree( + network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" +) + if outinds == nothing + outinds = noncommoninds(network...) + end + if length(outinds) == 1 + return outinds + end + if algorithm == "sequential-mps" + out_inds = [outinds[1]] + for i in 2:length(outinds) + out_inds = [out_inds, [outinds[i]]] + end + return out_inds + end + tng = TensorNetworkGraph(network, outinds) + grouped_uncontracted_inds = [[i] for i in outinds] + return inds_binary_tree!(tng, grouped_uncontracted_inds; algorithm=algorithm) +end + +function mincut_subnetwork( + network::Vector{ITensor}, sourceinds::Vector, uncontract_inds::Vector +) + @timeit timer "mincut_subnetwork" begin + if length(sourceinds) == length(uncontract_inds) + return network + end + tng = TensorNetworkGraph(network) + grouped_sourceinds = [[ind] for ind in sourceinds] + part1, part2, mincut = mincut_value(tng, grouped_sourceinds) + @assert length(part1) > 1 + @assert length(part2) > 1 + return [network[i] for i in part1 if i <= length(network)] + end +end + +function mincut_inds!(tng::TensorNetworkGraph, outinds::Vector) + @assert length(outinds) >= 1 + # base case here, for the case length(outinds) == 2, we still need to do the update + if length(outinds) == 1 + return outinds[1] + end + new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) + outinds = update!(tng, outinds, new_edge, minval) + return mincut_inds!(tng, outinds) +end + +function mincut_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) + @timeit timer "mincut_inds" begin + tng = copy(tng) + uncontract_inds = copy(uncontract_inds) + return mincut_inds!(tng, uncontract_inds) + end +end + +# update the graph +function update!(tng::TensorNetworkGraph, uncontract_inds::Vector, new_edge::Vector, minval) + add_vertex!(tng.graph) + last_vertex = size(tng.graph)[1] + u1, w_u1 = tng.out_edge_dict[new_edge[1]] + u2, w_u2 = tng.out_edge_dict[new_edge[2]] + Graphs.add_edge!(tng.graph, u1, last_vertex) + Graphs.add_edge!(tng.graph, u2, last_vertex) + Graphs.add_edge!(tng.graph, last_vertex, u1) + Graphs.add_edge!(tng.graph, last_vertex, u2) + new_weights = zeros(last_vertex, last_vertex) + new_weights[1:(last_vertex - 1), 1:(last_vertex - 1)] = tng.weights + #if not setting to MAX_WEIGHT would affect later tree selections + new_weights[u1, last_vertex] = MAX_WEIGHT + new_weights[u2, last_vertex] = MAX_WEIGHT + new_weights[last_vertex, u1] = MAX_WEIGHT + new_weights[last_vertex, u2] = MAX_WEIGHT + # update the dict + tng.inner_edge_dict[new_edge[1]] = (u1, last_vertex, MAX_WEIGHT)#w_u1) + tng.inner_edge_dict[new_edge[2]] = (u2, last_vertex, MAX_WEIGHT)#w_u2) + delete!(tng.out_edge_dict, new_edge[1]) + delete!(tng.out_edge_dict, new_edge[2]) + tng.out_edge_dict[new_edge] = (last_vertex, minval) + # update uncontract_inds + uncontract_inds = setdiff(uncontract_inds, new_edge) + uncontract_inds = vcat([new_edge], uncontract_inds) + tng.weights = new_weights + return uncontract_inds +end + +# TODO: rewrite this function +function new_edge_mincut(tng::TensorNetworkGraph, split_inds_list::Vector) + mincuts = [mincut_value(tng, split_inds)[3] for split_inds in split_inds_list] + split_sizes = [ + sum([tng.out_edge_dict[ind][2] for ind in split_inds]) for split_inds in split_inds_list + ] + dists = [distance(tng, inds...) for inds in split_inds_list] + weights = [min(mincuts[i], split_sizes[i]) for i in 1:length(mincuts)] + indices_min = [i for i in 1:length(mincuts) if weights[i] == min(weights...)] + cuts_min = [mincuts[i] for i in indices_min] + indices_min = [i for i in indices_min if mincuts[i] == min(cuts_min...)] + dists_min = [dists[i] for i in indices_min] + _, index = findmin(dists_min) + i = indices_min[index] + minval = weights[i] + new_edge = split_inds_list[i] + return new_edge, minval +end + +function mincut_value(tng::TensorNetworkGraph, split_inds::Vector) + tng = copy(tng) + # add two vertices to the graph to model the s and t + add_vertices!(tng.graph, 2) + t = size(tng.graph)[1] + s = t - 1 + new_weights = zeros(t, t) + new_weights[1:(t - 2), 1:(t - 2)] = tng.weights + for ind in split_inds + u, _ = tng.out_edge_dict[ind] + Graphs.add_edge!(tng.graph, u, s) + Graphs.add_edge!(tng.graph, s, u) + new_weights[u, s] = MAX_WEIGHT + new_weights[s, u] = MAX_WEIGHT + end + terminal_inds = setdiff(noncommoninds(tng), split_inds) + for ind in terminal_inds + u, _ = tng.out_edge_dict[ind] + Graphs.add_edge!(tng.graph, u, t) + Graphs.add_edge!(tng.graph, t, u) + new_weights[u, t] = MAX_WEIGHT + new_weights[t, u] = MAX_WEIGHT + end + # this t and s sequence makes sure part1 is the largest subgraph yielding mincut + part2, part1, flow = GraphsFlows.mincut( + tng.graph, t, s, new_weights, EdmondsKarpAlgorithm() + ) + return part1, part2, flow +end diff --git a/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl b/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl new file mode 100644 index 00000000..9ea7d46a --- /dev/null +++ b/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl @@ -0,0 +1,142 @@ +mutable struct TensorNetworkGraph + network::Vector{ITensor} + graph::SimpleDiGraph + weights::Matrix + #a dict that maps uncontracted Index to the adjacent vertices pair (i,j) + out_edge_dict::Dict + inner_edge_dict::Dict +end + +function TensorNetworkGraph(network::Vector{ITensor}) + uncontract_inds = noncommoninds(network...) + return TensorNetworkGraph(network, uncontract_inds) +end + +function TensorNetworkGraph(network::Vector{ITensor}, uncontract_inds::Vector) + graph = Graphs.DiGraph(length(network)) + # construct contract_edges + contract_edges = [] + inner_edge_dict = Dict() + for (i, t) in enumerate(network) + for ind in setdiff(inds(t), uncontract_inds) + if !haskey(inner_edge_dict, [ind]) + inner_edge_dict[[ind]] = (i, log2(space(ind))) + else + @assert(length(inner_edge_dict[[ind]]) == 2) + inner_edge_dict[[ind]] = (inner_edge_dict[[ind]][1], i, inner_edge_dict[[ind]][2]) + push!(contract_edges, inner_edge_dict[[ind]]) + end + end + end + weights = zeros(length(network), length(network)) + for e in contract_edges + u, v, f = e + Graphs.add_edge!(graph, u, v) + Graphs.add_edge!(graph, v, u) + weights[u, v] = f + weights[v, u] = f + end + # construct out_edge_dict + out_edge_dict = Dict() + for (i, t) in enumerate(network) + ucinds = intersect(inds(t), uncontract_inds) + if length(ucinds) == 0 + continue + end + for ind in ucinds + out_edge_dict[[ind]] = (i, log2(space(ind))) + end + end + return TensorNetworkGraph(network, graph, weights, out_edge_dict, inner_edge_dict) +end + +Base.show(io::IO, tng::TensorNetworkGraph) = print(io, tng.out_edge_dict) + +function Base.copy(tng::TensorNetworkGraph) + return TensorNetworkGraph( + tng.network, + copy(tng.graph), + copy(tng.weights), + copy(tng.out_edge_dict), + copy(tng.inner_edge_dict), + ) +end + +function ITensors.noncommoninds(tng::TensorNetworkGraph) + return keys(tng.out_edge_dict) +end + +function distance(tng::TensorNetworkGraph, s, t) + sindex = tng.out_edge_dict[s][1] + ds = dijkstra_shortest_paths(tng.graph, sindex, tng.weights) + get_dist(edge) = ds.dists[tng.out_edge_dict[edge][1]] + return get_dist(t) +end + +#TODO: delete this +distance(tng::TensorNetworkGraph, s) = 0.0 + +#TODO: below are used for visualization +function insert_outedge_vertex!(tng::TensorNetworkGraph) + if length(tng.out_edge_dict) == 0 + return nothing + end + add_vertices!(tng.graph, 1) + t = size(tng.graph)[1] + new_weights = zeros(t, t) + new_weights[1:(t - 1), 1:(t - 1)] = tng.weights + for (inds, edge) in tng.out_edge_dict + u, wu = edge + Graphs.add_edge!(tng.graph, u, t) + Graphs.add_edge!(tng.graph, t, u) + new_weights[u, t] = wu + new_weights[t, u] = wu + tng.out_edge_dict[inds] = (u, t, wu) + end + return tng.weights = new_weights +end + +function indsname(inds::Vector) + if length(inds) == 1 + return string(inds[1].tags) + end + return "" +end + +function visualize(tng::TensorNetworkGraph) + tng = copy(tng) + insert_outedge_vertex!(tng::TensorNetworkGraph) + wg = SimpleWeightedGraph(tng.graph) + for e in edges(tng.graph) + add_edge!(wg, src(e), dst(e), tng.weights[src(e), dst(e)]) + end + edgelabel_dict = Dict{Tuple{Int,Int},String}() + edgecolor_dict = Dict() + for (inds, edge) in tng.inner_edge_dict + if indsname(inds) == "" + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :blue + else + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :black + end + end + for (inds, edge) in tng.out_edge_dict + edgelabel_dict[(edge[1], edge[2])] = + indsname(inds) * "w=" * string(round(edge[3]; digits=2)) + edgecolor_dict[(edge[1], edge[2])] = :red + end + return graphplot( + wg; + markersize=0.3, + # names=names, + edgelabel=edgelabel_dict, + curves=false, + edgecolor=edgecolor_dict, + linewidth=20, + fontsize=30, + size=(7000, 7000), + ) +end diff --git a/src/ApproximateTNContraction/models/hamiltonians.jl b/src/ApproximateTNContraction/models/hamiltonians.jl deleted file mode 100644 index ff132677..00000000 --- a/src/ApproximateTNContraction/models/hamiltonians.jl +++ /dev/null @@ -1,144 +0,0 @@ -using ITensors -include("../networks/lattices.jl") - -struct LocalMPO - mpo::MPO - coord1::Tuple{<:Integer,<:Integer} - coord2::Tuple{<:Integer,<:Integer} -end - -struct LineMPO - mpo::MPO - coord::Union{Tuple{Colon,<:Integer},Tuple{<:Integer,Colon}} -end - -# Transverse field -# The critical point is h = 1.0 -# This is the most challenging part of the model for DMRG -function mpo(::Model"tfim", sites::Matrix{<:Index}; h::Float64) - Ny, Nx = size(sites) - sites_vec = reshape(sites, Nx * Ny) - lattice = square_lattice(Nx, Ny; yperiodic=false) - - opsum = OpSum() - for b in lattice - opsum += -1, "X", b.s1, "X", b.s2 - end - for i in 1:(Nx * Ny) - opsum += h, "Z", i - end - return MPO(opsum, sites_vec) -end - -function localham_term( - ::Model"tfim", - sites::Matrix{<:Index}, - bond::Tuple{Tuple{<:Integer,<:Integer},Tuple{<:Integer,<:Integer}}; - h::Float64, -) - Ny, Nx = size(sites) - coord1, coord2 = bond - opsum = OpSum() - opsum += -1, "X", 1, "X", 2 - if coord2[1] == coord1[1] + 1 - opsum += h, "Z", 1 - end - if coord2[1] == coord1[1] + 1 && coord2[1] == Ny - opsum += h, "Z", 2 - end - mpo = MPO(opsum, [sites[coord1...], sites[coord2...]]) - return LocalMPO(mpo, coord1, coord2) -end - -function localham(m::Model, sites; kwargs...) - Ny, Nx = size(sites) - lattice = Square((Ny, Nx)) - bds = bonds(lattice; periodic=false) - return [localham_term(m, sites, bond; kwargs...) for bond in bds] -end - -function localham_term( - ::Model"tfim", sites::Matrix{<:Index}, bond::Tuple{Colon,<:Integer}; h::Float64 -) - Ny, Nx = size(sites) - opsum = OpSum() - for i in 1:(Ny - 1) - opsum += -1, "X", i, "X", i + 1 - opsum += h, "Z", i - end - opsum += h, "Z", Ny - return LineMPO(MPO(opsum, sites[bond...]), bond) -end - -function localham_term( - ::Model"tfim", sites::Matrix{<:Index}, bond::Tuple{<:Integer,Colon}; h::Float64 -) - Ny, Nx = size(sites) - opsum = OpSum() - for i in 1:(Nx - 1) - opsum += -1, "X", i, "X", i + 1 - end - return LineMPO(MPO(opsum, sites[bond...]), bond) -end - -function lineham(m::Model, sites; kwargs...) - Ny, Nx = size(sites) - lattice = Square((Ny, Nx)) - bonds_row = [(i, :) for i in 1:Ny] - bonds_column = [(:, i) for i in 1:Nx] - bds = vcat(bonds_row, bonds_column) - return [localham_term(m, sites, bond; kwargs...) for bond in bds] -end - -# Check that the local Hamiltonian is the same as the MPO -function checkham(Hlocal::Array{LocalMPO}, H, sites) - @disable_warn_order begin - Ny, Nx = size(sites) - lattice = Square((Ny, Nx)) - bds = bonds(lattice; periodic=false) - Hlocal_full = ITensor() - for (i, bond) in enumerate(bds) - Hlocalterm_full = prod(Hlocal[i].mpo) - for y in 1:Ny - for x in 1:Nx - if !((y, x) in bond) - Hlocalterm_full *= op("Id", vec(sites), (x - 1) * Ny + y) - end - end - end - Hlocal_full += Hlocalterm_full - end - @show norm(Hlocal_full - prod(H)) - end - return isapprox(norm(Hlocal_full), norm(prod(H))) -end - -function checkham(Hline::Array{LineMPO}, H, sites) - @disable_warn_order begin - Ny, Nx = size(sites) - Hlocal_full = ITensor() - for h in Hline - h_full = prod(h.mpo) - if h.coord[1] isa Colon - for y in 1:Ny - for x in 1:Nx - if x != h.coord[2] - h_full *= op("Id", vec(sites), (x - 1) * Ny + y) - end - end - end - else - for y in 1:Ny - for x in 1:Nx - if y != h.coord[1] - h_full *= op("Id", vec(sites), (x - 1) * Ny + y) - end - end - end - end - Hlocal_full += h_full - end - @show norm(Hlocal_full - prod(H)) - end - return isapprox(norm(Hlocal_full), norm(prod(H))) -end diff --git a/src/ApproximateTNContraction/models/ising_classical_2d.jl b/src/ApproximateTNContraction/models/ising_classical_2d.jl deleted file mode 100644 index dd6b9be2..00000000 --- a/src/ApproximateTNContraction/models/ising_classical_2d.jl +++ /dev/null @@ -1,95 +0,0 @@ -using ITensors - -function local_boltzmann_weight(m::String, args...; kwargs...) - return local_boltzmann_weight(Model(m), args...; kwargs...) -end - -f(λ₊, λ₋) = [ - (λ₊ + λ₋)/2 (λ₊ - λ₋)/2 - (λ₊ - λ₋)/2 (λ₊ + λ₋)/2 -] - -function sqrt_bond_matrix(; β::Real, J::Real=1.0) - # Alternative method - #Q = [exp(β * J) exp(-β * J); exp(-β * J) exp(β * J)] - #return √Q - λ₊ = √(exp(β * J) + exp(-β * J)) - λ₋ = √(exp(β * J) - exp(-β * J)) - return f(λ₊, λ₋) -end - -# The local Boltzmann weight for the Ising -# model in 1 dimension -function local_boltzmann_weight( - ::Model"ising", ::Val{1}; β::Real, J::Real=1.0, sz::Bool=false -) - d = 2 # local dimension of the Ising local Boltzmann factor - s, s′ = Index.((d, d)) - T = ITensor(s, s′) - for i in 1:d - T[i, i] = 1.0 - end - if sz - T[1, 1] = -T[1, 1] - end - s̃, s̃′ = sim.((s, s′)) - T̃ = T * δ(s, s̃) * δ(s′, s̃′) - sqrtQ = sqrt_bond_matrix(; β=β, J=J) - @show sqrtQ - X = itensor(vec(sqrtQ), s̃, s) - X′ = itensor(vec(sqrtQ), s̃′, s′) - return array(permute(T̃ * X′ * X, s, s′)) -end - -# The local Boltzmann weight for the Ising -# model in 2 dimensions -function local_boltzmann_weight( - ::Model"ising", ::Val{2}; β::Real, J::Real=1.0, sz::Bool=false -) - d = 2 # local dimension of the Ising local Boltzmann factor - sₕ, sₕ′ = Index.((d, d)) - sᵥ, sᵥ′ = Index.((d, d)) - @assert dim(sₕ) == dim(sᵥ) - d = dim(sₕ) - T = ITensor(sₕ, sₕ′, sᵥ, sᵥ′) - for i in 1:d - T[i, i, i, i] = 1.0 - end - if sz - T[1, 1, 1, 1] = -T[1, 1, 1, 1] - end - s̃ₕ, s̃ₕ′, s̃ᵥ, s̃ᵥ′ = sim.((sₕ, sₕ′, sᵥ, sᵥ′)) - T̃ = T * δ(sₕ, s̃ₕ) * δ(sₕ′, s̃ₕ′) * δ(sᵥ, s̃ᵥ) * δ(sᵥ′, s̃ᵥ′) - X = sqrt_bond_matrix(; β=β, J=J) - Xₕ = itensor(vec(X), s̃ₕ, sₕ) - Xₕ′ = itensor(vec(X), s̃ₕ′, sₕ′) - Xᵥ = itensor(vec(X), s̃ᵥ, sᵥ) - Xᵥ′ = itensor(vec(X), s̃ᵥ′, sᵥ′) - return array(permute(T̃ * Xₕ′ * Xᵥ′ * Xₕ * Xᵥ, sₕ, sₕ′, sᵥ, sᵥ′)) -end - -function mpo_itensor( - m::Model, pair_sₕ::Pair{<:Index,<:Index}, pair_sᵥ::Pair{<:Index,<:Index}; kwargs... -) - sₕ, sₕ′ = pair_sₕ - sᵥ, sᵥ′ = pair_sᵥ - return itensor(mpo_array(m; kwargs...), sₕ, sₕ′, sᵥ, sᵥ′) -end - -critical_point(::Model"ising") = 0.5 * log(√2 + 1) - -function free_energy(::Model"ising"; β::Real, J::Real=1.0) - k = β * J - c = cosh(2 * k) - s = sinh(2 * k) - xmin = 0.0 - xmax = π - integrand(x) = log(c^2 + √(s^4 + 1 - 2 * s^2 * cos(x))) - integral, err = quadgk(integrand, xmin, xmax)::Tuple{Float64,Float64} - return -(log(2) + integral / π) / (2 * β) -end - -function magnetization(::Model"ising"; β::Real) - β > βc && return (1 - sinh(2 * β)^(-4))^(1 / 8) - return 0.0 -end diff --git a/src/ApproximateTNContraction/models/models.jl b/src/ApproximateTNContraction/models/models.jl deleted file mode 100644 index 241d5792..00000000 --- a/src/ApproximateTNContraction/models/models.jl +++ /dev/null @@ -1,19 +0,0 @@ -module Models - -export Model, critical_point, local_boltzmann_weight, mpo, localham, checklocalham - -struct Model{model} end - -Model(s::AbstractString) = Model{Symbol(s)}() - -# For notation: -# Model"tfim" == Model{:tfim} -# Model"heisenberg" == Model{:heisenberg} -macro Model_str(s) - return :(Model{$(Expr(:quote, Symbol(s)))}) -end - -include("ising_classical_2d.jl") -include("hamiltonians.jl") - -end diff --git a/src/ApproximateTNContraction/networks/3d_classical_ising.jl b/src/ApproximateTNContraction/networks/3d_classical_ising.jl index 30df3767..7dbccc41 100644 --- a/src/ApproximateTNContraction/networks/3d_classical_ising.jl +++ b/src/ApproximateTNContraction/networks/3d_classical_ising.jl @@ -42,6 +42,9 @@ function isingTensor(inds::Vector) end function ising_partition(N, d=2) - tn_inds = inds_network(N...; linkdims=d, periodic=false) - return map(inds -> isingTensor(inds), tn_inds) + tn = ITensorNetwork(named_grid(N); link_space=d) + for v in vertices(tn) + tn[v] = isingTensor(inds(tn[v])) + end + return Vector{ITensor}(tn) end diff --git a/src/ApproximateTNContraction/networks/inds_network.jl b/src/ApproximateTNContraction/networks/inds_network.jl deleted file mode 100644 index fac65e91..00000000 --- a/src/ApproximateTNContraction/networks/inds_network.jl +++ /dev/null @@ -1,68 +0,0 @@ - -function coordinate_tag(n) - str = replace("$n", ")" => "") - str = replace(str, "(" => "") - str = replace(str, " " => "") - if length(n) > 1 - str = replace(str, "," => ".") - else - str = replace(str, "," => "") - end - return str -end - -function link_tag(n1, n2) - link_string = "$(coordinate_tag(n1))↔$(coordinate_tag(n2))" - start_ind = nextind(link_string, 0, 1) - stop_ind = min(ncodeunits(link_string), nextind(link_string, 0, 16)) - link_string = link_string[start_ind:stop_ind] - return TagSet(link_string) -end - -function ITensors.linkinds(lattice::HyperCubic; linkdims, addtags=ts"") - dims = size(lattice) - N = length(dims) - linkinds_dict = Dict{Edge{N},Index{typeof(linkdims)}}() - for n in sites(lattice), edge_n in incident_edges(lattice, n) - l = Index(linkdims; tags=ITensors.addtags(link_tag(edge_n.edge...), addtags)) - get!(linkinds_dict, edge_n, l) - end - return linkinds_dict -end - -function get_link_ind(linkinds_dict::Dict, edge::Edge, site::Tuple) - l = linkinds_dict[edge] - return is_in_edge(site, edge) ? dag(l) : l -end - -# A network of link indices for a HyperCubic lattice, with -# no site indices. -function inds_network(dims::Int...; linkdims, kwargs...) - site_inds = fill(Index{typeof(linkdims)}[], dims) - return inds_network(site_inds; linkdims=linkdims, kwargs...) -end - -function inds_network(site_inds::Array{<:Index,N}; kwargs...) where {N} - return inds_network(map(x -> [x], site_inds); kwargs...) -end - -# A network of link indices for a HyperCubic lattice, with -# site indices specified. -function inds_network( - site_inds::Array{<:Vector{<:Index},N}; linkdims, addtags=ts"", periodic=true -) where {N} - dims = size(site_inds) - lattice = HyperCubic(dims) - linkinds_dict = linkinds(lattice; linkdims=linkdims, addtags=addtags) - inds = Array{Vector{Index{typeof(linkdims)}},N}(undef, dims) - for n in sites(lattice) - if periodic == true - edges = incident_edges(lattice, n) - else - edges = [e for e in incident_edges(lattice, n) if e.boundary == false] - end - inds_n = [get_link_ind(linkinds_dict, edge_n, n) for edge_n in edges] - inds[n...] = append!(inds_n, site_inds[n...]) - end - return inds -end diff --git a/src/ApproximateTNContraction/networks/lattices.jl b/src/ApproximateTNContraction/networks/lattices.jl deleted file mode 100644 index 9bbf2bda..00000000 --- a/src/ApproximateTNContraction/networks/lattices.jl +++ /dev/null @@ -1,125 +0,0 @@ -# -# HyperCubic lattice -# - -# An N-dimensional hypercubic lattice with periodic -# boundary conditions -struct HyperCubic{N} - dims::NTuple{N,Int} -end -Base.size(l::HyperCubic) = l.dims - -const Chain = HyperCubic{1} -const Square = HyperCubic{2} -const Cubic = HyperCubic{3} - -struct Edge{N} - edge::Tuple{NTuple{N,Int},NTuple{N,Int}} - boundary::Bool -end - -Base.getindex(edge::Edge, n::Int) = edge.edge[n] - -Base.reverse(edge::Edge) = Edge(reverse(edge.edge), edge.boundary) - -sites(lattice::HyperCubic) = (Tuple(s) for s in CartesianIndices(axes(lattice))) - -function is_in_edge(site::Tuple, edge::Edge) - if site == edge[1] - return false - elseif site == edge[2] - return true - else - error("Site $site is not incident to edge $edge") - end -end - -function onehot_tuple(n::Integer, length::Val{N}) where {N} - return ntuple(i -> i == n ? 1 : 0, Val(N)) -end - -# Obtain the neighbor in dimension `dim` in direction `dir`, for example: -# neighbor((2, 2), 2, 1) == (2, 3) -# neighbor((2, 2), 2, -1) == (2, 1) -# neighbor((2, 2), 1, -1) == (1, 2) -# neighbor((2, 3), 2, 1, lattice_size=(3, 3)) == (2, 1) -function neighbor( - site::NTuple{N,Int}, dim::Int, dir::Int; lattice_size=typemax(eltype(site)) -) where {N} - return map( - (a, b, d) -> mod1(a + dir * b, d), site, onehot_tuple(dim, Val(N)), lattice_size - ) -end - -# Check if the edge connecting to the specified neighbor of the site -# crosses the boundary of the lattice -function isboundary( - site::NTuple{N,Int}, dim::Int, dir::Int; lattice_size=typemax(eltype(site)) -) where {N} - return ((dir == 1) && (site[dim] == lattice_size[dim])) || ((dir == -1) && site[dim] == 1) -end - -# The neighboring sites of the specified site -function filterneighbors( - f, lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false -) 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) - bc_condition = periodic || !(isboundary(site, dim, dir; lattice_size=lattice_size)) - if f(site, site_neighbor) && bc_condition - push!(site_neighbors, site_neighbor) - end - end - return site_neighbors -end - -function neighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} - return filterneighbors(≠, lattice, site; periodic=periodic) -end - -function inneighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} - return filterneighbors(>, lattice, site; periodic=periodic) -end - -function outneighbors(lattice::HyperCubic{N}, site::NTuple{N,Int}; periodic=false) where {N} - return filterneighbors(<, lattice, site; periodic=periodic) -end - -# All of the edges connected to the vertex `site` -function incident_edges(lattice::HyperCubic{N}, site::NTuple{N,Int}) where {N} - lattice_size = size(lattice) - site_edges = Vector{Edge{N}}() - for dim in 1:N, dir in (-1, 1) - site_neighbor = neighbor(site, dim, dir; lattice_size=lattice_size) - boundary = false - if isboundary(site, dim, dir; lattice_size=lattice_size) - boundary = true - end - edge = (site, site_neighbor) - if dir == -1 - edge = reverse(edge) - end - push!(site_edges, Edge(edge, boundary)) - end - return site_edges -end - -function bonds(lattice::HyperCubic; periodic=false) - return [ - (s, n) for s in sites(lattice) for n in outneighbors(lattice, s; periodic=periodic) - ] -end - -function bonds(lattice::Square, coord::Tuple{Colon,<:Integer}) - rowsize = lattice.dims[1] - colsites = [(i, coord[2]) for i in 1:(rowsize - 1)] - return [(s, (s[1] + 1, s[2])) for s in colsites] -end - -function bonds(lattice::Square, coord::Tuple{<:Integer,Colon}) - colsize = lattice.dims[2] - rowsites = [(coord[1], i) for i in 1:(colsize - 1)] - return [(s, (s[1], s[2] + 1)) for s in rowsites] -end diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 69325205..4d3affe9 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -6,7 +6,7 @@ using ITensorNetworks.ApproximateTNContraction: inds_binary_tree, tree_embedding, approximate_contract -using ITensorNetworks.ApproximateTNContraction: timer, inds_network, Models, ising_partition +using ITensorNetworks.ApproximateTNContraction: timer, ising_partition include("utils.jl") @@ -56,8 +56,11 @@ end N = (5, 3) linkdim = 3 cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) + network = randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=linkdim) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end x, A = tn[:, 1], tn[:, 2] out_true = contract(MPO(A), MPS(x); cutoff=cutoff, maxdim=linkdim * linkdim) out2, log_norm = approximate_contract([A, x]; cutoff=cutoff, maxdim=linkdim * linkdim) @@ -100,8 +103,11 @@ end @testset "test inds_binary_tree of a 2D network" begin N = (3, 3, 3) linkdim = 2 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) + network = randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=linkdim) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end network = vec(tn[:, :, 1]) out = inds_binary_tree(network, noncommoninds(network...); algorithm="mincut") @test length(out) == 2 @@ -139,8 +145,11 @@ end N = (8, 8) linkdim = 2 cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - tn = map(inds -> randomITensor(inds...), tn_inds) + network = randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=linkdim) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end # tn = ising_partition(N, linkdim) ITensors.set_warn_order(100) @@ -163,16 +172,17 @@ end N = (8, 8) linkdim = 10 cutoff = 1e-15 - tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - + network = randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=linkdim) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end dim = 20 # warmup - tn = map(inds -> randomITensor(inds...), tn_inds) ITensors.set_warn_order(100) benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim) reset_timer!(timer) - tn = map(inds -> randomITensor(inds...), tn_inds) ITensors.set_warn_order(100) benchmark_peps_contraction(tn; cutoff=cutoff, maxdim=dim) show(timer) diff --git a/test/approximate_tn_contraction/indexgroup.jl b/test/approximate_tn_contraction/indexgroup.jl index 24480583..d4648566 100644 --- a/test/approximate_tn_contraction/indexgroup.jl +++ b/test/approximate_tn_contraction/indexgroup.jl @@ -1,8 +1,8 @@ using ITensors +using ITensorNetworks using ITensorNetworks.ApproximateTNContraction: IndexGroup, get_index_groups, get_leaves, neighbor_index_groups using ITensorNetworks.ApproximateTNContraction: - inds_network, line_network, IndexAdjacencyTree, topo_sort, @@ -14,8 +14,7 @@ using ITensorNetworks.ApproximateTNContraction: @testset "test generate_adjacency_tree" begin N = (3, 3) - tn_inds = inds_network(N...; linkdims=2, periodic=false) - tn = vec(map(inds -> randomITensor(inds...), tn_inds)) + tn = Vector{ITensor}(randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=2)) ctree = line_network(tn) tn_leaves = get_leaves(ctree) ctrees = topo_sort(ctree; leaves=tn_leaves) @@ -71,8 +70,7 @@ end @testset "test approximate_contract" begin N = (4, 4) - tn_inds = inds_network(N...; linkdims=2, periodic=false) - tn = vec(map(inds -> randomITensor(inds...), tn_inds)) + tn = Vector{ITensor}(randomITensorNetwork(IndsNetwork(named_grid(N)); link_space=2)) ctree = line_network(tn) approximate_contract(ctree; cutoff=1e-5, maxdim=20, ansatz="mps") approximate_contract(ctree; cutoff=1e-5, maxdim=20, ansatz="comb") diff --git a/test/approximate_tn_contraction/lattice.jl b/test/approximate_tn_contraction/lattice.jl deleted file mode 100644 index a4d696be..00000000 --- a/test/approximate_tn_contraction/lattice.jl +++ /dev/null @@ -1,9 +0,0 @@ -using ITensors -using ITensorNetworks -using ITensorNetworks.ApproximateTNContraction: Square, bonds - -@testset "test lattice" begin - lattice = Square((2, 3)) - bds = bonds(lattice; periodic=false) - @test length(bds) == 7 -end diff --git a/test/approximate_tn_contraction/models.jl b/test/approximate_tn_contraction/models.jl deleted file mode 100644 index 34f583f2..00000000 --- a/test/approximate_tn_contraction/models.jl +++ /dev/null @@ -1,14 +0,0 @@ -using ITensors -using ITensorNetworks -using ITensorNetworks.ApproximateTNContraction: Models - -@testset "test local hamiltonian builder" begin - Nx = 2 - Ny = 3 - sites = siteinds("S=1/2", Ny, Nx) - H = Models.mpo(Models.Model("tfim"), sites; h=1.0) - H_local = Models.localham(Models.Model("tfim"), sites; h=1.0) - H_line = Models.lineham(Models.Model("tfim"), sites; h=1.0) - @test Models.checkham(H_local, H, sites) - @test Models.checkham(H_line, H, sites) -end diff --git a/test/approximate_tn_contraction/runtests.jl b/test/approximate_tn_contraction/runtests.jl index 4a782168..21464a3c 100644 --- a/test/approximate_tn_contraction/runtests.jl +++ b/test/approximate_tn_contraction/runtests.jl @@ -2,15 +2,7 @@ using ITensorNetworks using Test @testset "ApproximateTNContraction.jl" begin - for filename in [ - "lattice.jl", - "models.jl", - "tree.jl", - "indexgroup.jl", - "cache.jl", - "contract.jl", - "interface.jl", - ] + for filename in ["tree.jl", "indexgroup.jl", "cache.jl", "contract.jl", "interface.jl"] println("Running $filename in ApproximateTNContraction.jl") include(filename) end From feb5b3d00a8a58253d5b21bf7c61f8b587a75032 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 21:46:52 -0600 Subject: [PATCH 15/24] [ApproxContraction] Remove old files --- .../contract/old_files/mincut_tree__.jl | 208 ------------------ .../contract/old_files/tensornetwork_graph.jl | 142 ------------ 2 files changed, 350 deletions(-) delete mode 100644 src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl delete mode 100644 src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl diff --git a/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl b/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl deleted file mode 100644 index 8e91f471..00000000 --- a/src/ApproximateTNContraction/contract/old_files/mincut_tree__.jl +++ /dev/null @@ -1,208 +0,0 @@ - -# a large number to prevent this edge being a cut -MAX_WEIGHT = 1e32 - -function inds_binary_tree( - network::Vector{ITensor}, inds_groups::Vector{<:Vector}; kwargs... -) - tng = TensorNetworkGraph(network, vectorize(inds_groups)) - function get_sub_tree(inds) - @assert all(ind -> ind isa Index, inds) - if length(inds) == 1 - return inds - end - inds = [[i] for i in inds] - return inds_binary_tree!(tng, inds; kwargs...) - end - inds_groups = [get_sub_tree(inds) for inds in inds_groups] - if length(inds_groups) <= 2 - return inds_groups - end - return inds_binary_tree!(tng, inds_groups; kwargs...) -end - -function inds_binary_tree!(tng::TensorNetworkGraph, outinds::Vector; algorithm="mincut") - @assert algorithm in ["mincut", "mps"] - @assert all(ind -> ind in keys(tng.out_edge_dict), outinds) - if algorithm == "mincut" - return mincut_inds!(tng, outinds) - elseif algorithm == "mps" - return line_to_tree(inds_linear_order!(tng, outinds)) - end -end - -function inds_linear_order( - network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}} -) - if outinds == nothing - outinds = noncommoninds(network...) - end - if length(outinds) == 1 - return outinds - end - tng = TensorNetworkGraph(network, outinds) - grouped_uncontracted_inds = [[i] for i in outinds] - return inds_linear_order!(tng, grouped_uncontracted_inds) -end - -function inds_linear_order!(tng::TensorNetworkGraph, outinds::Vector) - @assert length(outinds) >= 1 - # base case here, for the case length(outinds) == 2, we still need to do the update - if length(outinds) == 1 - return outinds[1] - end - if length(outinds) = 2 - return outinds - end - new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 1, 1))) - new_edge = new_edge[1] - # outinds = update!(tng, outinds, new_edge, minval) - # first_ind = new_edge - linear_order = [new_edge] - while length(outinds) > 2 - splitinds = [[new_edge, i] for i in outinds if i != new_edge] - new_edge, minval = new_edge_mincut(tng, splitinds) - outinds = update!(tng, outinds, new_edge, minval) - # first_ind = new_edge - push!(linear_order, new_edge[2]) - end - splitinds = [[new_edge, i] for i in outinds if i != new_edge] - @assert length(splitinds) == 1 - push!(linear_order, splitinds[1][2]) - return linear_order -end - -function inds_binary_tree( - network::Vector{ITensor}, outinds::Union{Nothing,Vector{<:Index}}; algorithm="mincut" -) - if outinds == nothing - outinds = noncommoninds(network...) - end - if length(outinds) == 1 - return outinds - end - if algorithm == "sequential-mps" - out_inds = [outinds[1]] - for i in 2:length(outinds) - out_inds = [out_inds, [outinds[i]]] - end - return out_inds - end - tng = TensorNetworkGraph(network, outinds) - grouped_uncontracted_inds = [[i] for i in outinds] - return inds_binary_tree!(tng, grouped_uncontracted_inds; algorithm=algorithm) -end - -function mincut_subnetwork( - network::Vector{ITensor}, sourceinds::Vector, uncontract_inds::Vector -) - @timeit timer "mincut_subnetwork" begin - if length(sourceinds) == length(uncontract_inds) - return network - end - tng = TensorNetworkGraph(network) - grouped_sourceinds = [[ind] for ind in sourceinds] - part1, part2, mincut = mincut_value(tng, grouped_sourceinds) - @assert length(part1) > 1 - @assert length(part2) > 1 - return [network[i] for i in part1 if i <= length(network)] - end -end - -function mincut_inds!(tng::TensorNetworkGraph, outinds::Vector) - @assert length(outinds) >= 1 - # base case here, for the case length(outinds) == 2, we still need to do the update - if length(outinds) == 1 - return outinds[1] - end - new_edge, minval = new_edge_mincut(tng, collect(powerset(outinds, 2, 2))) - outinds = update!(tng, outinds, new_edge, minval) - return mincut_inds!(tng, outinds) -end - -function mincut_inds(tng::TensorNetworkGraph, uncontract_inds::Vector) - @timeit timer "mincut_inds" begin - tng = copy(tng) - uncontract_inds = copy(uncontract_inds) - return mincut_inds!(tng, uncontract_inds) - end -end - -# update the graph -function update!(tng::TensorNetworkGraph, uncontract_inds::Vector, new_edge::Vector, minval) - add_vertex!(tng.graph) - last_vertex = size(tng.graph)[1] - u1, w_u1 = tng.out_edge_dict[new_edge[1]] - u2, w_u2 = tng.out_edge_dict[new_edge[2]] - Graphs.add_edge!(tng.graph, u1, last_vertex) - Graphs.add_edge!(tng.graph, u2, last_vertex) - Graphs.add_edge!(tng.graph, last_vertex, u1) - Graphs.add_edge!(tng.graph, last_vertex, u2) - new_weights = zeros(last_vertex, last_vertex) - new_weights[1:(last_vertex - 1), 1:(last_vertex - 1)] = tng.weights - #if not setting to MAX_WEIGHT would affect later tree selections - new_weights[u1, last_vertex] = MAX_WEIGHT - new_weights[u2, last_vertex] = MAX_WEIGHT - new_weights[last_vertex, u1] = MAX_WEIGHT - new_weights[last_vertex, u2] = MAX_WEIGHT - # update the dict - tng.inner_edge_dict[new_edge[1]] = (u1, last_vertex, MAX_WEIGHT)#w_u1) - tng.inner_edge_dict[new_edge[2]] = (u2, last_vertex, MAX_WEIGHT)#w_u2) - delete!(tng.out_edge_dict, new_edge[1]) - delete!(tng.out_edge_dict, new_edge[2]) - tng.out_edge_dict[new_edge] = (last_vertex, minval) - # update uncontract_inds - uncontract_inds = setdiff(uncontract_inds, new_edge) - uncontract_inds = vcat([new_edge], uncontract_inds) - tng.weights = new_weights - return uncontract_inds -end - -# TODO: rewrite this function -function new_edge_mincut(tng::TensorNetworkGraph, split_inds_list::Vector) - mincuts = [mincut_value(tng, split_inds)[3] for split_inds in split_inds_list] - split_sizes = [ - sum([tng.out_edge_dict[ind][2] for ind in split_inds]) for split_inds in split_inds_list - ] - dists = [distance(tng, inds...) for inds in split_inds_list] - weights = [min(mincuts[i], split_sizes[i]) for i in 1:length(mincuts)] - indices_min = [i for i in 1:length(mincuts) if weights[i] == min(weights...)] - cuts_min = [mincuts[i] for i in indices_min] - indices_min = [i for i in indices_min if mincuts[i] == min(cuts_min...)] - dists_min = [dists[i] for i in indices_min] - _, index = findmin(dists_min) - i = indices_min[index] - minval = weights[i] - new_edge = split_inds_list[i] - return new_edge, minval -end - -function mincut_value(tng::TensorNetworkGraph, split_inds::Vector) - tng = copy(tng) - # add two vertices to the graph to model the s and t - add_vertices!(tng.graph, 2) - t = size(tng.graph)[1] - s = t - 1 - new_weights = zeros(t, t) - new_weights[1:(t - 2), 1:(t - 2)] = tng.weights - for ind in split_inds - u, _ = tng.out_edge_dict[ind] - Graphs.add_edge!(tng.graph, u, s) - Graphs.add_edge!(tng.graph, s, u) - new_weights[u, s] = MAX_WEIGHT - new_weights[s, u] = MAX_WEIGHT - end - terminal_inds = setdiff(noncommoninds(tng), split_inds) - for ind in terminal_inds - u, _ = tng.out_edge_dict[ind] - Graphs.add_edge!(tng.graph, u, t) - Graphs.add_edge!(tng.graph, t, u) - new_weights[u, t] = MAX_WEIGHT - new_weights[t, u] = MAX_WEIGHT - end - # this t and s sequence makes sure part1 is the largest subgraph yielding mincut - part2, part1, flow = GraphsFlows.mincut( - tng.graph, t, s, new_weights, EdmondsKarpAlgorithm() - ) - return part1, part2, flow -end diff --git a/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl b/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl deleted file mode 100644 index 9ea7d46a..00000000 --- a/src/ApproximateTNContraction/contract/old_files/tensornetwork_graph.jl +++ /dev/null @@ -1,142 +0,0 @@ -mutable struct TensorNetworkGraph - network::Vector{ITensor} - graph::SimpleDiGraph - weights::Matrix - #a dict that maps uncontracted Index to the adjacent vertices pair (i,j) - out_edge_dict::Dict - inner_edge_dict::Dict -end - -function TensorNetworkGraph(network::Vector{ITensor}) - uncontract_inds = noncommoninds(network...) - return TensorNetworkGraph(network, uncontract_inds) -end - -function TensorNetworkGraph(network::Vector{ITensor}, uncontract_inds::Vector) - graph = Graphs.DiGraph(length(network)) - # construct contract_edges - contract_edges = [] - inner_edge_dict = Dict() - for (i, t) in enumerate(network) - for ind in setdiff(inds(t), uncontract_inds) - if !haskey(inner_edge_dict, [ind]) - inner_edge_dict[[ind]] = (i, log2(space(ind))) - else - @assert(length(inner_edge_dict[[ind]]) == 2) - inner_edge_dict[[ind]] = (inner_edge_dict[[ind]][1], i, inner_edge_dict[[ind]][2]) - push!(contract_edges, inner_edge_dict[[ind]]) - end - end - end - weights = zeros(length(network), length(network)) - for e in contract_edges - u, v, f = e - Graphs.add_edge!(graph, u, v) - Graphs.add_edge!(graph, v, u) - weights[u, v] = f - weights[v, u] = f - end - # construct out_edge_dict - out_edge_dict = Dict() - for (i, t) in enumerate(network) - ucinds = intersect(inds(t), uncontract_inds) - if length(ucinds) == 0 - continue - end - for ind in ucinds - out_edge_dict[[ind]] = (i, log2(space(ind))) - end - end - return TensorNetworkGraph(network, graph, weights, out_edge_dict, inner_edge_dict) -end - -Base.show(io::IO, tng::TensorNetworkGraph) = print(io, tng.out_edge_dict) - -function Base.copy(tng::TensorNetworkGraph) - return TensorNetworkGraph( - tng.network, - copy(tng.graph), - copy(tng.weights), - copy(tng.out_edge_dict), - copy(tng.inner_edge_dict), - ) -end - -function ITensors.noncommoninds(tng::TensorNetworkGraph) - return keys(tng.out_edge_dict) -end - -function distance(tng::TensorNetworkGraph, s, t) - sindex = tng.out_edge_dict[s][1] - ds = dijkstra_shortest_paths(tng.graph, sindex, tng.weights) - get_dist(edge) = ds.dists[tng.out_edge_dict[edge][1]] - return get_dist(t) -end - -#TODO: delete this -distance(tng::TensorNetworkGraph, s) = 0.0 - -#TODO: below are used for visualization -function insert_outedge_vertex!(tng::TensorNetworkGraph) - if length(tng.out_edge_dict) == 0 - return nothing - end - add_vertices!(tng.graph, 1) - t = size(tng.graph)[1] - new_weights = zeros(t, t) - new_weights[1:(t - 1), 1:(t - 1)] = tng.weights - for (inds, edge) in tng.out_edge_dict - u, wu = edge - Graphs.add_edge!(tng.graph, u, t) - Graphs.add_edge!(tng.graph, t, u) - new_weights[u, t] = wu - new_weights[t, u] = wu - tng.out_edge_dict[inds] = (u, t, wu) - end - return tng.weights = new_weights -end - -function indsname(inds::Vector) - if length(inds) == 1 - return string(inds[1].tags) - end - return "" -end - -function visualize(tng::TensorNetworkGraph) - tng = copy(tng) - insert_outedge_vertex!(tng::TensorNetworkGraph) - wg = SimpleWeightedGraph(tng.graph) - for e in edges(tng.graph) - add_edge!(wg, src(e), dst(e), tng.weights[src(e), dst(e)]) - end - edgelabel_dict = Dict{Tuple{Int,Int},String}() - edgecolor_dict = Dict() - for (inds, edge) in tng.inner_edge_dict - if indsname(inds) == "" - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :blue - else - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :black - end - end - for (inds, edge) in tng.out_edge_dict - edgelabel_dict[(edge[1], edge[2])] = - indsname(inds) * "w=" * string(round(edge[3]; digits=2)) - edgecolor_dict[(edge[1], edge[2])] = :red - end - return graphplot( - wg; - markersize=0.3, - # names=names, - edgelabel=edgelabel_dict, - curves=false, - edgecolor=edgecolor_dict, - linewidth=20, - fontsize=30, - size=(7000, 7000), - ) -end From 56edb2746d0258dd0f212e4a3e92b52597999e67 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 22:18:17 -0600 Subject: [PATCH 16/24] [ApproxContract] Retire ising_partition --- examples/approximate_contract/3dcube.jl | 16 +++--- .../ApproximateTNContraction.jl | 2 +- .../networks/3d_classical_ising.jl | 50 ------------------- test/approximate_tn_contraction/contract.jl | 3 +- 4 files changed, 11 insertions(+), 60 deletions(-) delete mode 100644 src/ApproximateTNContraction/networks/3d_classical_ising.jl diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index aab7efe8..b257d52f 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -1,8 +1,7 @@ using ITensors, TimerOutputs using NamedGraphs -using ITensorNetworks: contraction_sequence, ITensorNetwork -using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree -using ITensorNetworks.ApproximateTNContraction: timer, inds_network, ising_partition +using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network +using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree, timer function contract_log_norm(tn, seq) if seq isa Vector @@ -56,8 +55,11 @@ end function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache, ortho, env_line_size) ITensors.set_warn_order(100) reset_timer!(timer) - linkdim = 2 - tn = ising_partition(N, linkdim) + network = ising_network(named_grid(N), 0.3) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end if ortho == true @info "orthogonalize tn towards the first vertex" itn = ITensorNetwork(named_grid(N); link_space=2) @@ -125,8 +127,8 @@ end bench_3d_cube( (3, 3, 3); num_iter=2, - cutoff=1e-8, - maxdim=16, + cutoff=1e-10, + maxdim=32, ansatz="mps", use_cache=true, ortho=false, diff --git a/src/ApproximateTNContraction/ApproximateTNContraction.jl b/src/ApproximateTNContraction/ApproximateTNContraction.jl index 7893c166..8d6754d3 100644 --- a/src/ApproximateTNContraction/ApproximateTNContraction.jl +++ b/src/ApproximateTNContraction/ApproximateTNContraction.jl @@ -4,6 +4,7 @@ using ITensors using ITensors: data, contract +using NamedGraphs using TimerOutputs const timer = TimerOutput() @@ -11,7 +12,6 @@ const timer = TimerOutput() include("ITensors.jl") include("orthogonal_tensor.jl") include("networks/itensor_network.jl") -include("networks/3d_classical_ising.jl") include("interfaces/sweep_contractor.jl") include("contract/contract.jl") diff --git a/src/ApproximateTNContraction/networks/3d_classical_ising.jl b/src/ApproximateTNContraction/networks/3d_classical_ising.jl deleted file mode 100644 index 7dbccc41..00000000 --- a/src/ApproximateTNContraction/networks/3d_classical_ising.jl +++ /dev/null @@ -1,50 +0,0 @@ -using ITensors - -function ising_mpo(indices::Vector, β::Real, J::Real=1.0; sz::Bool=false) - d = dim(indices[1]) - for ind in indices - @assert d == dim(ind) - end - order = length(indices) - T = ITensor(indices...) - for i in 1:d - index = [i for _ in 1:order] - T[index...] = 1.0 - end - if sz - index = [1 for _ in 1:order] - T[index...] = -1.0 - end - simindices = map(sim, indices) - for i in 1:length(indices) - T = T * delta(indices[i], simindices[i]) - end - - f(λ₊, λ₋) = [ - (λ₊ + λ₋)/2 (λ₊ - λ₋)/2 - (λ₊ - λ₋)/2 (λ₊ + λ₋)/2 - ] - λ₊ = √(exp(β * J) + exp(-β * J)) - λ₋ = √(exp(β * J) - exp(-β * J)) - X = f(λ₊, λ₋) - - for i in 1:length(indices) - Xh = itensor(vec(X), simindices[i], indices[i]) - T = T * Xh - end - return T -end - -function isingTensor(inds::Vector) - # βc = 0.5 * log(√2 + 1) - β = 0.3 # 1.0 * βc - return ising_mpo(inds, β) -end - -function ising_partition(N, d=2) - tn = ITensorNetwork(named_grid(N); link_space=d) - for v in vertices(tn) - tn[v] = isingTensor(inds(tn[v])) - end - return Vector{ITensor}(tn) -end diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 4d3affe9..419941de 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -6,7 +6,7 @@ using ITensorNetworks.ApproximateTNContraction: inds_binary_tree, tree_embedding, approximate_contract -using ITensorNetworks.ApproximateTNContraction: timer, ising_partition +using ITensorNetworks.ApproximateTNContraction: timer include("utils.jl") @@ -150,7 +150,6 @@ end for v in vertices(network) tn[v...] = network[v...] end - # tn = ising_partition(N, linkdim) ITensors.set_warn_order(100) maxdim = linkdim^N[2] From 9e33bb56eab444d7ba17d91ccb7f7dd3227d1ad9 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Fri, 6 Jan 2023 22:30:40 -0600 Subject: [PATCH 17/24] [ApproxContraction] Retire unused itensor functions --- .../ApproximateTNContraction.jl | 1 - src/ApproximateTNContraction/ITensors.jl | 49 ------------------- 2 files changed, 50 deletions(-) delete mode 100644 src/ApproximateTNContraction/ITensors.jl diff --git a/src/ApproximateTNContraction/ApproximateTNContraction.jl b/src/ApproximateTNContraction/ApproximateTNContraction.jl index 8d6754d3..b221366b 100644 --- a/src/ApproximateTNContraction/ApproximateTNContraction.jl +++ b/src/ApproximateTNContraction/ApproximateTNContraction.jl @@ -9,7 +9,6 @@ using TimerOutputs const timer = TimerOutput() -include("ITensors.jl") include("orthogonal_tensor.jl") include("networks/itensor_network.jl") include("interfaces/sweep_contractor.jl") diff --git a/src/ApproximateTNContraction/ITensors.jl b/src/ApproximateTNContraction/ITensors.jl deleted file mode 100644 index bab000bd..00000000 --- a/src/ApproximateTNContraction/ITensors.jl +++ /dev/null @@ -1,49 +0,0 @@ -# -# ITensors.jl extensions -# - -# Generalize siteind to n-dimensional lattice -function ITensors.siteind(st::SiteType, N1::Integer, N2::Integer, Ns::Integer...; kwargs...) - s = siteind(st; kwargs...) - if !isnothing(s) - ts = "n1=$N1,n2=$N2" - for i in eachindex(Ns) - ts *= ",n$(i + 2)=$(Ns[i])" - end - return addtags(s, ts) - end - return isnothing(s) && error(space_error_message(st)) -end - -# Generalize siteinds to n-dimensional lattice -function ITensors.siteinds( - str::AbstractString, N1::Integer, N2::Integer, Ns::Integer...; kwargs... -) - st = SiteType(str) - return [siteind(st, ns...) for ns in Base.product(1:N1, 1:N2, UnitRange.(1, Ns)...)] -end - -# Get the promoted type of the Index objects in a collection -# of Index (Tuple, Vector, ITensor, etc.) -indtype(i::Index) = typeof(i) -indtype(T::Type{<:Index}) = T -indtype(is::Tuple{Vararg{<:Index}}) = eltype(is) -indtype(is::Vector{<:Index}) = eltype(is) -indtype(A::ITensor...) = indtype(inds.(A)) - -indtype(tn1, tn2) = promote_type(indtype(tn1), indtype(tn2)) -indtype(tn) = mapreduce(indtype, promote_type, tn) - -# -# MPS functionality extensions -# - -Base.keytype(m::MPS) = keytype(data(m)) - -# A version of indexing which returns an empty order-0 ITensor -# when out of bounds -get_itensor(x::MPS, n::Int) = n in 1:length(x) ? x[n] : ITensor() - -# Reverse the site ordering of an MPS. -# XXX: also reverse the orthogonality limits. -Base.reverse(x::MPS) = MPS(reverse(x.data)) From c17a143a7e74c520b967e415c06672e1f7ae1a08 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Tue, 10 Jan 2023 15:09:00 -0600 Subject: [PATCH 18/24] [approxContract] add orthogonalization (performance not as expected) --- examples/approximate_contract/3dcube.jl | 102 ++++++++++-------- .../contract/contract.jl | 68 ++++++++++-- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index b257d52f..21dc4d17 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -1,7 +1,8 @@ using ITensors, TimerOutputs -using NamedGraphs +using ITensorNetworks using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network -using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree, timer +using ITensorNetworks.ApproximateTNContraction: + approximate_contract, line_to_tree, timer, line_network function contract_log_norm(tn, seq) if seq isa Vector @@ -29,14 +30,19 @@ function exact_contract(N) return contract_log_norm(tn, seq) end -function build_tntree(tn, N; env_line_size) +function build_tntree(tn, N; strategy, env_size) + @assert strategy in ["element", "line"] + if strategy == "element" + tn = vec(tn) + return line_network(tn) + end line_index = 1 num_lines = N[2] * N[3] tntree = nothing while line_index <= N[2] * N[3] partition = Vector{ITensor}() @info "partition" - for _ in 1:env_line_size + for _ in 1:env_size if line_index <= num_lines @info "line_index", line_index push!(partition, tn[:, line_index]...) @@ -52,7 +58,9 @@ function build_tntree(tn, N; env_line_size) return tntree end -function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache, ortho, env_line_size) +function bench_3d_cube( + N; num_iter, cutoff, maxdim, ansatz, snake, use_cache, ortho, strategy, env_size +) ITensors.set_warn_order(100) reset_timer!(timer) network = ising_network(named_grid(N), 0.3) @@ -60,47 +68,44 @@ function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache, ortho, en for v in vertices(network) tn[v...] = network[v...] end - if ortho == true - @info "orthogonalize tn towards the first vertex" - itn = ITensorNetwork(named_grid(N); link_space=2) - for i in 1:N[1] - for j in 1:N[2] - for k in 1:N[3] - itn[i, j, k] = tn[i, j, k] - end - end - end - itn = orthogonalize(itn, (1, 1, 1)) - @info itn[1, 1, 1] - @info itn[1, 1, 1].tensor - for i in 1:N[1] - for j in 1:N[2] - for k in 1:N[3] - tn[i, j, k] = itn[i, j, k] - end - end - end - end - # tn_inds = inds_network(N...; linkdims=linkdim, periodic=false) - # tn = map(inds -> randomITensor(inds...), tn_inds) - # tntree = nothing - # for k in 1:N[3] - # rangej = iseven(k) ? reverse(1:N[2]) : 1:N[2] - # for j in rangej - # @info j, k - # if tntree == nothing - # tntree = tn[:, j, k] - # else - # tntree = [tntree, tn[:, j, k]] + # if ortho == true + # @info "orthogonalize tn towards the first vertex" + # itn = ITensorNetwork(named_grid(N); link_space=2) + # for i in 1:N[1] + # for j in 1:N[2] + # for k in 1:N[3] + # itn[i, j, k] = tn[i, j, k] + # end + # end + # end + # itn = orthogonalize(itn, (1, 1, 1)) + # @info itn[1, 1, 1] + # @info itn[1, 1, 1].tensor + # for i in 1:N[1] + # for j in 1:N[2] + # for k in 1:N[3] + # tn[i, j, k] = itn[i, j, k] + # end # end # end # end + if snake == true + for k in 1:N[3] + rangej = iseven(k) ? reverse(1:N[2]) : 1:N[2] + tn[:, rangej, k] = tn[:, 1:N[2], k] + end + end tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = build_tntree(tn, N; env_line_size=env_line_size) + tntree = build_tntree(tn, N; strategy=strategy, env_size=env_size) out_list = [] for _ in 1:num_iter out, log_acc_norm = approximate_contract( - tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache + tntree; + cutoff=cutoff, + maxdim=maxdim, + ansatz=ansatz, + use_cache=use_cache, + orthogonalize=ortho, ) @info "out is", log(out[1][1]) + log_acc_norm push!(out_list, log(out[1][1]) + log_acc_norm) @@ -108,13 +113,14 @@ function bench_3d_cube(N; num_iter, cutoff, maxdim, ansatz, use_cache, ortho, en show(timer) # after warmup, start to benchmark reset_timer!(timer) - linkdim = 2 - tn = ising_partition(N, linkdim) - tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = build_tntree(tn, N; env_line_size=env_line_size) for _ in 1:num_iter out, log_acc_norm = approximate_contract( - tntree; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, use_cache=use_cache + tntree; + cutoff=cutoff, + maxdim=maxdim, + ansatz=ansatz, + use_cache=use_cache, + orthogonalize=ortho, ) @info "out is", log(out[1][1]) + log_acc_norm push!(out_list, log(out[1][1]) + log_acc_norm) @@ -125,12 +131,14 @@ end # exact_contract((5, 5, 5)) bench_3d_cube( - (3, 3, 3); + (6, 6, 6); num_iter=2, - cutoff=1e-10, + cutoff=1e-8, maxdim=32, ansatz="mps", + snake=false, use_cache=true, ortho=false, - env_line_size=1, + strategy="line", + env_size=1, ) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 725f0a74..12dc52dc 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -746,13 +746,10 @@ function get_tn_cache_sub_info( end function get_tn_cache_info( - ctree_to_tn_tree::Dict{Vector,Dict{Vector,OrthogonalITensor}}, - ctree_1::Vector, - ctree_2::Vector, - cache_binary_trees::Vector, + ctree_to_tn_tree, ctree_1::Vector, ctree_2::Vector, cache_binary_trees::Vector ) @timeit timer "get_tn_cache_info" begin - if haskey(ctree_to_tn_tree, ctree_1) + if haskey(ctree_to_tn_tree, ctree_1) && ctree_to_tn_tree[ctree_1] isa Dict tn_tree_1 = ctree_to_tn_tree[ctree_1] cached_tn_tree1, uncached_tn1, new_igs_1 = get_tn_cache_sub_info( tn_tree_1, cache_binary_trees @@ -762,7 +759,7 @@ function get_tn_cache_info( uncached_tn1 = get_child_tn(ctree_to_tn_tree, ctree_1) new_igs_1 = [nothing, nothing] end - if haskey(ctree_to_tn_tree, ctree_2) + if haskey(ctree_to_tn_tree, ctree_2) && ctree_to_tn_tree[ctree_2] isa Dict tn_tree_2 = ctree_to_tn_tree[ctree_2] cached_tn_tree2, uncached_tn2, new_igs_2 = get_tn_cache_sub_info( tn_tree_2, cache_binary_trees @@ -813,17 +810,56 @@ function update_tn_tree_keys!(tn_tree, inds_btree, pairs::Vector{Pair}) end end -function get_child_tn( - ctree_to_tn_tree::Dict{Vector,Dict{Vector,OrthogonalITensor}}, ctree::Vector -) +function get_child_tn(ctree_to_tn_tree, ctree::Vector) if !haskey(ctree_to_tn_tree, ctree) @assert ctree isa Vector{ITensor} return orthogonal_tensors(ctree) + elseif ctree_to_tn_tree[ctree] isa Vector{OrthogonalITensor} + return ctree_to_tn_tree[ctree] else return vcat(collect(values(ctree_to_tn_tree[ctree]))...) end end +_index_less(a::Index, b::Index) = tags(a)[1] < tags(b)[1] + +function orthogonalize!(ctree_to_tn_tree::Dict, environments::Vector, c::Vector) + @info "start orthogonalize with env size", length(environments) + @timeit timer "orthogonalize" begin + index = nothing + if c[1] in environments + index = 1 + elseif c[2] in environments + index = 2 + end + if index == nothing + return nothing + end + network = vcat([get_child_tn(ctree_to_tn_tree, env) for env in environments]...) + network = get_tensors(network) + source_tensor = get_child_tn(ctree_to_tn_tree, c[index])[end].tensor + v = findfirst(i -> i == source_tensor, network) + orth_tn = orthogonalize(ITensorNetwork(network), v) + tensor_to_ortho_tensor = Dict{ITensor,ITensor}() + for i in 1:length(network) + inds1 = sort(inds(orth_tn[i]); lt=_index_less) + inds2 = sort(inds(network[i]); lt=_index_less) + inds1_tags = [tags(i) for i in inds1] + inds2_tags = [tags(i) for i in inds2] + @assert inds1_tags == inds2_tags + new_tensor = replaceinds(orth_tn[i], inds1, inds2) + tensor_to_ortho_tensor[network[i]] = new_tensor + end + for env in environments + ortho_tensors = Vector{OrthogonalITensor}([ + OrthogonalITensor(tensor_to_ortho_tensor[t.tensor]) for + t in get_child_tn(ctree_to_tn_tree, env) + ]) + ctree_to_tn_tree[env] = ortho_tensors + end + end +end + # ctree: contraction tree # tn: vector of tensors representing a tensor network # tn_tree: a dict maps each index tree in the tn to a tensor @@ -831,18 +867,28 @@ end # ig: index group # contract_ig: the index group to be contracted next # ig_tree: an index group with a tree hierarchy -function approximate_contract(ctree::Vector; cutoff, maxdim, ansatz="mps", use_cache=true) +function approximate_contract( + ctree::Vector; cutoff, maxdim, ansatz="mps", use_cache=true, orthogonalize=false +) @timeit timer "approximate_contract" begin tn_leaves = get_leaves(ctree) + environments = tn_leaves ctrees = topo_sort(ctree; leaves=tn_leaves) ctree_to_igs, ctree_to_adj_tree, ctree_to_contract_igs, ig_to_linear_order = _approximate_contract_pre_process( tn_leaves, ctrees ) # mapping each contraction tree to a tensor network - ctree_to_tn_tree = Dict{Vector,Dict{Vector,OrthogonalITensor}}() + ctree_to_tn_tree = Dict{ + Vector,Union{Dict{Vector,OrthogonalITensor},Vector{OrthogonalITensor}} + }() # accumulate norm log_accumulated_norm = 0.0 for (ii, c) in enumerate(ctrees) + @info "orthogonalize", orthogonalize + if orthogonalize == true + orthogonalize!(ctree_to_tn_tree, environments, c) + environments = setdiff(environments, c) + end @info ii, "th tree approximation" if ctree_to_igs[c] == [] @assert c == ctrees[end] From c0177e82bcda19c407aa954f4710b5f97a9b820c Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Wed, 11 Jan 2023 14:38:57 -0600 Subject: [PATCH 19/24] [ApproxContraction] update orthogonalize --- .../contract/contract.jl | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 12dc52dc..51c8f9aa 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -824,22 +824,29 @@ end _index_less(a::Index, b::Index) = tags(a)[1] < tags(b)[1] function orthogonalize!(ctree_to_tn_tree::Dict, environments::Vector, c::Vector) - @info "start orthogonalize with env size", length(environments) @timeit timer "orthogonalize" begin - index = nothing + index = [] if c[1] in environments - index = 1 + push!(index, 1) elseif c[2] in environments - index = 2 + push!(index, 2) end - if index == nothing + if index == [] return nothing end + environments = setdiff(environments, c) + if length(environments) == 0 + return nothing + end + @info "start orthogonalize with env size", length(environments) network = vcat([get_child_tn(ctree_to_tn_tree, env) for env in environments]...) network = get_tensors(network) - source_tensor = get_child_tn(ctree_to_tn_tree, c[index])[end].tensor - v = findfirst(i -> i == source_tensor, network) - orth_tn = orthogonalize(ITensorNetwork(network), v) + env_boundary = get_child_tn(ctree_to_tn_tree, c[index[1]]) + source_tensor = env_boundary[1].tensor + @assert !(source_tensor in network) + push!(network, source_tensor) + ctree_to_tn_tree[c[index[1]]] = env_boundary[2:end] + orth_tn = orthogonalize(ITensorNetwork(network), length(network)) tensor_to_ortho_tensor = Dict{ITensor,ITensor}() for i in 1:length(network) inds1 = sort(inds(orth_tn[i]); lt=_index_less) @@ -857,6 +864,10 @@ function orthogonalize!(ctree_to_tn_tree::Dict, environments::Vector, c::Vector) ]) ctree_to_tn_tree[env] = ortho_tensors end + push!( + ctree_to_tn_tree[c[index[1]]], + OrthogonalITensor(tensor_to_ortho_tensor[source_tensor]), + ) end end From 78953a1480498fd1bf110b5384ad1b6e1df01591 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Wed, 11 Jan 2023 15:44:55 -0600 Subject: [PATCH 20/24] [ApproxContraction] update 3dcube env interface in contraction tree --- examples/approximate_contract/3dcube.jl | 49 ++++++++++++------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 21dc4d17..c0f232bb 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -30,36 +30,35 @@ function exact_contract(N) return contract_log_norm(tn, seq) end -function build_tntree(tn, N; strategy, env_size) - @assert strategy in ["element", "line"] - if strategy == "element" - tn = vec(tn) - return line_network(tn) - end - line_index = 1 - num_lines = N[2] * N[3] +function build_tntree(tn, N; env_size) + @assert length(N) == length(env_size) + n = [Integer(N[i] / env_size[i]) for i in 1:length(N)] tntree = nothing - while line_index <= N[2] * N[3] - partition = Vector{ITensor}() - @info "partition" - for _ in 1:env_size - if line_index <= num_lines - @info "line_index", line_index - push!(partition, tn[:, line_index]...) - line_index += 1 + for k in 1:n[3] + for j in 1:n[2] + for i in 1:n[1] + ii = (i - 1) * env_size[1] + jj = (j - 1) * env_size[2] + kk = (k - 1) * env_size[3] + sub_tn = tn[ + (ii + 1):(ii + env_size[1]), + (jj + 1):(jj + env_size[2]), + (kk + 1):(kk + env_size[3]), + ] + sub_tn = vec(sub_tn) + if tntree == nothing + tntree = sub_tn + else + tntree = [tntree, sub_tn] + end end end - if tntree == nothing - tntree = partition - else - tntree = [tntree, partition] - end end return tntree end function bench_3d_cube( - N; num_iter, cutoff, maxdim, ansatz, snake, use_cache, ortho, strategy, env_size + N; num_iter, cutoff, maxdim, ansatz, snake, use_cache, ortho, env_size ) ITensors.set_warn_order(100) reset_timer!(timer) @@ -95,8 +94,7 @@ function bench_3d_cube( tn[:, rangej, k] = tn[:, 1:N[2], k] end end - tn = reshape(tn, (N[1], N[2] * N[3])) - tntree = build_tntree(tn, N; strategy=strategy, env_size=env_size) + tntree = build_tntree(tn, N; env_size=env_size) out_list = [] for _ in 1:num_iter out, log_acc_norm = approximate_contract( @@ -139,6 +137,5 @@ bench_3d_cube( snake=false, use_cache=true, ortho=false, - strategy="line", - env_size=1, + env_size=(2, 2, 1), ) From e705c3db2f0dbcef2dc96c5f6b03b0fa14e09361 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Thu, 12 Jan 2023 12:34:57 -0600 Subject: [PATCH 21/24] [ApproxContraction] Update 3dcube.jl --- examples/approximate_contract/3dcube.jl | 71 +++++++++++++++++++++---- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index c0f232bb..21e22e8b 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -4,11 +4,19 @@ using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree, timer, line_network +INDEX = 0 + function contract_log_norm(tn, seq) + global INDEX if seq isa Vector + if length(seq) == 1 + return seq[1] + end t1 = contract_log_norm(tn, seq[1]) t2 = contract_log_norm(tn, seq[2]) @info size(t1[1]), size(t2[1]) + INDEX += 1 + @info "INDEX", INDEX out = t1[1] * t2[1] nrm = norm(out) out /= nrm @@ -20,19 +28,24 @@ function contract_log_norm(tn, seq) end function exact_contract(N) - ITensors.set_warn_order(100) + ITensors.set_warn_order(1000) reset_timer!(timer) linkdim = 2 - tn = vec(ising_partition(N, linkdim)) - # contraction_sequence(tn; alg="kahypar_bipartite", sc_target=30) - seq = line_to_tree([i for i in 1:prod(N)]) + network = ising_network(named_grid(N), 0.3) + tn = Array{ITensor,length(N)}(undef, N...) + for v in vertices(network) + tn[v...] = network[v...] + end + tn = vec(tn) + seq = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=36) + @info seq tn = [(i, 0.0) for i in tn] return contract_log_norm(tn, seq) end function build_tntree(tn, N; env_size) @assert length(N) == length(env_size) - n = [Integer(N[i] / env_size[i]) for i in 1:length(N)] + n = [ceil(Int, N[i] / env_size[i]) for i in 1:length(N)] tntree = nothing for k in 1:n[3] for j in 1:n[2] @@ -40,11 +53,10 @@ function build_tntree(tn, N; env_size) ii = (i - 1) * env_size[1] jj = (j - 1) * env_size[2] kk = (k - 1) * env_size[3] - sub_tn = tn[ - (ii + 1):(ii + env_size[1]), - (jj + 1):(jj + env_size[2]), - (kk + 1):(kk + env_size[3]), - ] + ii_end = min(ii + env_size[1], N[1]) + jj_end = min(jj + env_size[2], N[2]) + kk_end = min(kk + env_size[3], N[3]) + sub_tn = tn[(ii + 1):ii_end, (jj + 1):jj_end, (kk + 1):kk_end] sub_tn = vec(sub_tn) if tntree == nothing tntree = sub_tn @@ -57,6 +69,45 @@ function build_tntree(tn, N; env_size) return tntree end +function build_recursive_tntree(tn, N; env_size) + @assert env_size == (3, 3, 1) + tn_tree1 = vec(tn[1:3, 1:3, 1]) + tn_tree1 = [vec(tn[1:3, 1:3, 2]), tn_tree1] + tn_tree1 = [vec(tn[1:3, 1:3, 3]), tn_tree1] + + tn_tree2 = vec(tn[1:3, 4:6, 1]) + tn_tree2 = [vec(tn[1:3, 4:6, 2]), tn_tree2] + tn_tree2 = [vec(tn[1:3, 4:6, 3]), tn_tree2] + + tn_tree3 = vec(tn[4:6, 1:3, 1]) + tn_tree3 = [vec(tn[4:6, 1:3, 2]), tn_tree3] + tn_tree3 = [vec(tn[4:6, 1:3, 3]), tn_tree3] + + tn_tree4 = vec(tn[4:6, 4:6, 1]) + tn_tree4 = [vec(tn[4:6, 4:6, 2]), tn_tree4] + tn_tree4 = [vec(tn[4:6, 4:6, 3]), tn_tree4] + + tn_tree5 = vec(tn[1:3, 1:3, 6]) + tn_tree5 = [vec(tn[1:3, 1:3, 5]), tn_tree5] + tn_tree5 = [vec(tn[1:3, 1:3, 4]), tn_tree5] + + tn_tree6 = vec(tn[1:3, 4:6, 6]) + tn_tree6 = [vec(tn[1:3, 4:6, 5]), tn_tree6] + tn_tree6 = [vec(tn[1:3, 4:6, 4]), tn_tree6] + + tn_tree7 = vec(tn[4:6, 1:3, 6]) + tn_tree7 = [vec(tn[4:6, 1:3, 5]), tn_tree7] + tn_tree7 = [vec(tn[4:6, 1:3, 4]), tn_tree7] + + tn_tree8 = vec(tn[4:6, 4:6, 6]) + tn_tree8 = [vec(tn[4:6, 4:6, 5]), tn_tree8] + tn_tree8 = [vec(tn[4:6, 4:6, 4]), tn_tree8] + return [ + [[tn_tree1, tn_tree2], [tn_tree3, tn_tree4]], + [[tn_tree5, tn_tree6], [tn_tree7, tn_tree8]], + ] +end + function bench_3d_cube( N; num_iter, cutoff, maxdim, ansatz, snake, use_cache, ortho, env_size ) From 8c82b0b8cf3fd2a410ce87b5427a5f2beedaf37e Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Mon, 23 Jan 2023 21:46:08 -0600 Subject: [PATCH 22/24] [ApproxContract] Rewrite tree embedding, add tree approximation using svd --- examples/approximate_contract/3dcube.jl | 44 +++-- .../contract/contract.jl | 171 +++++++++++++++--- .../contract/tree_embedding.jl | 138 ++++++++------ .../contract/tree_embedding_.jl | 119 ++++++++++++ .../abstracttreetensornetwork.jl | 2 +- test/approximate_tn_contraction/contract.jl | 6 +- 6 files changed, 374 insertions(+), 106 deletions(-) create mode 100644 src/ApproximateTNContraction/contract/tree_embedding_.jl diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 21e22e8b..1c88b3dc 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -109,7 +109,7 @@ function build_recursive_tntree(tn, N; env_size) end function bench_3d_cube( - N; num_iter, cutoff, maxdim, ansatz, snake, use_cache, ortho, env_size + N; num_iter, cutoff, maxdim, ansatz, algorithm, snake, use_cache, ortho, env_size ) ITensors.set_warn_order(100) reset_timer!(timer) @@ -119,26 +119,26 @@ function bench_3d_cube( tn[v...] = network[v...] end # if ortho == true - # @info "orthogonalize tn towards the first vertex" - # itn = ITensorNetwork(named_grid(N); link_space=2) - # for i in 1:N[1] - # for j in 1:N[2] - # for k in 1:N[3] - # itn[i, j, k] = tn[i, j, k] - # end + # @info "orthogonalize tn towards the first vertex" + # itn = ITensorNetwork(named_grid(N); link_space=2) + # for i in 1:N[1] + # for j in 1:N[2] + # for k in 1:N[3] + # itn[i, j, k] = tn[i, j, k] # end # end - # itn = orthogonalize(itn, (1, 1, 1)) - # @info itn[1, 1, 1] - # @info itn[1, 1, 1].tensor - # for i in 1:N[1] - # for j in 1:N[2] - # for k in 1:N[3] - # tn[i, j, k] = itn[i, j, k] - # end + # end + # itn = orthogonalize(itn, (1, 1, 1)) + # @info itn[1, 1, 1] + # @info itn[1, 1, 1].tensor + # for i in 1:N[1] + # for j in 1:N[2] + # for k in 1:N[3] + # tn[i, j, k] = itn[i, j, k] # end # end # end + # end if snake == true for k in 1:N[3] rangej = iseven(k) ? reverse(1:N[2]) : 1:N[2] @@ -153,6 +153,7 @@ function bench_3d_cube( cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, + algorithm=algorithm, use_cache=use_cache, orthogonalize=ortho, ) @@ -168,6 +169,7 @@ function bench_3d_cube( cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, + algorithm=algorithm, use_cache=use_cache, orthogonalize=ortho, ) @@ -179,14 +181,16 @@ function bench_3d_cube( end # exact_contract((5, 5, 5)) -bench_3d_cube( +# TODO: (6, 6, 6), env_size=(2, 1, 1) is buggy (cutoff=1e-12, maxdim=256, ansatz="comb", algorithm="density_matrix",) +@time bench_3d_cube( (6, 6, 6); num_iter=2, - cutoff=1e-8, - maxdim=32, + cutoff=1e-12, + maxdim=320, ansatz="mps", + algorithm="density_matrix", snake=false, use_cache=true, ortho=false, - env_size=(2, 2, 1), + env_size=(3, 1, 1), ) diff --git a/src/ApproximateTNContraction/contract/contract.jl b/src/ApproximateTNContraction/contract/contract.jl index 51c8f9aa..44a00086 100644 --- a/src/ApproximateTNContraction/contract/contract.jl +++ b/src/ApproximateTNContraction/contract/contract.jl @@ -1,7 +1,7 @@ using Graphs, GraphsFlows, Combinatorics, SimpleWeightedGraphs using GraphRecipes, Plots using OMEinsumContractionOrders -using ITensorNetworks: contraction_sequence +using ITensorNetworks: contraction_sequence, TTN, IndsNetwork function Base.show(io::IO, tensor::ITensor) return print(io, string(inds(tensor))) @@ -14,9 +14,8 @@ include("tree_embedding.jl") function optcontract(t_list::Vector) @timeit timer "optcontract" begin - # TODO: make this support orthotensor if length(t_list) == 0 - return ITensor(1.0) + return OrthogonalITensor(ITensor(1.0)) end @assert t_list isa Vector{OrthogonalITensor} t_list = get_tensors(t_list) @@ -25,7 +24,7 @@ function optcontract(t_list::Vector) # @info "size of t is", size(t) # end @timeit timer "contraction_sequence" begin - seq = contraction_sequence(t_list; alg="greedy") + seq = contraction_sequence(t_list; alg="sa_bipartite") end @timeit timer "contract" begin output = contract(t_list; sequence=seq) @@ -57,7 +56,8 @@ function approximate_contract_ctree_to_tensor( cutoff, maxdim, maxsize=10^15, - algorithm="mps", + ansatz="mps", + algorithm="density_matrix", ) uncontract_inds = noncommoninds(tn...) allinds = collect(Set(mapreduce(t -> collect(inds(t)), vcat, tn))) @@ -80,16 +80,20 @@ function approximate_contract_ctree_to_tensor( # tn = Vector{ITensor}(vcat(deltas, tnprime)) # end if inds_btree == nothing - inds_btree = inds_binary_tree(get_tensors(tn), nothing; algorithm=algorithm) + inds_btree = inds_binary_tree(get_tensors(tn), nothing; algorithm=ansatz) end - # tree_approximation(tn, inds_btree; cutoff=cutoff, maxdim=maxdim) - embedding = tree_embedding(tn, inds_btree) + embedding = tree_embedding(tn, inds_btree; algorithm=algorithm) tn = Vector{OrthogonalITensor}(vcat(collect(values(embedding))...)) i2 = noncommoninds(tn...) @assert (length(uncontract_inds) == length(i2)) - @timeit timer "tree_approximation_cache" begin - return tree_approximation_cache( - embedding, inds_btree; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + @timeit timer "tree_approximation" begin + return tree_approximation( + embedding, + inds_btree; + cutoff=cutoff, + maxdim=maxdim, + maxsize=maxsize, + algorithm=algorithm, ) end end @@ -823,6 +827,15 @@ end _index_less(a::Index, b::Index) = tags(a)[1] < tags(b)[1] +function _replaceinds(t1::ITensor, t2::ITensor) + inds1 = sort(inds(t1); lt=_index_less) + inds2 = sort(inds(t2); lt=_index_less) + inds1_tags = [tags(i) for i in inds1] + inds2_tags = [tags(i) for i in inds2] + @assert inds1_tags == inds2_tags + return replaceinds(t1, inds1, inds2) +end + function orthogonalize!(ctree_to_tn_tree::Dict, environments::Vector, c::Vector) @timeit timer "orthogonalize" begin index = [] @@ -849,12 +862,7 @@ function orthogonalize!(ctree_to_tn_tree::Dict, environments::Vector, c::Vector) orth_tn = orthogonalize(ITensorNetwork(network), length(network)) tensor_to_ortho_tensor = Dict{ITensor,ITensor}() for i in 1:length(network) - inds1 = sort(inds(orth_tn[i]); lt=_index_less) - inds2 = sort(inds(network[i]); lt=_index_less) - inds1_tags = [tags(i) for i in inds1] - inds2_tags = [tags(i) for i in inds2] - @assert inds1_tags == inds2_tags - new_tensor = replaceinds(orth_tn[i], inds1, inds2) + new_tensor = _replaceinds(orth_tn[i], network[i]) tensor_to_ortho_tensor[network[i]] = new_tensor end for env in environments @@ -879,7 +887,13 @@ end # contract_ig: the index group to be contracted next # ig_tree: an index group with a tree hierarchy function approximate_contract( - ctree::Vector; cutoff, maxdim, ansatz="mps", use_cache=true, orthogonalize=false + ctree::Vector; + cutoff, + maxdim, + ansatz="mps", + use_cache=true, + orthogonalize=false, + algorithm="density_matrix", ) @timeit timer "approximate_contract" begin tn_leaves = get_leaves(ctree) @@ -919,7 +933,7 @@ function approximate_contract( ansatz=ansatz, ) ctree_to_tn_tree[c], log_root_norm = approximate_contract_ctree_to_tensor( - [tn1..., tn2...], inds_btree; cutoff=cutoff, maxdim=maxdim + [tn1..., tn2...], inds_btree; cutoff=cutoff, maxdim=maxdim, algorithm=algorithm ) log_accumulated_norm += log_root_norm continue @@ -977,24 +991,121 @@ function approximate_contract( new_igs, ctree_to_contract_igs[c], new_ig_to_linear_order; ansatz=ansatz ) new_tn_tree, log_root_norm = approximate_contract_ctree_to_tensor( - uncached_tn, inds_btree; cutoff=cutoff, maxdim=maxdim + uncached_tn, inds_btree; cutoff=cutoff, maxdim=maxdim, algorithm=algorithm ) log_accumulated_norm += log_root_norm if length(new_ig_to_binary_tree_pairs) != 0 update_tn_tree_keys!(new_tn_tree, inds_btree, new_ig_to_binary_tree_pairs) end ctree_to_tn_tree[c] = merge(new_tn_tree, cached_tn_tree) + # release the memory + delete!(ctree_to_tn_tree, c[1]) + delete!(ctree_to_tn_tree, c[2]) end tn = vcat(collect(values(ctree_to_tn_tree[ctrees[end]]))...) return get_tensors(tn), log_accumulated_norm end end -# interlaced HOSVD using caching -function tree_approximation_cache( +function tree_approximation( + embedding::Dict, + inds_btree::Vector; + cutoff=1e-15, + maxdim=10000, + maxsize=10000, + algorithm="density_matrix", +) + @assert algorithm in ["density_matrix", "density_matrix_contract_first", "svd"] + if algorithm == "density_matrix" + return tree_approximation_density_matrix( + embedding, inds_btree; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + ) + end + if algorithm == "density_matrix_contract_first" + @info "density_matrix_contract_first" + btree_to_contracted_tn = Dict{Vector,Vector{OrthogonalITensor}}() + for (btree, ts) in embedding + btree_to_contracted_tn[btree] = [optcontract(ts)] + end + return tree_approximation_density_matrix( + btree_to_contracted_tn, inds_btree; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + ) + end + if algorithm == "svd" + return tree_approximation_svd( + embedding, inds_btree; cutoff=cutoff, maxdim=maxdim, maxsize=maxsize + ) + end +end + +function tree_approximation_svd( embedding::Dict, inds_btree::Vector; cutoff=1e-15, maxdim=10000, maxsize=10000 ) - @info "start tree_approximation_cache", inds_btree + @info "start tree_approximation_svd", inds_btree + @info "cutoff", cutoff, "maxdim", maxdim + network = Vector{ITensor}() + btree_to_order = Dict{Vector,Int}() + root_vertex = nothing + for (btree, ts) in embedding + # use dense to convert Diag type to dense for QR decomposition TODO: raise an error in ITensors + push!(network, dense(optcontract(ts).tensor)) + btree_to_order[btree] = length(network) + if btree == inds_btree + root_vertex = length(network) + end + end + @assert root_vertex != nothing + ttn = TTN(ITensorNetwork(network)) + @timeit timer "truncate" begin + truncate_ttn = truncate(ttn; cutoff=cutoff, maxdim=maxdim, root_vertex=root_vertex) + end + out_network = [truncate_ttn[i] for i in 1:length(network)] + inds1 = sort(uncontractinds(out_network); lt=_index_less) + inds2 = sort(uncontractinds(network); lt=_index_less) + out_network = replaceinds(out_network, Dict(zip(inds1, inds2))) + root_norm = norm(out_network[root_vertex]) + out_network[root_vertex] /= root_norm + ctree_to_tensor = Dict{Vector,OrthogonalITensor}() + for node in topo_sort(inds_btree; type=Vector{<:Vector}) + children_tensors = [] + if !(node[1] isa Vector{<:Vector}) + push!(children_tensors, OrthogonalITensor(out_network[btree_to_order[node[1]]])) + end + if !(node[2] isa Vector{<:Vector}) + push!(children_tensors, OrthogonalITensor(out_network[btree_to_order[node[2]]])) + end + t = OrthogonalITensor(out_network[btree_to_order[node]]) + if children_tensors == [] + ctree_to_tensor[node] = t + else + ctree_to_tensor[node] = optcontract([t, children_tensors...]) + end + end + return ctree_to_tensor, log(root_norm) +end + +function _randomized_svd(t::ITensor, linds, rinds; maxdim) + cutoff = 1e-16 + @info "randomized svd", "cutoff", cutoff, "maxdim", maxdim + rand_dim = maxdim + 20 + new_index = Index(Integer(rand_dim), "rand_dim") + rand_mat = randomITensor(rinds..., new_index) + rand_t = contract(t, rand_mat) + Q, _ = factorize(rand_t, linds...; which_decomp="qr", ortho="left") + rand_t = contract(t, Q) + Q, _ = factorize(rand_t, rinds...; which_decomp="qr", ortho="left") + rand_t = contract(t, Q) + Q, _ = factorize(rand_t, linds...; which_decomp="qr", ortho="left") + rand_mat = contract(t, Q) + U, diag, _ = svd(rand_mat, rinds...; maxdim=maxdim, cutoff=cutoff, alg="qr_iteration") + return diag, U +end + +# interlaced HOSVD using density matrix and caching +function tree_approximation_density_matrix( + embedding::Dict, inds_btree::Vector; cutoff=1e-15, maxdim=10000, maxsize=10000 +) + @info "start tree_approximation_density matrix", inds_btree ctree_to_tensor = Dict{Vector,OrthogonalITensor}() # initialize sim_dict network = vcat(collect(values(embedding))...) @@ -1039,13 +1150,19 @@ function tree_approximation_cache( net = [subenvtensor, netket..., subnet1..., subnet2...] tnormal = optcontract(net) dim2 = floor(maxsize / (space(ind1_pair[1]) * space(ind2_pair[1]))) - dim = min(maxdim, dim2) + max_dim = min(maxdim, dim2) t00 = time() @info "eigen input size", size(tnormal.tensor) @timeit timer "eigen" begin - diag, U = eigen( - tnormal.tensor, linds, rinds; cutoff=cutoff, maxdim=dim, ishermitian=true - ) + left_size = prod([dim(a) for a in linds]) + right_size = prod([dim(a) for a in rinds]) + if left_size < max_dim * 5 || right_size < max_dim * 5 + diag, U = eigen( + tnormal.tensor, linds, rinds; cutoff=cutoff, maxdim=max_dim, ishermitian=true + ) + else + diag, U = _randomized_svd(tnormal.tensor, linds, rinds; maxdim=max_dim) + end end t11 = time() - t00 @info "size of U", size(U), "size of diag", size(diag), "costs", t11 diff --git a/src/ApproximateTNContraction/contract/tree_embedding.jl b/src/ApproximateTNContraction/contract/tree_embedding.jl index 45f885ef..db0ba6d5 100644 --- a/src/ApproximateTNContraction/contract/tree_embedding.jl +++ b/src/ApproximateTNContraction/contract/tree_embedding.jl @@ -1,12 +1,12 @@ include("union_find.jl") -function tree_embedding(network::Vector{OrthogonalITensor}, inds_btree::Vector) +function tree_embedding(network::Vector{OrthogonalITensor}, inds_btree::Vector; algorithm) # TODO: consider identity matrices tensor_to_ortho_tensor = Dict{ITensor,OrthogonalITensor}() for ortho_tensor in network tensor_to_ortho_tensor[ortho_tensor.tensor] = ortho_tensor end - tnets_dict = tree_embedding(get_tensors(network), inds_btree) + tnets_dict = tree_embedding(get_tensors(network), inds_btree; algorithm=algorithm) ortho_tnets_dict = Dict() for (key, tensors) in tnets_dict ortho_tensors = Vector{OrthogonalITensor}() @@ -22,53 +22,95 @@ function tree_embedding(network::Vector{OrthogonalITensor}, inds_btree::Vector) return ortho_tnets_dict end -function tree_embedding(network::Vector{ITensor}, inds_btree::Vector) +function mincut_subnetwork_insert_deltas( + network::Vector{ITensor}, source_inds::Vector{<:Index} +) + out_inds = noncommoninds(network...) + # terminal_inds = setdiff(out_inds, source_inds) + # tensors_to_add_delta = [] + # for t in network + # t_inds = inds(t) + # if length(intersect(source_inds, t_inds)) > 0 && length(intersect(terminal_inds, t_inds)) > 0 + # push!(tensors_to_add_delta, t) + # end + # end + # inds_to_add_delta = [] + # for t in tensors_to_add_delta + # uncontract_inds = intersect(inds(t), out_inds) + # inds_to_add_delta = [inds_to_add_delta..., uncontract_inds...] + # end + # deltas, networkprime, _ = split_deltas(inds_to_add_delta, network) deltas, networkprime, _ = split_deltas(noncommoninds(network...), network) - network = vcat(deltas, networkprime) - # tnets_dict map each inds_btree node to a tensor network - tnets_dict = Dict() - function embed(tree::Vector) - if length(tree) == 1 - # add delta to handle the case with two output edges neighboring to one tensor - # being split (MPS case with element grouping) - ind = tree[1] - sim_dict = Dict([ind => sim(ind)]) - tnets_dict[tree] = [delta(ind, sim_dict[ind])] - network = replaceinds(network, sim_dict) - return Tuple([sim_dict[ind]]) - end - ind1 = embed(tree[1]) - ind2 = embed(tree[2]) - deltas, splitinds, tnets_dict[tree[1]] = insert_deltas(ind1, ind2, tnets_dict[tree[1]]) - network = Vector{ITensor}(vcat(network, deltas)) - # use mincut to get the subnetwork - subnetwork = mincut_subnetwork(network, splitinds, noncommoninds(network...)) - subsplitinds = intersect(splitinds, noncommoninds(subnetwork...)) - remaininds = collect(setdiff(noncommoninds(subnetwork...), subsplitinds)) - network = collect(setdiff(network, subnetwork)) - # remaininds - deltas, subnetwork, _ = split_deltas(remaininds, subnetwork) - network = vcat(network, deltas) - # subsplitinds - inds = collect(setdiff(splitinds, subsplitinds)) - if length(inds) > 0 - inds = Vector{Index}(inds) - deltas, network, _ = split_deltas(inds, network) - subnetwork = vcat(subnetwork, deltas) + network = Vector{ITensor}(vcat(deltas, networkprime)) + source_subnetwork = mincut_subnetwork(network, source_inds, out_inds) + remain_network = setdiff(network, source_subnetwork) + source_subnetwork = simplify_deltas(source_subnetwork) + remain_network = simplify_deltas(remain_network) + @assert ( + length(noncommoninds(network...)) == + length(noncommoninds(source_subnetwork..., remain_network...)) + ) + return source_subnetwork, remain_network +end + +function tree_embedding(network::Vector{ITensor}, inds_btree::Vector; algorithm) + btree_to_output_tn = Dict{Vector,Vector{ITensor}}() + btree_to_input_tn = Dict{Vector,Vector{ITensor}}() + btree_to_input_tn[inds_btree] = network + nodes = reverse(topo_sort(inds_btree; type=Vector{<:Vector})) + nodes = [nodes..., get_leaves(inds_btree)...] + for node in nodes + @assert haskey(btree_to_input_tn, node) + input_tn = btree_to_input_tn[node] + # @info "node", node + if length(node) == 1 + btree_to_output_tn[node] = input_tn + continue end - # @info "$(tree), $(TreeTensor(subnetwork...))" - tnets_dict[tree] = subnetwork - return Tuple(setdiff(noncommoninds(subnetwork...), splitinds)) + net1, input_tn = mincut_subnetwork_insert_deltas(input_tn, vectorize(node[1])) + btree_to_input_tn[node[1]] = net1 + net1, input_tn = mincut_subnetwork_insert_deltas(input_tn, vectorize(node[2])) + btree_to_input_tn[node[2]] = net1 + btree_to_output_tn[node] = input_tn + # @info "btree_to_output_tn[node]", btree_to_output_tn[node] + end + if algorithm == "svd" + return btree_to_output_tn + else + return remove_deltas(btree_to_output_tn) end - @assert (length(inds_btree) >= 2) - embed(inds_btree) - return remove_deltas(tnets_dict) end is_delta(t) = (t.tensor.storage.data == 1.0) +function simplify_deltas(network::Vector{ITensor}) + out_delta_inds = Vector{Pair}() + # outinds will always be the roots in union-find + outinds = noncommoninds(network...) + deltas = filter(t -> is_delta(t), network) + inds_list = map(t -> collect(inds(t)), deltas) + deltainds = collect(Set(vcat(inds_list...))) + uf = UF(deltainds) + for t in deltas + i1, i2 = inds(t) + if root(uf, i1) in outinds && root(uf, i2) in outinds + push!(out_delta_inds, root(uf, i1) => root(uf, i2)) + end + if root(uf, i1) in outinds + connect(uf, i2, i1) + else + connect(uf, i1, i2) + end + end + sim_dict = Dict([ind => root(uf, ind) for ind in deltainds]) + network = setdiff(network, deltas) + network = replaceinds(network, sim_dict) + out_delta = [delta(i.first, i.second) for i in out_delta_inds] + return Vector{ITensor}([network..., out_delta...]) +end + # remove deltas to improve the performance -function remove_deltas(tnets_dict) +function remove_deltas(tnets_dict::Dict) # only remove deltas in intermediate nodes ks = filter(k -> (length(k) > 1), collect(keys(tnets_dict))) network = vcat([tnets_dict[k] for k in ks]...) @@ -103,17 +145,3 @@ function split_deltas(inds, subnet) subnet = replaceinds(subnet, sim_dict) return deltas, subnet, collect(values(sim_dict)) end - -function insert_deltas(ind1, ind2, subnet1) - intersect_inds = intersect(ind1, ind2) - ind1_unique = collect(setdiff(ind1, intersect_inds)) - ind2_unique = collect(setdiff(ind2, intersect_inds)) - outinds = vcat(ind1_unique, ind2_unique) - # look at intersect_inds - deltas = [] - if length(intersect_inds) >= 1 - deltas, subnet1, siminds = split_deltas(intersect_inds, subnet1) - outinds = vcat(outinds, intersect_inds, siminds) - end - return deltas, outinds, subnet1 -end diff --git a/src/ApproximateTNContraction/contract/tree_embedding_.jl b/src/ApproximateTNContraction/contract/tree_embedding_.jl new file mode 100644 index 00000000..a6363c77 --- /dev/null +++ b/src/ApproximateTNContraction/contract/tree_embedding_.jl @@ -0,0 +1,119 @@ +include("union_find.jl") + +function tree_embedding(network::Vector{OrthogonalITensor}, inds_btree::Vector; algorithm) + # TODO: consider identity matrices + tensor_to_ortho_tensor = Dict{ITensor,OrthogonalITensor}() + for ortho_tensor in network + tensor_to_ortho_tensor[ortho_tensor.tensor] = ortho_tensor + end + tnets_dict = tree_embedding(get_tensors(network), inds_btree) + ortho_tnets_dict = Dict() + for (key, tensors) in tnets_dict + ortho_tensors = Vector{OrthogonalITensor}() + for t in tensors + if haskey(tensor_to_ortho_tensor, t) + push!(ortho_tensors, tensor_to_ortho_tensor[t]) + else + push!(ortho_tensors, OrthogonalITensor(t)) + end + end + ortho_tnets_dict[key] = ortho_tensors + end + return ortho_tnets_dict +end + +function tree_embedding(network::Vector{ITensor}, inds_btree::Vector) + deltas, networkprime, _ = split_deltas(noncommoninds(network...), network) + network = vcat(deltas, networkprime) + # tnets_dict map each inds_btree node to a tensor network + tnets_dict = Dict() + function embed(tree::Vector) + if length(tree) == 1 + # add delta to handle the case with two output edges neighboring to one tensor + # being split (MPS case with element grouping) + ind = tree[1] + sim_dict = Dict([ind => sim(ind)]) + tnets_dict[tree] = [delta(ind, sim_dict[ind])] + network = replaceinds(network, sim_dict) + return Tuple([sim_dict[ind]]) + end + ind1 = embed(tree[1]) + ind2 = embed(tree[2]) + deltas, splitinds, tnets_dict[tree[1]] = insert_deltas(ind1, ind2, tnets_dict[tree[1]]) + network = Vector{ITensor}(vcat(network, deltas)) + # use mincut to get the subnetwork + subnetwork = mincut_subnetwork(network, splitinds, noncommoninds(network...)) + subsplitinds = intersect(splitinds, noncommoninds(subnetwork...)) + remaininds = collect(setdiff(noncommoninds(subnetwork...), subsplitinds)) + network = collect(setdiff(network, subnetwork)) + # remaininds + deltas, subnetwork, _ = split_deltas(remaininds, subnetwork) + network = vcat(network, deltas) + # subsplitinds + inds = collect(setdiff(splitinds, subsplitinds)) + if length(inds) > 0 + inds = Vector{Index}(inds) + deltas, network, _ = split_deltas(inds, network) + subnetwork = vcat(subnetwork, deltas) + end + # @info "$(tree), $(TreeTensor(subnetwork...))" + tnets_dict[tree] = subnetwork + return Tuple(setdiff(noncommoninds(subnetwork...), splitinds)) + end + @assert (length(inds_btree) >= 2) + embed(inds_btree) + return remove_deltas(tnets_dict) +end + +is_delta(t) = (t.tensor.storage.data == 1.0) + +# remove deltas to improve the performance +function remove_deltas(tnets_dict) + # only remove deltas in intermediate nodes + ks = filter(k -> (length(k) > 1), collect(keys(tnets_dict))) + network = vcat([tnets_dict[k] for k in ks]...) + # outinds will always be the roots in union-find + outinds = noncommoninds(network...) + + deltas = filter(t -> is_delta(t), network) + inds_list = map(t -> collect(inds(t)), deltas) + deltainds = collect(Set(vcat(inds_list...))) + uf = UF(deltainds) + for t in deltas + i1, i2 = inds(t) + if root(uf, i1) in outinds + connect(uf, i2, i1) + else + connect(uf, i1, i2) + end + end + sim_dict = Dict([ind => root(uf, ind) for ind in deltainds]) + for k in ks + net = tnets_dict[k] + net = setdiff(net, deltas) + tnets_dict[k] = replaceinds(net, sim_dict) + # @info "$(k), $(TreeTensor(net...))" + end + return tnets_dict +end + +function split_deltas(inds, subnet) + sim_dict = Dict([ind => sim(ind) for ind in inds]) + deltas = [delta(i, sim_dict[i]) for i in inds] + subnet = replaceinds(subnet, sim_dict) + return deltas, subnet, collect(values(sim_dict)) +end + +function insert_deltas(ind1, ind2, subnet1) + intersect_inds = intersect(ind1, ind2) + ind1_unique = collect(setdiff(ind1, intersect_inds)) + ind2_unique = collect(setdiff(ind2, intersect_inds)) + outinds = vcat(ind1_unique, ind2_unique) + # look at intersect_inds + deltas = [] + if length(intersect_inds) >= 1 + deltas, subnet1, siminds = split_deltas(intersect_inds, subnet1) + outinds = vcat(outinds, intersect_inds, siminds) + end + return deltas, outinds, subnet1 +end diff --git a/src/treetensornetworks/abstracttreetensornetwork.jl b/src/treetensornetworks/abstracttreetensornetwork.jl index 4b4e977e..ce305c94 100644 --- a/src/treetensornetworks/abstracttreetensornetwork.jl +++ b/src/treetensornetworks/abstracttreetensornetwork.jl @@ -114,7 +114,7 @@ end # function truncate( - ψ::AbstractTTN; root_vertex::Tuple=default_root_vertex(ψ), kwargs... + ψ::AbstractTTN; root_vertex::Union{Int,Tuple}=default_root_vertex(ψ), kwargs... ) for e in post_order_dfs_edges(ψ, root_vertex) # always orthogonalize towards source first to make truncations controlled diff --git a/test/approximate_tn_contraction/contract.jl b/test/approximate_tn_contraction/contract.jl index 419941de..5bc3e75b 100644 --- a/test/approximate_tn_contraction/contract.jl +++ b/test/approximate_tn_contraction/contract.jl @@ -2,7 +2,7 @@ using ITensors, TimerOutputs using ITensorNetworks.ApproximateTNContraction: get_tensors, OrthogonalITensor, - tree_approximation_cache, + tree_approximation, inds_binary_tree, tree_embedding, approximate_contract @@ -46,7 +46,7 @@ include("utils.jl") [[[i], [j]], [[k], [l]]] => [ABCD], [[[[i], [j]], [[k], [l]]], [m]] => [ABCDE], ]) - out, log_norm = tree_approximation_cache(embedding, btree) + out, log_norm = tree_approximation(embedding, btree; algorithm="svd") out[btree].tensor *= exp(log_norm) out = get_tensors(collect(values(out))) @test isapprox(contract(out...), contract(get_tensors(tensors)...)) @@ -126,7 +126,7 @@ end network = M[:] out1 = contract(network...) inds_btree = inds_binary_tree(network, [i, j, k, l, m]; algorithm="mincut") - tnet_dict = tree_embedding(network, inds_btree) + tnet_dict = tree_embedding(network, inds_btree; algorithm="mincut") network2 = vcat(collect(values(tnet_dict))...) out2 = contract(network2...) i1 = noncommoninds(network...) From d3592b643e3fd866ec1224bd5c7d06aa2ff8e861 Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Mon, 30 Jan 2023 14:45:45 -0600 Subject: [PATCH 23/24] General ising_network with external force --- examples/approximate_contract/3dcube.jl | 143 +++++++++++++++++------- src/specialitensornetworks.jl | 49 +++++++- 2 files changed, 148 insertions(+), 44 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index 1c88b3dc..b416fae1 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -1,4 +1,5 @@ using ITensors, TimerOutputs +using KaHyPar using ITensorNetworks using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network using ITensorNetworks.ApproximateTNContraction: @@ -27,17 +28,17 @@ function contract_log_norm(tn, seq) end end -function exact_contract(N) +function exact_contract(N; beta) ITensors.set_warn_order(1000) reset_timer!(timer) linkdim = 2 - network = ising_network(named_grid(N), 0.3) + network = ising_network(named_grid(N), beta) tn = Array{ITensor,length(N)}(undef, N...) for v in vertices(network) tn[v...] = network[v...] end tn = vec(tn) - seq = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=36) + seq = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=20) @info seq tn = [(i, 0.0) for i in tn] return contract_log_norm(tn, seq) @@ -108,46 +109,50 @@ function build_recursive_tntree(tn, N; env_size) ] end -function bench_3d_cube( - N; num_iter, cutoff, maxdim, ansatz, algorithm, snake, use_cache, ortho, env_size -) +# if ortho == true +# @info "orthogonalize tn towards the first vertex" +# itn = ITensorNetwork(named_grid(N); link_space=2) +# for i in 1:N[1] +# for j in 1:N[2] +# for k in 1:N[3] +# itn[i, j, k] = tn[i, j, k] +# end +# end +# end +# itn = orthogonalize(itn, (1, 1, 1)) +# @info itn[1, 1, 1] +# @info itn[1, 1, 1].tensor +# for i in 1:N[1] +# for j in 1:N[2] +# for k in 1:N[3] +# tn[i, j, k] = itn[i, j, k] +# end +# end +# end +# end +function build_tntree(N; beta, h, snake, env_size, szverts) + @info "beta is", beta ITensors.set_warn_order(100) - reset_timer!(timer) - network = ising_network(named_grid(N), 0.3) + network = ising_network(named_grid(N), beta, h; szverts=szverts) tn = Array{ITensor,length(N)}(undef, N...) for v in vertices(network) tn[v...] = network[v...] end - # if ortho == true - # @info "orthogonalize tn towards the first vertex" - # itn = ITensorNetwork(named_grid(N); link_space=2) - # for i in 1:N[1] - # for j in 1:N[2] - # for k in 1:N[3] - # itn[i, j, k] = tn[i, j, k] - # end - # end - # end - # itn = orthogonalize(itn, (1, 1, 1)) - # @info itn[1, 1, 1] - # @info itn[1, 1, 1].tensor - # for i in 1:N[1] - # for j in 1:N[2] - # for k in 1:N[3] - # tn[i, j, k] = itn[i, j, k] - # end - # end - # end - # end if snake == true for k in 1:N[3] rangej = iseven(k) ? reverse(1:N[2]) : 1:N[2] tn[:, rangej, k] = tn[:, 1:N[2], k] end end - tntree = build_tntree(tn, N; env_size=env_size) - out_list = [] - for _ in 1:num_iter + return build_tntree(tn, N; env_size=env_size) +end + +function bench_3d_cube_lnZ( + N; beta, h, num_iter, cutoff, maxdim, ansatz, algorithm, snake, use_cache, ortho, env_size +) + reset_timer!(timer) + tntree = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=nothing) + function _run() out, log_acc_norm = approximate_contract( tntree; cutoff=cutoff, @@ -158,14 +163,43 @@ function bench_3d_cube( orthogonalize=ortho, ) @info "out is", log(out[1][1]) + log_acc_norm - push!(out_list, log(out[1][1]) + log_acc_norm) + return log(out[1][1]) + log_acc_norm + end + out_list = [] + for _ in 1:num_iter + push!(out_list, _run()) end show(timer) # after warmup, start to benchmark reset_timer!(timer) for _ in 1:num_iter + push!(out_list, _run()) + end + @info "lnZ results are", out_list, "mean is", sum(out_list) / (num_iter * 2) + return show(timer) +end + +function bench_3d_cube_magnetization( + N; + beta, + h, + num_iter, + cutoff, + maxdim, + ansatz, + algorithm, + snake, + use_cache, + ortho, + env_size, + szverts, +) + reset_timer!(timer) + tntree1 = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=szverts) + tntree2 = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=nothing) + function _run() out, log_acc_norm = approximate_contract( - tntree; + tntree1; cutoff=cutoff, maxdim=maxdim, ansatz=ansatz, @@ -173,24 +207,47 @@ function bench_3d_cube( use_cache=use_cache, orthogonalize=ortho, ) - @info "out is", log(out[1][1]) + log_acc_norm - push!(out_list, log(out[1][1]) + log_acc_norm) + lognorm1 = log(out[1][1]) + log_acc_norm + out, log_acc_norm = approximate_contract( + tntree2; + cutoff=cutoff, + maxdim=maxdim, + ansatz=ansatz, + algorithm=algorithm, + use_cache=use_cache, + orthogonalize=ortho, + ) + lognorm2 = log(out[1][1]) + log_acc_norm + return lognorm1 / lognorm2 end - @info "lnZ results are", out_list, "mean is", sum(out_list) / (num_iter * 2) + out_list = [] + for _ in 1:num_iter + push!(out_list, _run()) + end + show(timer) + # after warmup, start to benchmark + reset_timer!(timer) + for _ in 1:num_iter + push!(out_list, _run()) + end + @info "magnetization results are", out_list, "mean is", sum(out_list) / (num_iter * 2) return show(timer) end -# exact_contract((5, 5, 5)) +# exact_contract((1, 20, 20); beta=0.44) # TODO: (6, 6, 6), env_size=(2, 1, 1) is buggy (cutoff=1e-12, maxdim=256, ansatz="comb", algorithm="density_matrix",) -@time bench_3d_cube( - (6, 6, 6); +@time bench_3d_cube_magnetization( + (1, 6, 6); + beta=0.44, + h=0.0001, num_iter=2, - cutoff=1e-12, - maxdim=320, + cutoff=1e-20, + maxdim=64, ansatz="mps", algorithm="density_matrix", snake=false, use_cache=true, ortho=false, - env_size=(3, 1, 1), + env_size=(1, 5, 1), + szverts=[(1, 3, 3)], ) diff --git a/src/specialitensornetworks.jl b/src/specialitensornetworks.jl index cd560d76..7af00894 100644 --- a/src/specialitensornetworks.jl +++ b/src/specialitensornetworks.jl @@ -1,3 +1,5 @@ +using LinearAlgebra: diagm + """ RETURN A TENSOR NETWORK WITH COPY TENSORS ON EACH VERTEX. Note that passing a link_space will mean the indices of the resulting network don't match those of the input indsnetwork @@ -49,6 +51,39 @@ function ising_network(eltype::Type, s::IndsNetwork, beta::Number; szverts=nothi return ψ end +function ising_network( + eltype::Type, s::IndsNetwork, beta::Number, h::Number; szverts=nothing +) + tn = delta_network(eltype, s) + if (szverts != nothing) + for v in szverts + tn[v] = diagITensor(eltype[1, -1], inds(tn[v])) + end + end + for edge in edges(tn) + v1 = src(edge) + v2 = dst(edge) + i = commoninds(tn[v1], tn[v2])[1] + deg_v1 = degree(tn, v1) + deg_v2 = degree(tn, v2) + f11 = exp(beta + h / deg_v1 + h / deg_v2) + f12 = exp(-beta + h / deg_v1 - h / deg_v2) + f21 = exp(-beta - h / deg_v1 + h / deg_v2) + f22 = exp(beta - h / deg_v1 - h / deg_v2) + q = eltype[f11 f12; f21 f22] + w, V = eigen(q) + w = map(sqrt, w) + sqrt_q = V * diagm(w) * inv(V) + t = itensor(sqrt_q, i, i') + tn[v1] = tn[v1] * t + tn[v1] = noprime!(tn[v1]) + t = itensor(sqrt_q, i', i) + tn[v2] = tn[v2] * t + tn[v2] = noprime!(tn[v2]) + end + return tn +end + function ising_network(s::IndsNetwork, beta::Number; szverts=nothing) return ising_network(typeof(beta), s, beta; szverts) end @@ -62,10 +97,20 @@ function ising_network(eltype::Type, g::NamedGraph, beta::Number; szverts=nothin return ising_network(eltype, IndsNetwork(g; link_space=2), beta; szverts) end +function ising_network( + eltype::Type, g::NamedGraph, beta::Number, h::Number; szverts=nothing +) + return ising_network(eltype, IndsNetwork(g; link_space=2), beta, h; szverts) +end + function ising_network(g::NamedGraph, beta::Number; szverts=nothing) return ising_network(eltype(beta), g, beta; szverts) end +function ising_network(g::NamedGraph, beta::Number, h::Number; szverts=nothing) + return ising_network(eltype(beta), g, beta, h; szverts) +end + """ Build an ITensor network on a graph specified by the inds network s. Bond_dim is given by link_space and entries are randomised (normal distribution, mean 0 std 1) """ @@ -79,7 +124,9 @@ function randomITensorNetwork(s::IndsNetwork; link_space=nothing) return randomITensorNetwork(Float64, s; link_space) end -@traitfn function randomITensorNetwork(eltype::Type, g::::IsUnderlyingGraph; link_space=nothing) +@traitfn function randomITensorNetwork( + eltype::Type, g::::IsUnderlyingGraph; link_space=nothing +) return randomITensorNetwork(eltype, IndsNetwork(g); link_space) end From 71215d9a60202afa5b9d18112f33afbf0a12757c Mon Sep 17 00:00:00 2001 From: LinjianMa Date: Wed, 1 Feb 2023 20:54:13 -0600 Subject: [PATCH 24/24] Update 3dcube.jl --- examples/approximate_contract/3dcube.jl | 151 +++++++++++++++++++++--- 1 file changed, 132 insertions(+), 19 deletions(-) diff --git a/examples/approximate_contract/3dcube.jl b/examples/approximate_contract/3dcube.jl index b416fae1..e0675ac2 100644 --- a/examples/approximate_contract/3dcube.jl +++ b/examples/approximate_contract/3dcube.jl @@ -1,7 +1,7 @@ -using ITensors, TimerOutputs +using ITensors, TimerOutputs, Graphs using KaHyPar using ITensorNetworks -using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network +using ITensorNetworks: contraction_sequence, ITensorNetwork, ising_network, vertex_tag using ITensorNetworks.ApproximateTNContraction: approximate_contract, line_to_tree, timer, line_network @@ -28,7 +28,7 @@ function contract_log_norm(tn, seq) end end -function exact_contract(N; beta) +function exact_contract(N; beta, sc_target) ITensors.set_warn_order(1000) reset_timer!(timer) linkdim = 2 @@ -38,7 +38,7 @@ function exact_contract(N; beta) tn[v...] = network[v...] end tn = vec(tn) - seq = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=20) + seq = contraction_sequence(tn; alg="kahypar_bipartite", sc_target=sc_target) @info seq tn = [(i, 0.0) for i in tn] return contract_log_norm(tn, seq) @@ -130,7 +130,7 @@ end # end # end # end -function build_tntree(N; beta, h, snake, env_size, szverts) +function build_tntree(N; block_size, beta, h, snake, env_size, szverts) @info "beta is", beta ITensors.set_warn_order(100) network = ising_network(named_grid(N), beta, h; szverts=szverts) @@ -144,14 +144,77 @@ function build_tntree(N; beta, h, snake, env_size, szverts) tn[:, rangej, k] = tn[:, 1:N[2], k] end end - return build_tntree(tn, N; env_size=env_size) + if block_size == (1, 1, 1) + return build_tntree(tn, N; env_size=env_size) + end + tn_reduced = ITensorNetwork() + reduced_N = ( + ceil(Int, N[1] / block_size[1]), + ceil(Int, N[2] / block_size[2]), + ceil(Int, N[3] / block_size[3]), + ) + for i in 1:reduced_N[1] + for j in 1:reduced_N[2] + for k in 1:reduced_N[3] + add_vertex!(tn_reduced, (i, j, k)) + ii = (i - 1) * block_size[1] + jj = (j - 1) * block_size[2] + kk = (k - 1) * block_size[3] + ii_end = min(ii + block_size[1], N[1]) + jj_end = min(jj + block_size[2], N[2]) + kk_end = min(kk + block_size[3], N[3]) + tn_reduced[(i, j, k)] = ITensors.contract( + tn[(ii + 1):ii_end, (jj + 1):jj_end, (kk + 1):kk_end]... + ) + end + end + end + for e in edges(tn_reduced) + v1, v2 = e.src, e.dst + C = combiner( + commoninds(tn_reduced[v1], tn_reduced[v2])...; + tags="$(vertex_tag(v1))↔$(vertex_tag(v2))", + ) + tn_reduced[v1] = tn_reduced[v1] * C + tn_reduced[v2] = tn_reduced[v2] * C + end + network_reduced = Array{ITensor,3}(undef, reduced_N...) + for v in vertices(tn_reduced) + network_reduced[v...] = tn_reduced[v...] + end + reduced_env = ( + ceil(Int, env_size[1] / block_size[1]), + ceil(Int, env_size[2] / block_size[2]), + ceil(Int, env_size[3] / block_size[3]), + ) + return build_tntree(network_reduced, reduced_N; env_size=reduced_env) end function bench_3d_cube_lnZ( - N; beta, h, num_iter, cutoff, maxdim, ansatz, algorithm, snake, use_cache, ortho, env_size + N; + block_size, + beta, + h, + num_iter, + cutoff, + maxdim, + ansatz, + algorithm, + snake, + use_cache, + ortho, + env_size, ) reset_timer!(timer) - tntree = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=nothing) + tntree = build_tntree( + N; + block_size=block_size, + beta=beta, + h=h, + snake=snake, + env_size=env_size, + szverts=nothing, + ) function _run() out, log_acc_norm = approximate_contract( tntree; @@ -181,6 +244,7 @@ end function bench_3d_cube_magnetization( N; + block_size, beta, h, num_iter, @@ -195,8 +259,24 @@ function bench_3d_cube_magnetization( szverts, ) reset_timer!(timer) - tntree1 = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=szverts) - tntree2 = build_tntree(N; beta=beta, h=h, snake=snake, env_size=env_size, szverts=nothing) + tntree1 = build_tntree( + N; + block_size=block_size, + beta=beta, + h=h, + snake=snake, + env_size=env_size, + szverts=szverts, + ) + tntree2 = build_tntree( + N; + block_size=block_size, + beta=beta, + h=h, + snake=snake, + env_size=env_size, + szverts=nothing, + ) function _run() out, log_acc_norm = approximate_contract( tntree1; @@ -234,20 +314,53 @@ function bench_3d_cube_magnetization( return show(timer) end -# exact_contract((1, 20, 20); beta=0.44) +# exact_contract((4, 4, 10); beta=0.3, sc_target=28) # TODO: (6, 6, 6), env_size=(2, 1, 1) is buggy (cutoff=1e-12, maxdim=256, ansatz="comb", algorithm="density_matrix",) -@time bench_3d_cube_magnetization( - (1, 6, 6); - beta=0.44, - h=0.0001, +# TODO below is buggy +# @time bench_3d_cube_lnZ( +# (3, 8, 10); +# use_2D=false, +# beta=0.3, +# h=0.0, +# num_iter=2, +# cutoff=1e-20, +# maxdim=128, +# ansatz="mps", +# algorithm="density_matrix", +# snake=false, +# use_cache=true, +# ortho=false, +# env_size=(3, 1, 1), +# ) +@time bench_3d_cube_lnZ( + (6, 6, 6); + block_size=(6, 1, 1), + beta=0.3, + h=0.0, num_iter=2, - cutoff=1e-20, - maxdim=64, + cutoff=1e-12, + maxdim=128, ansatz="mps", algorithm="density_matrix", snake=false, use_cache=true, ortho=false, - env_size=(1, 5, 1), - szverts=[(1, 3, 3)], + env_size=(6, 1, 1), ) + +# @time bench_3d_cube_magnetization( +# (1, 6, 6); +# use_2D=true, +# beta=0.44, +# h=0.0001, +# num_iter=2, +# cutoff=1e-20, +# maxdim=64, +# ansatz="mps", +# algorithm="density_matrix", +# snake=false, +# use_cache=true, +# ortho=false, +# env_size=(1, 6, 1), +# szverts=[(1, 3, 3)], +# )