From c8459471e67c4163539808b8d432032bdde93838 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 13 Sep 2024 10:19:49 -0400 Subject: [PATCH 01/47] Blah --- examples/test.jl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/test.jl diff --git a/examples/test.jl b/examples/test.jl new file mode 100644 index 00000000..6bf1bfcf --- /dev/null +++ b/examples/test.jl @@ -0,0 +1,21 @@ +using ITensorNetworks: IndsNetwork, siteinds, ttn +using ITensorNetworks.ModelHamiltonians: ising +using ITensors: Index, OpSum, terms, sites +using NamedGraphs.NamedGraphGenerators: named_grid +using NamedGraphs.GraphsExtensions: rem_vertex + +function filter_terms(H, verts) + H_new = OpSum() + for term in terms(H) + if isempty(filter(v -> v ∈ verts, sites(term))) + H_new += term + end + end + return H_new +end + +g = named_grid((8,1)) +s = siteinds("S=1/2", g) +H = ising(s) +H_mod = filter_terms(H, [(4,1)]) +ttno = ttn(H_mod, s) \ No newline at end of file From 6ff0cd572c947e9b1ed3642e690b43233277beb0 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 17 Oct 2024 14:56:22 +0100 Subject: [PATCH 02/47] Bug fix in current ortho. Change test --- .../alternating_update/region_update.jl | 45 ++++++++----------- .../test_solvers/test_dmrg.jl | 12 ++--- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/solvers/alternating_update/region_update.jl b/src/solvers/alternating_update/region_update.jl index b92adc8c..97241c20 100644 --- a/src/solvers/alternating_update/region_update.jl +++ b/src/solvers/alternating_update/region_update.jl @@ -7,36 +7,27 @@ function current_ortho(sweep_plan, which_region_update) if !isa(region, AbstractEdge) && length(region) == 1 return only(current_verts) end - if which_region_update == length(regions) - # look back by one should be sufficient, but may be brittle? - overlapping_vertex = only( - intersect(current_verts, support(regions[which_region_update - 1])) - ) - return overlapping_vertex - else - # look forward - other_regions = filter( - x -> !(issetequal(x, current_verts)), support.(regions[(which_region_update + 1):end]) + # look forward + other_regions = filter( + x -> !(issetequal(x, current_verts)), support.(regions[(which_region_update + 1):end]) + ) + # find the first region that has overlapping support with current region + ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) + if isnothing(ind) + # look backward + other_regions = reverse( + filter( + x -> !(issetequal(x, current_verts)), support.(regions[1:(which_region_update - 1)]) + ), ) - # find the first region that has overlapping support with current region ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - if isnothing(ind) - # look backward - other_regions = reverse( - filter( - x -> !(issetequal(x, current_verts)), - support.(regions[1:(which_region_update - 1)]), - ), - ) - ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - end - @assert !isnothing(ind) - future_verts = union(support(other_regions[ind])) - # return ortho_ceter as the vertex in current region that does not overlap with following one - overlapping_vertex = intersect(current_verts, future_verts) - nonoverlapping_vertex = only(setdiff(current_verts, overlapping_vertex)) - return nonoverlapping_vertex end + @assert !isnothing(ind) + future_verts = union(support(other_regions[ind])) + # return ortho_ceter as the vertex in current region that does not overlap with following one + overlapping_vertex = intersect(current_verts, future_verts) + nonoverlapping_vertex = only(setdiff(current_verts, overlapping_vertex)) + return nonoverlapping_vertex end function region_update( diff --git a/test/test_treetensornetworks/test_solvers/test_dmrg.jl b/test/test_treetensornetworks/test_solvers/test_dmrg.jl index cf8a1caf..004ec561 100644 --- a/test/test_treetensornetworks/test_solvers/test_dmrg.jl +++ b/test/test_treetensornetworks/test_solvers/test_dmrg.jl @@ -1,7 +1,7 @@ @eval module $(gensym()) using DataGraphs: edge_data, vertex_data using Dictionaries: Dictionary -using Graphs: nv, vertices +using Graphs: nv, vertices, uniform_tree using ITensorMPS: ITensorMPS using ITensorNetworks: ITensorNetworks, @@ -19,6 +19,7 @@ using ITensorNetworks.ITensorsExtensions: replace_vertices using ITensorNetworks.ModelHamiltonians: ModelHamiltonians using ITensors: ITensors using KrylovKit: eigsolve +using NamedGraphs: NamedGraph, rename_vertices using NamedGraphs.NamedGraphGenerators: named_comb_tree using Observers: observer using StableRNGs: StableRNG @@ -313,11 +314,12 @@ end nsites = 2 nsweeps = 10 - c = named_comb_tree((3, 2)) - s = siteinds("S=1/2", c) - os = ModelHamiltonians.heisenberg(c) - H = ttn(os, s) rng = StableRNG(1234) + g = NamedGraph(uniform_tree(10)) + g = rename_vertices(v -> (v, 1), g) + s = siteinds("S=1/2", g) + os = ModelHamiltonians.heisenberg(g) + H = ttn(os, s) psi = random_ttn(rng, s; link_space=5) e, psi = dmrg(H, psi; nsweeps, maxdim, nsites) From d0967229e2c9c0d645ad110bb3944566b52d3385 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 26 Nov 2024 13:50:41 -0500 Subject: [PATCH 03/47] Fix bug --- src/abstractitensornetwork.jl | 40 ++++++++++++------- .../abstracttreetensornetwork.jl | 12 ++++-- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/abstractitensornetwork.jl b/src/abstractitensornetwork.jl index fc0edce4..afdbbb41 100644 --- a/src/abstractitensornetwork.jl +++ b/src/abstractitensornetwork.jl @@ -19,6 +19,7 @@ using Graphs: using ITensors: ITensors, ITensor, + @Algorithm_str, addtags, combiner, commoninds, @@ -44,7 +45,7 @@ using MacroTools: @capture using NamedGraphs: NamedGraphs, NamedGraph, not_implemented, steiner_tree using NamedGraphs.GraphsExtensions: ⊔, directed_graph, incident_edges, rename_vertices, vertextype -using NDTensors: NDTensors, dim +using NDTensors: NDTensors, dim, Algorithm using SplitApplyCombine: flatten abstract type AbstractITensorNetwork{V} <: AbstractDataGraph{V,ITensor,ITensor} end @@ -585,17 +586,22 @@ function LinearAlgebra.factorize(tn::AbstractITensorNetwork, edge::Pair; kwargs. end # For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? -function orthogonalize_walk(tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs...) - return orthogonalize_walk(tn, [edge]; kwargs...) +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs... +) + return gauge_walk(tn, [edge]; kwargs...) end -function orthogonalize_walk(tn::AbstractITensorNetwork, edge::Pair; kwargs...) - return orthogonalize_walk(tn, edgetype(tn)(edge); kwargs...) +function gauge_walk(alg::Algorithm, tn::AbstractITensorNetwork, edge::Pair; kwargs...) + return gauge_walk(alg::Algorithm, tn, edgetype(tn)(edge); kwargs...) end # For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? -function orthogonalize_walk( - tn::AbstractITensorNetwork, edges::Vector{<:AbstractEdge}; kwargs... +function gauge_walk( + alg::Algorithm"orthogonalize", + tn::AbstractITensorNetwork, + edges::Vector{<:AbstractEdge}; + kwargs..., ) # tn = factorize(tn, edge; kwargs...) # # TODO: Implement as `only(common_neighbors(tn, src(edge), dst(edge)))` @@ -612,22 +618,28 @@ function orthogonalize_walk( return tn end -function orthogonalize_walk(tn::AbstractITensorNetwork, edges::Vector{<:Pair}; kwargs...) - return orthogonalize_walk(tn, edgetype(tn).(edges); kwargs...) +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edges::Vector{<:Pair}; kwargs... +) + return gauge_walk(alg, tn, edgetype(tn).(edges); kwargs...) end -# Orthogonalize an ITensorNetwork towards a region, treating +# Gauge a ITensorNetwork towards a region, treating # the network as a tree spanned by a spanning tree. -function tree_orthogonalize(ψ::AbstractITensorNetwork, region::Vector) +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region::Vector) region_center = length(region) != 1 ? first(center(steiner_tree(ψ, region))) : only(region) path = post_order_dfs_edges(bfs_tree(ψ, region_center), region_center) path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) - return orthogonalize_walk(ψ, path) + return gauge_walk(alg, ψ, path) +end + +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region) + return tree_gauge(alg, ψ, [region]) end -function tree_orthogonalize(ψ::AbstractITensorNetwork, region) - return tree_orthogonalize(ψ, [region]) +function tree_orthogonalize(ψ::AbstractITensorNetwork, region; kwargs...) + return tree_gauge(Algorithm("orthogonalize"), ψ, region; kwargs...) end # TODO: decide whether to use graph mutating methods when resulting graph is unchanged? diff --git a/src/treetensornetworks/abstracttreetensornetwork.jl b/src/treetensornetworks/abstracttreetensornetwork.jl index 8815b33f..f6c8f49f 100644 --- a/src/treetensornetworks/abstracttreetensornetwork.jl +++ b/src/treetensornetworks/abstracttreetensornetwork.jl @@ -8,7 +8,7 @@ using NamedGraphs.GraphsExtensions: a_star using NamedGraphs: namedgraph_a_star, steiner_tree using IsApprox: IsApprox, Approx -using ITensors: ITensors, @Algorithm_str, directsum, hasinds, permute, plev +using ITensors: ITensors, Algorithm, @Algorithm_str, directsum, hasinds, permute, plev using ITensorMPS: ITensorMPS, linkind, loginner, lognorm, orthogonalize using TupleTools: TupleTools @@ -35,19 +35,23 @@ function set_ortho_region(tn::AbstractTTN, new_region) return error("Not implemented") end -function ITensorMPS.orthogonalize(ttn::AbstractTTN, region::Vector; kwargs...) +function gauge(alg::Algorithm, ttn::AbstractTTN, region::Vector; kwargs...) issetequal(region, ortho_region(ttn)) && return ttn st = steiner_tree(ttn, union(region, ortho_region(ttn))) path = post_order_dfs_edges(st, first(region)) path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) if !isempty(path) - ttn = typeof(ttn)(orthogonalize_walk(ITensorNetwork(ttn), path; kwargs...)) + ttn = typeof(ttn)(gauge_walk(alg, ITensorNetwork(ttn), path; kwargs...)) end return set_ortho_region(ttn, region) end +function gauge(alg::Algorithm, ttn::AbstractTTN, region; kwargs...) + return gauge(alg, ttn, [region]; kwargs...) +end + function ITensorMPS.orthogonalize(ttn::AbstractTTN, region; kwargs...) - return orthogonalize(ttn, [region]; kwargs...) + return gauge(Algorithm("orthogonalize"), ttn, region; kwargs...) end function tree_orthogonalize(ttn::AbstractTTN, args...; kwargs...) From 921810df73c6b3e58a8d90e8a13754c640ca9cee Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 18 Dec 2024 14:24:36 -0500 Subject: [PATCH 04/47] Save First Run --- examples/test_boundarymps.jl | 19 +++++++++++++ src/ITensorNetworks.jl | 1 + src/caches/boundarympscache.jl | 51 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 examples/test_boundarymps.jl create mode 100644 src/caches/boundarympscache.jl diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl new file mode 100644 index 00000000..f8b2631d --- /dev/null +++ b/examples/test_boundarymps.jl @@ -0,0 +1,19 @@ +using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, + partitionedplanargraph, gauges, partitionedges, messages, update +using ITensorNetworks.ModelHamiltonians: ising +using ITensors: Index, OpSum, terms, sites +using NamedGraphs: NamedEdge +using NamedGraphs.NamedGraphGenerators: named_grid +using NamedGraphs.GraphsExtensions: rem_vertex +using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, PartitionEdge + +L = 3 +g = named_grid((L,L)) +s = siteinds("S=1/2", g) +ψ = random_tensornetwork(s; link_space = 2) +ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) +ψIψ = BoundaryMPSCache(ψIψ) + +#@show PartitionEdge(NamedEdge(1 => 2)) +#@show PartitionEdge(1 => 2) +#@show partitionedges(ψIψ, PartitionEdge(NamedEdge(2 => 1))) \ No newline at end of file diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 28da183a..b7d56fa7 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -29,6 +29,7 @@ include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") include("caches/beliefpropagationcache.jl") +include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl new file mode 100644 index 00000000..84f46a06 --- /dev/null +++ b/src/caches/boundarympscache.jl @@ -0,0 +1,51 @@ +using NamedGraphs: NamedGraphs +using ITensorNetworks: ITensorNetworks +using NamedGraphs.PartitionedGraphs: partitioned_graph + +struct BoundaryMPSCache{BPC,G, D} + bp_cache::BPC + gauges::G + partitionedvertices::D +end + +bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache +gauges(bmpsc::BoundaryMPSCache) = bmpsc.gauges +partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph + +function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) + planar_graph = partitioned_graph(bpc) + vertex_groups = group(sort_f, collect(vertices(planar_graph))) + gauges = Dictionary(keys(vertex_groups), ["Nothing" for pv in keys(vertex_groups)]) + return BoundaryMPSCache(bpc, gauges, vertex_groups) +end + +#Get all partitionedges within a column / row partition +function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex) + pg = partitionedplanargraph(bmpsc) + vs = sort(vertices(pg, pv)) + return PartitionEdge.([vs[i] => vs[i+1] for i in 1:length(vs)]) +end + +# #Get all partitionedges between rows/ columns +function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pg = partitionedplanargraph(bmpsc) + pv_src_verts, pv_dst_verts = vertices(pg, src(pe)), vertices(pg, dst(pe)) + es = vcat(edges(pg), reverse.(edges(pg))) + return PartitionEdge.(filter(e -> src(e) ∈ pv_src_verts && dst(e) ∈ pv_dst_verts, es)) +end + +# function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex) + +#Needed for implementation, forward from beliefpropagationcache +for f in [ + :messages, + :message, + :update_message, + :update, + ] + @eval begin + function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) + return $f(bp_cache(bmpsc), args...; kwargs...) + end + end +end \ No newline at end of file From c1c1f948a1f7a365a9aa8060a094c967e4639377 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 18 Dec 2024 17:51:39 -0500 Subject: [PATCH 05/47] Preliminary ideas --- examples/test_boundarymps.jl | 4 +-- src/caches/boundarympscache.jl | 47 ++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index f8b2631d..fdbf55cb 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,5 +1,5 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, - partitionedplanargraph, gauges, partitionedges, messages, update + partitionedplanargraph, gauges, partitionedges, messages, update, partition_update using ITensorNetworks.ModelHamiltonians: ising using ITensors: Index, OpSum, terms, sites using NamedGraphs: NamedEdge @@ -16,4 +16,4 @@ s = siteinds("S=1/2", g) #@show PartitionEdge(NamedEdge(1 => 2)) #@show PartitionEdge(1 => 2) -#@show partitionedges(ψIψ, PartitionEdge(NamedEdge(2 => 1))) \ No newline at end of file +@show partition_update(ψIψ, (1,1), (1,2)) \ No newline at end of file diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 84f46a06..0066f0e1 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -5,12 +5,19 @@ using NamedGraphs.PartitionedGraphs: partitioned_graph struct BoundaryMPSCache{BPC,G, D} bp_cache::BPC gauges::G - partitionedvertices::D + partitions::D end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache gauges(bmpsc::BoundaryMPSCache) = bmpsc.gauges -partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph +partitions(bmpsc::BoundaryMPSCache) = bmpsc.partitions +planargraph(bmpsc::BoundaryMPSCache) = partitioned_graph(bp_cache(bmpsc)) + +ITensorNetworks.partitionvertices(bmpsc::BoundaryMPSCache, partition::Int64) = partitions(bmpsc)[partition] + +function get_partition(bmpsc::BoundaryMPSCache, v) + return only(filter(pv -> v ∈ partitionvertices(bmpsc, pv), keys(partitions(bmpsc)))) +end function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) planar_graph = partitioned_graph(bpc) @@ -19,22 +26,36 @@ function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) return BoundaryMPSCache(bpc, gauges, vertex_groups) end -#Get all partitionedges within a column / row partition -function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex) - pg = partitionedplanargraph(bmpsc) - vs = sort(vertices(pg, pv)) - return PartitionEdge.([vs[i] => vs[i+1] for i in 1:length(vs)]) +#Get all partitionedges within a partition, sorted top to bottom +function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) + vs = partitionvertices(bmpsc, partition) + return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end # #Get all partitionedges between rows/ columns -function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) - pg = partitionedplanargraph(bmpsc) - pv_src_verts, pv_dst_verts = vertices(pg, src(pe)), vertices(pg, dst(pe)) - es = vcat(edges(pg), reverse.(edges(pg))) - return PartitionEdge.(filter(e -> src(e) ∈ pv_src_verts && dst(e) ∈ pv_dst_verts, es)) +# function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) +# pg = partitionedplanargraph(bmpsc) +# pv_src_verts, pv_dst_verts = vertices(pg, src(pe)), vertices(pg, dst(pe)) +# es = vcat(edges(pg), reverse.(edges(pg))) +# return PartitionEdge.(filter(e -> src(e) ∈ pv_src_verts && dst(e) ∈ pv_dst_verts, es)) +# end + +#Update all messages flowing within a partition +function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) + edges = partitionedges(bmpsc, pv) + return update(bmpsc, vcat(edges, reverse(reverse.(edges))); kwargs...) end -# function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex) +#Update all messages flowing within a partition from v1 to v2 +function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) + pv1, pv2 = get_partition(bmpsc, v1), get_partition(bmpsc, v2) + @assert pv1 == pv2 + vs = sort(partitionvertices(bmpsc, pv1)) + v1_pos, v2_pos = only(findall(x->x==v1, vs)), only(findall(x->x==v2, vs)) + v1_pos < v2_pos && return PartitionEdge.([vs[i] => vs[i+1] for i in v1_pos:(v2_pos-1)]) + es = PartitionEdge.([vs[i] => vs[i-1] for i in v2_pos:(v1_pos+1)]) + return update(bmpsc, es; kwargs...) +end #Needed for implementation, forward from beliefpropagationcache for f in [ From c7ab747281ee05c69b00e3d991b4175ce892d195 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 18 Dec 2024 22:01:42 -0500 Subject: [PATCH 06/47] Save --- src/caches/boundarympscache.jl | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 0066f0e1..6a79fbb9 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -21,7 +21,9 @@ end function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) planar_graph = partitioned_graph(bpc) + #TODO: Make sure these are sorted vertex_groups = group(sort_f, collect(vertices(planar_graph))) + gauges = Dictionary(keys(vertex_groups), ["Nothing" for pv in keys(vertex_groups)]) return BoundaryMPSCache(bpc, gauges, vertex_groups) end @@ -32,29 +34,25 @@ function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, partition::Int6 return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end -# #Get all partitionedges between rows/ columns -# function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) -# pg = partitionedplanargraph(bmpsc) -# pv_src_verts, pv_dst_verts = vertices(pg, src(pe)), vertices(pg, dst(pe)) -# es = vcat(edges(pg), reverse.(edges(pg))) -# return PartitionEdge.(filter(e -> src(e) ∈ pv_src_verts && dst(e) ∈ pv_dst_verts, es)) -# end - #Update all messages flowing within a partition function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) edges = partitionedges(bmpsc, pv) return update(bmpsc, vcat(edges, reverse(reverse.(edges))); kwargs...) end -#Update all messages flowing within a partition from v1 to v2 -function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) +#Edges between v1 and v2 within a partition +function update_sequence(bmpsc::BoundaryMPSCache, v1, v2) pv1, pv2 = get_partition(bmpsc, v1), get_partition(bmpsc, v2) @assert pv1 == pv2 - vs = sort(partitionvertices(bmpsc, pv1)) + vs = partitionvertices(bmpsc, pv1) v1_pos, v2_pos = only(findall(x->x==v1, vs)), only(findall(x->x==v2, vs)) v1_pos < v2_pos && return PartitionEdge.([vs[i] => vs[i+1] for i in v1_pos:(v2_pos-1)]) - es = PartitionEdge.([vs[i] => vs[i-1] for i in v2_pos:(v1_pos+1)]) - return update(bmpsc, es; kwargs...) + return PartitionEdge.([vs[i] => vs[i-1] for i in v2_pos:(v1_pos+1)]) +end + +#Update all messages flowing within a partition from v1 to v2 +function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) + return update(bmpsc, update_sequence(bmpsc, v1, v2); kwargs...) end #Needed for implementation, forward from beliefpropagationcache From 6f9dc3231239cb587318297cda2ca32f1b546d34 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 19 Dec 2024 16:25:54 -0500 Subject: [PATCH 07/47] First commit --- examples/test_boundarymps.jl | 36 ++++++-- src/caches/boundarympscache.jl | 154 +++++++++++++++++++++++++++------ 2 files changed, 158 insertions(+), 32 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index fdbf55cb..37eab610 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,19 +1,45 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, - partitionedplanargraph, gauges, partitionedges, messages, update, partition_update + gauges, partitionedges, messages, update, partition_update, set_messages, message, + planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect using ITensorNetworks.ModelHamiltonians: ising -using ITensors: Index, OpSum, terms, sites +using ITensors: ITensors, Index, OpSum, terms, sites, contract using NamedGraphs: NamedEdge using NamedGraphs.NamedGraphGenerators: named_grid using NamedGraphs.GraphsExtensions: rem_vertex using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, PartitionEdge +using LinearAlgebra: normalize +using Graphs: center + +using Random + +Random.seed!(1234) L = 3 g = named_grid((L,L)) +#g = rem_vertex(g, (2,2)) +vc = first(center(g)) s = siteinds("S=1/2", g) ψ = random_tensornetwork(s; link_space = 2) +bp_update_kwargs = (; maxiter = 50, tol = 1e-14) + +#Run BP first to normalize and put in a stable gauge ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) +ψIψ = update(ψIψ; bp_update_kwargs...) +ψ = VidalITensorNetwork(ψ; cache! = Ref(ψIψ), update_cache = false, cache_update_kwargs = (; maxiter = 0)) +ψ = ITensorNetwork(ψ) +ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + ψIψ = BoundaryMPSCache(ψIψ) -#@show PartitionEdge(NamedEdge(1 => 2)) -#@show PartitionEdge(1 => 2) -@show partition_update(ψIψ, (1,1), (1,2)) \ No newline at end of file +ψIψ = set_messages(ψIψ; message_rank = 12) + +ψIψ = mps_update(ψIψ; maxiter = 10, niters = 10) + +ψIψ = partition_update(ψIψ, nothing, vc) + +ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") +sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] + +@show sz + +@show expect(ψ, "Z", [vc]; alg = "bp") \ No newline at end of file diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 6a79fbb9..7dfb3a24 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -1,70 +1,170 @@ using NamedGraphs: NamedGraphs -using ITensorNetworks: ITensorNetworks -using NamedGraphs.PartitionedGraphs: partitioned_graph +using ITensorNetworks: ITensorNetworks, BeliefPropagationCache +using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex +using SplitApplyCombine: group -struct BoundaryMPSCache{BPC,G, D} +struct BoundaryMPSCache{BPC,G, PG} bp_cache::BPC gauges::G - partitions::D + partitionedplanargraph::PG end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache gauges(bmpsc::BoundaryMPSCache) = bmpsc.gauges -partitions(bmpsc::BoundaryMPSCache) = bmpsc.partitions -planargraph(bmpsc::BoundaryMPSCache) = partitioned_graph(bp_cache(bmpsc)) +partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph +ppg(bmpsc) = partitionedplanargraph(bmpsc) +planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) -ITensorNetworks.partitionvertices(bmpsc::BoundaryMPSCache, partition::Int64) = partitions(bmpsc)[partition] - -function get_partition(bmpsc::BoundaryMPSCache, v) - return only(filter(pv -> v ∈ partitionvertices(bmpsc, pv), keys(partitions(bmpsc)))) +function Base.copy(bmpsc::BoundaryMPSCache) + return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(gauges(bmpsc)), copy(ppg(bmpsc))) end +planargraph_vertices(bmpsc::BoundaryMPSCache, pv::PartitionVertex) = vertices(ppg(bmpsc), pv) +planargraph_partitionvertex(bmpsc::BoundaryMPSCache, vertex) = partitionvertex(ppg(bmpsc), vertex) + function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) planar_graph = partitioned_graph(bpc) - #TODO: Make sure these are sorted vertex_groups = group(sort_f, collect(vertices(planar_graph))) - - gauges = Dictionary(keys(vertex_groups), ["Nothing" for pv in keys(vertex_groups)]) - return BoundaryMPSCache(bpc, gauges, vertex_groups) + ppg = PartitionedGraph(planar_graph, vertex_groups) + gauges = Dictionary(partitionvertices(ppg), ["Nothing" for pv in partitionvertices(ppg)]) + return BoundaryMPSCache(bpc, gauges, ppg) end #Get all partitionedges within a partition, sorted top to bottom -function ITensorNetworks.partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) - vs = partitionvertices(bmpsc, partition) +function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex) + vs = sort(planargraph_vertices(bmpsc, pv)) return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end -#Update all messages flowing within a partition -function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) - edges = partitionedges(bmpsc, pv) - return update(bmpsc, vcat(edges, reverse(reverse.(edges))); kwargs...) -end - #Edges between v1 and v2 within a partition function update_sequence(bmpsc::BoundaryMPSCache, v1, v2) - pv1, pv2 = get_partition(bmpsc, v1), get_partition(bmpsc, v2) + pv1, pv2 = planargraph_partitionvertex(bmpsc, v1), planargraph_partitionvertex(bmpsc, v2) @assert pv1 == pv2 - vs = partitionvertices(bmpsc, pv1) + vs = planargraph_vertices(bmpsc, pv1) v1_pos, v2_pos = only(findall(x->x==v1, vs)), only(findall(x->x==v2, vs)) v1_pos < v2_pos && return PartitionEdge.([vs[i] => vs[i+1] for i in v1_pos:(v2_pos-1)]) - return PartitionEdge.([vs[i] => vs[i-1] for i in v2_pos:(v1_pos+1)]) + return PartitionEdge.([vs[i] => vs[i-1] for i in v1_pos:(v2_pos+1)]) +end + +#Get all partitionedges from p1 to p2, flowing from top to bottom +#TODO: Bring in line with NamedGraphs change +function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pg = planargraph(bmpsc) + src_vs, dst_vs = planargraph_vertices(bmpsc, src(pe)), planargraph_vertices(bmpsc, dst(pe)) + es = filter(x -> !isempty(last(x)), [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs]) + es = map(x -> first(x) => only(last(x)), es) + return PartitionEdge.(NamedEdge.(es)) +end + +function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + set!(ms, pe, m) + return bmpsc +end + +function set_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge; message_rank::Int64 = 1) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + pes = planargraph_partitionedges(bmpsc, pe) + prev_virtual_ind = nothing + for (i, pg_pe) in enumerate(pes) + siteinds = linkinds(bmpsc, pg_pe) + next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing + inds = filter(x -> !isnothing(x), [siteinds; prev_virtual_ind; next_virtual_index]) + set!(ms, pg_pe, ITensor[ITensor(1.0, inds)]) + prev_virtual_ind = next_virtual_index + end + return bmpsc +end + +function set_messages(bmpsc::BoundaryMPSCache; message_rank::Int64 = 1) + bmpsc = copy(bmpsc) + pes = partitionedges(ppg(bmpsc)) + for pe in vcat(pes, reverse(reverse.(pes))) + bmpsc = set_messages(bmpsc, pe; message_rank) + end + return bmpsc +end + +function switch_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + pes = planargraph_partitionedges(bmpsc, pe) + for pe_i in pes + me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) + set!(ms, pe_i, dag.(mer)) + set!(ms, reverse(pe_i), dag.(me)) + end + return bmpsc +end + +#Update all messages flowing within a partition +function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) + edges = planargraph_partitionedges(bmpsc, pv) + return update(bmpsc, vcat(edges, reverse(reverse.(edges))); kwargs...) end #Update all messages flowing within a partition from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) + v1 == v2 && return bmpsc + isnothing(v1) && return partition_update(bmpsc, planargraph_partitionvertex(bmpsc, v2)) return update(bmpsc, update_sequence(bmpsc, v1, v2); kwargs...) end -#Needed for implementation, forward from beliefpropagationcache +function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) + bmpsc = switch_messages(bmpsc, pe) + pes = planargraph_partitionedges(bmpsc, pe) + update_seq = vcat(pes, reverse(pes)) + prev_v = nothing + for i in 1:niters + for update_pe in update_seq + cur_v = parent(src(update_pe)) + #TODO: Move gauge centre to update_pe + + bmpsc = partition_update(bmpsc, prev_v, cur_v) + me = update_message(bmpsc, update_pe) + bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) + prev_v = cur_v + end + end + return switch_messages(bmpsc, pe) +end + +function mps_update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge} = default_edge_sequence(ppg(bmpsc)); maxiter::Int64 = 1, niters::Int64 = 1) + bmpsc = copy(bmpsc) + for i in 1:maxiter + for pe in pes + bmpsc = mps_update(bmpsc, pe; niters) + end + end + return bmpsc +end + +#Forward onto beliefpropagationcache for f in [ :messages, :message, :update_message, - :update, + :(ITensorNetworks.linkinds), + :default_edge_sequence, + :environment ] @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) return $f(bp_cache(bmpsc), args...; kwargs...) end end +end + +#Wrap around beliefpropagationcache +for f in [ + :update +] + @eval begin + function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) + bpc = $f(bp_cache(bmpsc), args...; kwargs...) + return BoundaryMPSCache(bpc, gauges(bmpsc), ppg(bmpsc)) + end + end end \ No newline at end of file From 08f574af3037126215db8def8b8e37a5143c2430 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 19 Dec 2024 18:18:36 -0500 Subject: [PATCH 08/47] Sequence sorting --- examples/test_boundarymps.jl | 16 ++++----- src/caches/boundarympscache.jl | 59 ++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 37eab610..59ceda01 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,5 +1,5 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, - gauges, partitionedges, messages, update, partition_update, set_messages, message, + partitionedges, messages, update, partition_update, set_messages, message, planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect using ITensorNetworks.ModelHamiltonians: ising using ITensors: ITensors, Index, OpSum, terms, sites, contract @@ -14,12 +14,12 @@ using Random Random.seed!(1234) -L = 3 +L = 4 g = named_grid((L,L)) -#g = rem_vertex(g, (2,2)) +g = rem_vertex(g, (2,2)) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(s; link_space = 2) +ψ = random_tensornetwork(ComplexF64, s; link_space = 4) bp_update_kwargs = (; maxiter = 50, tol = 1e-14) #Run BP first to normalize and put in a stable gauge @@ -29,13 +29,13 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14) ψ = ITensorNetwork(ψ) ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -ψIψ = BoundaryMPSCache(ψIψ) +ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 12) +ψIψ = set_messages(ψIψ; message_rank = 4) -ψIψ = mps_update(ψIψ; maxiter = 10, niters = 10) +ψIψ = mps_update(ψIψ; maxiter = 10, niters = 5) -ψIψ = partition_update(ψIψ, nothing, vc) +ψIψ = partition_update(ψIψ, vc) ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 7dfb3a24..888ee25e 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -3,31 +3,29 @@ using ITensorNetworks: ITensorNetworks, BeliefPropagationCache using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex using SplitApplyCombine: group -struct BoundaryMPSCache{BPC,G, PG} +struct BoundaryMPSCache{BPC,PG} bp_cache::BPC - gauges::G partitionedplanargraph::PG end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache -gauges(bmpsc::BoundaryMPSCache) = bmpsc.gauges partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph ppg(bmpsc) = partitionedplanargraph(bmpsc) planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) function Base.copy(bmpsc::BoundaryMPSCache) - return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(gauges(bmpsc)), copy(ppg(bmpsc))) + return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) end planargraph_vertices(bmpsc::BoundaryMPSCache, pv::PartitionVertex) = vertices(ppg(bmpsc), pv) planargraph_partitionvertex(bmpsc::BoundaryMPSCache, vertex) = partitionvertex(ppg(bmpsc), vertex) +planargraph_partitionedge(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = partitionedge(ppg(bmpsc), parent(pe)) -function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f = v -> first(v)) +function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v)) planar_graph = partitioned_graph(bpc) vertex_groups = group(sort_f, collect(vertices(planar_graph))) ppg = PartitionedGraph(planar_graph, vertex_groups) - gauges = Dictionary(partitionvertices(ppg), ["Nothing" for pv in partitionvertices(ppg)]) - return BoundaryMPSCache(bpc, gauges, ppg) + return BoundaryMPSCache(bpc, ppg) end #Get all partitionedges within a partition, sorted top to bottom @@ -38,12 +36,40 @@ end #Edges between v1 and v2 within a partition function update_sequence(bmpsc::BoundaryMPSCache, v1, v2) + v1 == v2 && return PartitionEdge[] pv1, pv2 = planargraph_partitionvertex(bmpsc, v1), planargraph_partitionvertex(bmpsc, v2) @assert pv1 == pv2 - vs = planargraph_vertices(bmpsc, pv1) + vs = sort(planargraph_vertices(bmpsc, pv1)) v1_pos, v2_pos = only(findall(x->x==v1, vs)), only(findall(x->x==v2, vs)) v1_pos < v2_pos && return PartitionEdge.([vs[i] => vs[i+1] for i in v1_pos:(v2_pos-1)]) - return PartitionEdge.([vs[i] => vs[i-1] for i in v1_pos:(v2_pos+1)]) + return PartitionEdge.([vs[i] => vs[i-1] for i in v1_pos:-1:(v2_pos+1)]) +end + +#Edges toward v within a partition +function update_sequence(bmpsc::BoundaryMPSCache, v) + pv = planargraph_partitionvertex(bmpsc, v) + vs = sort(planargraph_vertices(bmpsc, pv)) + seq = vcat(update_sequence(bmpsc, last(vs), v), update_sequence(bmpsc, first(vs), v)) + return seq +end + +#Edges between pe1 and pe2 along an interpartition +function update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) + ppgpe1, ppgpe2 = planargraph_partitionedge(bmpsc, pe1), planargraph_partitionedge(bmpsc, pe2) + @assert ppgpe1 == ppgpe2 + #TODO: Sort these top to bottom + pes = planargraph_partitionedges(bmpsc, ppgpe1) + pe1_pos, pe2_pos = only(findall(x->x==pe1, pes)), only(findall(x->x==pe2, pes)) + pe1_pos < pe2_pos && return PartitionEdge.([parent(pes[i]) => parent(pes[i+1]) for i in pe1_pos:(pe2_pos-1)]) + return PartitionEdge.([parent(pes[i]) => parent(pes[i-1]) for i in pe1_pos:-1:(pe2_pos+1)]) +end + +#Edges toward pe1 along an interpartition +function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + ppgpe = planargraph_partitionedge(bmpsc, pe) + #TODO: Sort these top to bottom + pes = planargraph_partitionedges(bmpsc, ppgpe) + return vcat(update_sequence(pv, last(pes), pe), update_sequence(pv, first(pes), pe)) end #Get all partitionedges from p1 to p2, flowing from top to bottom @@ -107,11 +133,14 @@ end #Update all messages flowing within a partition from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) - v1 == v2 && return bmpsc - isnothing(v1) && return partition_update(bmpsc, planargraph_partitionvertex(bmpsc, v2)) return update(bmpsc, update_sequence(bmpsc, v1, v2); kwargs...) end +#Update all messages flowing within a partition from v1 to v2 +function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) + return update(bmpsc, update_sequence(bmpsc, v); kwargs...) +end + function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) bmpsc = switch_messages(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, pe) @@ -122,7 +151,11 @@ function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = cur_v = parent(src(update_pe)) #TODO: Move gauge centre to update_pe - bmpsc = partition_update(bmpsc, prev_v, cur_v) + if !isnothing(prev_v) + bmpsc = partition_update(bmpsc, prev_v, cur_v) + else + bmpsc = partition_update(bmpsc, cur_v) + end me = update_message(bmpsc, update_pe) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) prev_v = cur_v @@ -164,7 +197,7 @@ for f in [ @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) bpc = $f(bp_cache(bmpsc), args...; kwargs...) - return BoundaryMPSCache(bpc, gauges(bmpsc), ppg(bmpsc)) + return BoundaryMPSCache(bpc, ppg(bmpsc)) end end end \ No newline at end of file From 63b18ee07929b6b170cdb53e0af7f3dbfc0d05aa Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 19 Dec 2024 18:50:11 -0500 Subject: [PATCH 09/47] Test change --- examples/test_boundarymps.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 59ceda01..a9c68a70 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -19,7 +19,7 @@ g = named_grid((L,L)) g = rem_vertex(g, (2,2)) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(ComplexF64, s; link_space = 4) +ψ = random_tensornetwork(s; link_space = 4) bp_update_kwargs = (; maxiter = 50, tol = 1e-14) #Run BP first to normalize and put in a stable gauge @@ -31,9 +31,9 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14) ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 4) +ψIψ = set_messages(ψIψ; message_rank = 1) -ψIψ = mps_update(ψIψ; maxiter = 10, niters = 5) +ψIψ = mps_update(ψIψ; niters = 50) ψIψ = partition_update(ψIψ, vc) From 008b981d22e81850bc81548b5984a7204bd1d8a5 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 26 Dec 2024 16:26:28 -0500 Subject: [PATCH 10/47] Stuff --- examples/test_boundarymps.jl | 60 ++++++++++++++++++++++------ src/caches/beliefpropagationcache.jl | 2 +- src/caches/boundarympscache.jl | 60 ++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index a9c68a70..5c9a436c 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,8 +1,11 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, - planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect + planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, + default_message_update, contraction_sequence, gauge_move, ortho_gauge +using OMEinsumContractionOrders +using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensorNetworks.ModelHamiltonians: ising -using ITensors: ITensors, Index, OpSum, terms, sites, contract +using ITensors: ITensor, ITensors, Index, OpSum, terms, sites, contract, commonind, replaceind, replaceinds, prime, dag, noncommonind, noncommoninds, inds using NamedGraphs: NamedEdge using NamedGraphs.NamedGraphGenerators: named_grid using NamedGraphs.GraphsExtensions: rem_vertex @@ -12,15 +15,36 @@ using Graphs: center using Random +function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) + s = siteinds(ψ) + ψIψ = QuadraticFormNetwork(ψ) + ψOψ = QuadraticFormNetwork(ψ) + for (op_string, v) in zip(ops, vs) + ψOψ[(v, "operator")] = ITensors.op(op_string, s[v]) + end + numer_seq = contraction_sequence(ψOψ; alg="sa_bipartite") + denom_seq = contraction_sequence(ψIψ; alg="sa_bipartite") + numer, denom = contract(ψOψ; sequence=numer_seq)[], contract(ψIψ; sequence=denom_seq)[] + return numer / denom +end + +function exact_expect(ψ::ITensorNetwork, op_string::String, v) + return exact_expect(ψ, [op_string], [v]) +end + +function make_eigs_real(A::ITensor) + return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) +end + Random.seed!(1234) -L = 4 +L = 3 g = named_grid((L,L)) g = rem_vertex(g, (2,2)) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(s; link_space = 4) -bp_update_kwargs = (; maxiter = 50, tol = 1e-14) +ψ = random_tensornetwork(ComplexF64, s; link_space = 2) +bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) #Run BP first to normalize and put in a stable gauge ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) @@ -31,15 +55,27 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14) ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 1) +ψIψ = set_messages(ψIψ; message_rank = 2) + +#@show inds.(message(ψIψ, PartitionEdge((2,3) => (1,3)))) +#@show inds.(message(ψIψ, PartitionEdge((2,1) => (1,1)))) +#ψIψ = gauge_move(ψIψ, PartitionEdge((1,3) => (2,3)), PartitionEdge((1,1) => (2,1))) + + +#ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1))) +#ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1)),PartitionEdge((1,3) => (2,3))) +#TODO: Fix issue with not having messages on depleted square graphss +ψIψ = mps_update(ψIψ, PartitionEdge(2=>1); niters = 15) + +#ψIψ = mps_update(ψIψ; niters = 15) -ψIψ = mps_update(ψIψ; niters = 50) +# ψIψ = partition_update(ψIψ, vc) -ψIψ = partition_update(ψIψ, vc) +# ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") +# sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] -ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") -sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] +# @show sz -@show sz +# @show expect(ψ, "Z", [vc]; alg = "bp") -@show expect(ψ, "Z", [vc]; alg = "bp") \ No newline at end of file +# @show exact_expect(ψ, "Z", vc) \ No newline at end of file diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index d5886ec7..73f3c521 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -14,7 +14,7 @@ using NamedGraphs.PartitionedGraphs: using SimpleTraits: SimpleTraits, Not, @traitfn using NDTensors: NDTensors -default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, inds_e))] default_messages(ptn::PartitionedGraph) = Dictionary() function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) sequence = optimal_contraction_sequence(contract_list) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 888ee25e..265033c8 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -2,6 +2,7 @@ using NamedGraphs: NamedGraphs using ITensorNetworks: ITensorNetworks, BeliefPropagationCache using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex using SplitApplyCombine: group +using ITensors: commoninds struct BoundaryMPSCache{BPC,PG} bp_cache::BPC @@ -69,7 +70,7 @@ function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ppgpe = planargraph_partitionedge(bmpsc, pe) #TODO: Sort these top to bottom pes = planargraph_partitionedges(bmpsc, ppgpe) - return vcat(update_sequence(pv, last(pes), pe), update_sequence(pv, first(pes), pe)) + return vcat(update_sequence(bmpsc, last(pes), pe), update_sequence(bmpsc, first(pes), pe)) end #Get all partitionedges from p1 to p2, flowing from top to bottom @@ -98,7 +99,7 @@ function set_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge; message_rank:: siteinds = linkinds(bmpsc, pg_pe) next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing inds = filter(x -> !isnothing(x), [siteinds; prev_virtual_ind; next_virtual_index]) - set!(ms, pg_pe, ITensor[ITensor(1.0, inds)]) + set!(ms, pg_pe, ITensor[delta(inds)]) prev_virtual_ind = next_virtual_index end return bmpsc @@ -141,24 +142,75 @@ function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) return update(bmpsc, update_sequence(bmpsc, v); kwargs...) end +function ortho_gauge(a::ITensor, b::ITensor; kwargs...) + @assert !isempty(commoninds(a,b)) + left_inds = uniqueinds(a, b) + X, Y = factorize(a, left_inds; ortho="left", kwargs...) + return X, b*Y +end + +function gauge_move(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + if length(message(bmpsc, pe1)) == 1 + m1 = only(message(bmpsc, pe1)) + else + m1 = contract(message(bmpsc, pe1); sequence = "automatic") + end + if length(message(bmpsc, pe2)) == 1 + m2 = only(message(bmpsc, pe2)) + else + m2 = contract(message(bmpsc, pe2); sequence = "automatic") + end + m1, m2 = ortho_gauge(m1, m2; kwargs...) + bmpsc = set_message(bmpsc, pe1, ITensor[m1]) + bmpsc = set_message(bmpsc, pe2, ITensor[m2]) + return bmpsc +end + +function ortho_gauge(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) + pe_seq = update_sequence(bmpsc, pe) + @show pe_seq + for pe_pair in pe_seq + pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) + bmpsc = gauge_move(bmpsc, pe1, pe2) + end + return bmpsc +end + +function ortho_gauge(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + pe_seq = update_sequence(bmpsc, pe1, pe2) + for pe_pair in pe_seq + pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) + bmpsc = gauge_move(bmpsc, pe1, pe2) + end + return bmpsc +end + + function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) bmpsc = switch_messages(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, pe) update_seq = vcat(pes, reverse(pes)) prev_v = nothing + prev_pe = nothing for i in 1:niters for update_pe in update_seq + @show update_pe cur_v = parent(src(update_pe)) - #TODO: Move gauge centre to update_pe - + if !isnothing(prev_pe) + bmpsc = ortho_gauge(bmpsc, reverse(prev_pe), reverse(update_pe)) + else + bmpsc = ortho_gauge(bmpsc, reverse(update_pe)) + end if !isnothing(prev_v) bmpsc = partition_update(bmpsc, prev_v, cur_v) else bmpsc = partition_update(bmpsc, cur_v) end + #TODO: This will be missing incoming messages in depleted square graphs?!?! me = update_message(bmpsc, update_pe) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) prev_v = cur_v + prev_pe = update_pe end end return switch_messages(bmpsc, pe) From 4febcc3280bec624f1f1d89a2011d59a4b61e957 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 26 Dec 2024 17:00:47 -0500 Subject: [PATCH 11/47] Gauging --- examples/test_boundarymps.jl | 22 +++++++++++----------- src/caches/boundarympscache.jl | 2 -- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 5c9a436c..4bca2898 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -38,9 +38,9 @@ end Random.seed!(1234) -L = 3 +L = 4 g = named_grid((L,L)) -g = rem_vertex(g, (2,2)) +#g = rem_vertex(g, (2,2)) vc = first(center(g)) s = siteinds("S=1/2", g) ψ = random_tensornetwork(ComplexF64, s; link_space = 2) @@ -55,7 +55,7 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eig ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 2) +ψIψ = set_messages(ψIψ; message_rank = 4) #@show inds.(message(ψIψ, PartitionEdge((2,3) => (1,3)))) #@show inds.(message(ψIψ, PartitionEdge((2,1) => (1,1)))) @@ -65,17 +65,17 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eig #ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1))) #ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1)),PartitionEdge((1,3) => (2,3))) #TODO: Fix issue with not having messages on depleted square graphss -ψIψ = mps_update(ψIψ, PartitionEdge(2=>1); niters = 15) +#ψIψ = mps_update(ψIψ, PartitionEdge(2=>1); niters = 15) -#ψIψ = mps_update(ψIψ; niters = 15) +ψIψ = mps_update(ψIψ; niters = 15) -# ψIψ = partition_update(ψIψ, vc) +ψIψ = partition_update(ψIψ, vc) -# ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") -# sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] +ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") +sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] -# @show sz +@show sz -# @show expect(ψ, "Z", [vc]; alg = "bp") +@show expect(ψ, "Z", [vc]; alg = "bp") -# @show exact_expect(ψ, "Z", vc) \ No newline at end of file +@show exact_expect(ψ, "Z", vc) \ No newline at end of file diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 265033c8..1f24a3ef 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -168,7 +168,6 @@ end function ortho_gauge(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) pe_seq = update_sequence(bmpsc, pe) - @show pe_seq for pe_pair in pe_seq pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) bmpsc = gauge_move(bmpsc, pe1, pe2) @@ -194,7 +193,6 @@ function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = prev_pe = nothing for i in 1:niters for update_pe in update_seq - @show update_pe cur_v = parent(src(update_pe)) if !isnothing(prev_pe) bmpsc = ortho_gauge(bmpsc, reverse(prev_pe), reverse(update_pe)) From 50c53d792cf322dee1cc6864ed2c55375b393218 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 27 Dec 2024 11:50:11 -0500 Subject: [PATCH 12/47] Workinggit add examples/test_boundarymps.jl --- examples/test_boundarymps.jl | 40 +++++++++++++++++++++++++--------- src/caches/boundarympscache.jl | 5 +++++ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 4bca2898..0da81ad9 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,13 +1,13 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, gauge_move, ortho_gauge + default_message_update, contraction_sequence, gauge_move, ortho_gauge, insert_linkinds using OMEinsumContractionOrders using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensorNetworks.ModelHamiltonians: ising using ITensors: ITensor, ITensors, Index, OpSum, terms, sites, contract, commonind, replaceind, replaceinds, prime, dag, noncommonind, noncommoninds, inds -using NamedGraphs: NamedEdge -using NamedGraphs.NamedGraphGenerators: named_grid +using NamedGraphs: AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors +using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph using NamedGraphs.GraphsExtensions: rem_vertex using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, PartitionEdge using LinearAlgebra: normalize @@ -36,14 +36,31 @@ function make_eigs_real(A::ITensor) return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) end -Random.seed!(1234) +function missing_edges(g::AbstractGraph) + cols = unique(first.(collect(vertices(g)))) + missing_es = NamedEdge[] + for c in cols + c_vs = sort(filter(v -> first(v) == c, collect(vertices(g)))) + for i in 1:(length(c_vs) - 1) + if c_vs[i] ∉ neighbors(g, c_vs[i+1]) + push!(missing_es, NamedEdge(c_vs[i] => c_vs[i+1])) + end + end + end + return missing_es +end + +Random.seed!(1834) +ITensors.disable_warn_order() L = 4 -g = named_grid((L,L)) +#g = named_grid((L,L)) #g = rem_vertex(g, (2,2)) +g = named_hexagonal_lattice_graph(L, L) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(ComplexF64, s; link_space = 2) +ψ = random_tensornetwork(ComplexF64, s; link_space = 4) +ψ = insert_linkinds(ψ, missing_edges(ψ)) bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) #Run BP first to normalize and put in a stable gauge @@ -55,17 +72,20 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eig ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 4) +ψIψ = set_messages(ψIψ; message_rank = 64) #@show inds.(message(ψIψ, PartitionEdge((2,3) => (1,3)))) #@show inds.(message(ψIψ, PartitionEdge((2,1) => (1,1)))) #ψIψ = gauge_move(ψIψ, PartitionEdge((1,3) => (2,3)), PartitionEdge((1,1) => (2,1))) - -#ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1))) +#@show message(ψIψ, PartitionEdge((3,2) => (4,2))) +#@show message(ψIψ, PartitionEdge((4,2) => (3,2))) +#ψIψ = switch_messages(ψIψ, PartitionEdge(3 => 4)) +#ψIψ = switch_messages(ψIψ, PartitionEdge(3 => 4)) +#ψIψ = ortho_gauge(ψIψ, PartitionEdge((3,1) => (4,1))) #ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1)),PartitionEdge((1,3) => (2,3))) #TODO: Fix issue with not having messages on depleted square graphss -#ψIψ = mps_update(ψIψ, PartitionEdge(2=>1); niters = 15) +#ψIψ = mps_update(ψIψ, PartitionEdge(4=>3); niters = 15) ψIψ = mps_update(ψIψ; niters = 15) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 1f24a3ef..1f3ce447 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -60,6 +60,7 @@ function update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::Parti @assert ppgpe1 == ppgpe2 #TODO: Sort these top to bottom pes = planargraph_partitionedges(bmpsc, ppgpe1) + pes = sort(pes; by = x -> src(parent(x))) pe1_pos, pe2_pos = only(findall(x->x==pe1, pes)), only(findall(x->x==pe2, pes)) pe1_pos < pe2_pos && return PartitionEdge.([parent(pes[i]) => parent(pes[i+1]) for i in pe1_pos:(pe2_pos-1)]) return PartitionEdge.([parent(pes[i]) => parent(pes[i-1]) for i in pe1_pos:-1:(pe2_pos+1)]) @@ -70,6 +71,7 @@ function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ppgpe = planargraph_partitionedge(bmpsc, pe) #TODO: Sort these top to bottom pes = planargraph_partitionedges(bmpsc, ppgpe) + pes = sort(pes; by = x -> src(parent(x))) return vcat(update_sequence(bmpsc, last(pes), pe), update_sequence(bmpsc, first(pes), pe)) end @@ -94,6 +96,7 @@ function set_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge; message_rank:: bmpsc = copy(bmpsc) ms = messages(bmpsc) pes = planargraph_partitionedges(bmpsc, pe) + pes = sort(pes; by = x -> src(parent(x))) prev_virtual_ind = nothing for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) @@ -118,6 +121,7 @@ function switch_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge) bmpsc = copy(bmpsc) ms = messages(bmpsc) pes = planargraph_partitionedges(bmpsc, pe) + pes = sort(pes; by = x -> src(parent(x))) for pe_i in pes me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) set!(ms, pe_i, dag.(mer)) @@ -188,6 +192,7 @@ end function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) bmpsc = switch_messages(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, pe) + pe update_seq = vcat(pes, reverse(pes)) prev_v = nothing prev_pe = nothing From 9b9b05d1ef99b90311d3625fce6f2ce5f5dcc71a Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 28 Dec 2024 09:03:39 -0500 Subject: [PATCH 13/47] Missing edges --- examples/test_boundarymps.jl | 53 ++++++++++------------------------ src/caches/boundarympscache.jl | 39 +++++++++++++++++++++---- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 0da81ad9..02568e40 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,15 +1,17 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, gauge_move, ortho_gauge, insert_linkinds + default_message_update, contraction_sequence, gauge_move, ortho_gauge, insert_linkinds, + partitioned_tensornetwork, default_message using OMEinsumContractionOrders using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensorNetworks.ModelHamiltonians: ising using ITensors: ITensor, ITensors, Index, OpSum, terms, sites, contract, commonind, replaceind, replaceinds, prime, dag, noncommonind, noncommoninds, inds -using NamedGraphs: AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors +using NamedGraphs: NamedGraphs, AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph -using NamedGraphs.GraphsExtensions: rem_vertex -using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, PartitionEdge +using NamedGraphs.GraphsExtensions: rem_vertex, add_edges +using NamedGraphs.PartitionedGraphs: PartitionedGraph, partitioned_graph, PartitionVertex, PartitionEdge, unpartitioned_graph, + partitioned_vertices, which_partition using LinearAlgebra: normalize using Graphs: center @@ -36,56 +38,31 @@ function make_eigs_real(A::ITensor) return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) end -function missing_edges(g::AbstractGraph) - cols = unique(first.(collect(vertices(g)))) - missing_es = NamedEdge[] - for c in cols - c_vs = sort(filter(v -> first(v) == c, collect(vertices(g)))) - for i in 1:(length(c_vs) - 1) - if c_vs[i] ∉ neighbors(g, c_vs[i+1]) - push!(missing_es, NamedEdge(c_vs[i] => c_vs[i+1])) - end - end - end - return missing_es -end - Random.seed!(1834) ITensors.disable_warn_order() L = 4 -#g = named_grid((L,L)) -#g = rem_vertex(g, (2,2)) -g = named_hexagonal_lattice_graph(L, L) +g = named_grid((10,3)) +g = rem_vertex(g, (2,2)) +#g = named_hexagonal_lattice_graph(L, L) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(ComplexF64, s; link_space = 4) -ψ = insert_linkinds(ψ, missing_edges(ψ)) +ψ = random_tensornetwork(ComplexF64, s; link_space = 2) bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) #Run BP first to normalize and put in a stable gauge +#bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) +#bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) ψIψ = update(ψIψ; bp_update_kwargs...) ψ = VidalITensorNetwork(ψ; cache! = Ref(ψIψ), update_cache = false, cache_update_kwargs = (; maxiter = 0)) ψ = ITensorNetwork(ψ) -ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - -ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> first(v)) -ψIψ = set_messages(ψIψ; message_rank = 64) +ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -#@show inds.(message(ψIψ, PartitionEdge((2,3) => (1,3)))) -#@show inds.(message(ψIψ, PartitionEdge((2,1) => (1,1)))) -#ψIψ = gauge_move(ψIψ, PartitionEdge((1,3) => (2,3)), PartitionEdge((1,1) => (2,1))) +ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> last(v)) -#@show message(ψIψ, PartitionEdge((3,2) => (4,2))) -#@show message(ψIψ, PartitionEdge((4,2) => (3,2))) -#ψIψ = switch_messages(ψIψ, PartitionEdge(3 => 4)) -#ψIψ = switch_messages(ψIψ, PartitionEdge(3 => 4)) -#ψIψ = ortho_gauge(ψIψ, PartitionEdge((3,1) => (4,1))) -#ψIψ = ortho_gauge(ψIψ, PartitionEdge((1,1) => (2,1)),PartitionEdge((1,3) => (2,3))) -#TODO: Fix issue with not having messages on depleted square graphss -#ψIψ = mps_update(ψIψ, PartitionEdge(4=>3); niters = 15) +ψIψ = set_messages(ψIψ; message_rank = 4) ψIψ = mps_update(ψIψ; niters = 15) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 1f3ce447..744e5c1b 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -1,6 +1,8 @@ using NamedGraphs: NamedGraphs +using NamedGraphs.GraphsExtensions: add_edges using ITensorNetworks: ITensorNetworks, BeliefPropagationCache -using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex +using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, + which_partition using SplitApplyCombine: group using ITensors: commoninds @@ -23,10 +25,11 @@ planargraph_partitionvertex(bmpsc::BoundaryMPSCache, vertex) = partitionvertex(p planargraph_partitionedge(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = partitionedge(ppg(bmpsc), parent(pe)) function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v)) - planar_graph = partitioned_graph(bpc) - vertex_groups = group(sort_f, collect(vertices(planar_graph))) - ppg = PartitionedGraph(planar_graph, vertex_groups) - return BoundaryMPSCache(bpc, ppg) + bpc = insert_missing_planar_edges(bpc; sort_f) + planar_graph = partitioned_graph(bpc) + vertex_groups = group(sort_f, collect(vertices(planar_graph))) + ppg = PartitionedGraph(planar_graph, vertex_groups) + return BoundaryMPSCache(bpc, ppg) end #Get all partitionedges within a partition, sorted top to bottom @@ -255,4 +258,30 @@ for f in [ return BoundaryMPSCache(bpc, ppg(bmpsc)) end end +end + +function NamedGraphs.GraphsExtensions.add_edges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) + g = partitioned_graph(pg) + g = add_edges(g, parent.(pes)) + return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) +end + +function NamedGraphs.GraphsExtensions.add_edges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) + pg = add_edges(partitioned_tensornetwork(bpc), pes) + return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) +end + +function insert_missing_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) + pg = partitioned_graph(bpc) + partitions = unique(sort_f.(collect(vertices(pg)))) + es_to_add = PartitionEdge[] + for p in partitions + vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) + for i in 1:(length(vs) - 1) + if vs[i] ∉ neighbors(pg, vs[i+1]) + push!(es_to_add, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) + end + end + end + return add_edges(bpc, es_to_add) end \ No newline at end of file From a8c04d3c529bc9089ef7768d702aa155050fb689 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 29 Dec 2024 12:56:38 -0500 Subject: [PATCH 14/47] Simplify --- src/caches/boundarympscache.jl | 64 +++++++--------------------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 744e5c1b..1280325f 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -38,30 +38,10 @@ function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end -#Edges between v1 and v2 within a partition -function update_sequence(bmpsc::BoundaryMPSCache, v1, v2) - v1 == v2 && return PartitionEdge[] - pv1, pv2 = planargraph_partitionvertex(bmpsc, v1), planargraph_partitionvertex(bmpsc, v2) - @assert pv1 == pv2 - vs = sort(planargraph_vertices(bmpsc, pv1)) - v1_pos, v2_pos = only(findall(x->x==v1, vs)), only(findall(x->x==v2, vs)) - v1_pos < v2_pos && return PartitionEdge.([vs[i] => vs[i+1] for i in v1_pos:(v2_pos-1)]) - return PartitionEdge.([vs[i] => vs[i-1] for i in v1_pos:-1:(v2_pos+1)]) -end - -#Edges toward v within a partition -function update_sequence(bmpsc::BoundaryMPSCache, v) - pv = planargraph_partitionvertex(bmpsc, v) - vs = sort(planargraph_vertices(bmpsc, pv)) - seq = vcat(update_sequence(bmpsc, last(vs), v), update_sequence(bmpsc, first(vs), v)) - return seq -end - #Edges between pe1 and pe2 along an interpartition function update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) ppgpe1, ppgpe2 = planargraph_partitionedge(bmpsc, pe1), planargraph_partitionedge(bmpsc, pe2) @assert ppgpe1 == ppgpe2 - #TODO: Sort these top to bottom pes = planargraph_partitionedges(bmpsc, ppgpe1) pes = sort(pes; by = x -> src(parent(x))) pe1_pos, pe2_pos = only(findall(x->x==pe1, pes)), only(findall(x->x==pe2, pes)) @@ -72,7 +52,6 @@ end #Edges toward pe1 along an interpartition function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ppgpe = planargraph_partitionedge(bmpsc, pe) - #TODO: Sort these top to bottom pes = planargraph_partitionedges(bmpsc, ppgpe) pes = sort(pes; by = x -> src(parent(x))) return vcat(update_sequence(bmpsc, last(pes), pe), update_sequence(bmpsc, first(pes), pe)) @@ -135,18 +114,22 @@ end #Update all messages flowing within a partition function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) - edges = planargraph_partitionedges(bmpsc, pv) - return update(bmpsc, vcat(edges, reverse(reverse.(edges))); kwargs...) + vs = sort(planargraph_vertices(bmpsc, pv)) + bmpsc = update(bmpsc, first(vs); kwargs...) + bmpsc = update(bmpsc, last(vs); kwargs...) + return bmpsc end #Update all messages flowing within a partition from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) - return update(bmpsc, update_sequence(bmpsc, v1, v2); kwargs...) + return update(bmpsc, PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); kwargs...) end #Update all messages flowing within a partition from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) - return update(bmpsc, update_sequence(bmpsc, v); kwargs...) + pv = planargraph_partitionvertex(bmpsc, v) + g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) + return update(bmpsc, PartitionEdge.(post_order_dfs_edges(g, v)); kwargs...) end function ortho_gauge(a::ITensor, b::ITensor; kwargs...) @@ -157,16 +140,7 @@ function ortho_gauge(a::ITensor, b::ITensor; kwargs...) end function gauge_move(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) - if length(message(bmpsc, pe1)) == 1 - m1 = only(message(bmpsc, pe1)) - else - m1 = contract(message(bmpsc, pe1); sequence = "automatic") - end - if length(message(bmpsc, pe2)) == 1 - m2 = only(message(bmpsc, pe2)) - else - m2 = contract(message(bmpsc, pe2); sequence = "automatic") - end + m1, m2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) m1, m2 = ortho_gauge(m1, m2; kwargs...) bmpsc = set_message(bmpsc, pe1, ITensor[m1]) bmpsc = set_message(bmpsc, pe2, ITensor[m2]) @@ -195,28 +169,16 @@ end function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) bmpsc = switch_messages(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, pe) - pe update_seq = vcat(pes, reverse(pes)) - prev_v = nothing - prev_pe = nothing + prev_v, prev_pe = nothing, nothing for i in 1:niters for update_pe in update_seq cur_v = parent(src(update_pe)) - if !isnothing(prev_pe) - bmpsc = ortho_gauge(bmpsc, reverse(prev_pe), reverse(update_pe)) - else - bmpsc = ortho_gauge(bmpsc, reverse(update_pe)) - end - if !isnothing(prev_v) - bmpsc = partition_update(bmpsc, prev_v, cur_v) - else - bmpsc = partition_update(bmpsc, cur_v) - end - #TODO: This will be missing incoming messages in depleted square graphs?!?! + bmpsc = !isnothing(prev_pe) ? ortho_gauge(bmpsc, reverse(prev_pe), reverse(update_pe)) : ortho_gauge(bmpsc, reverse(update_pe)) + bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v) : partition_update(bmpsc, cur_v) me = update_message(bmpsc, update_pe) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) - prev_v = cur_v - prev_pe = update_pe + prev_v, prev_pe = cur_v, update_pe end end return switch_messages(bmpsc, pe) From d8ce9cbb091d6b2120d7aad4c5f475175e925950 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 29 Dec 2024 16:23:00 -0500 Subject: [PATCH 15/47] Code clean --- examples/test_boundarymps.jl | 16 ++--- src/caches/boundarympscache.jl | 106 ++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 02568e40..6a2d2933 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,6 +1,6 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, - planargraph_partitionedges, update_sequence, switch_messages, mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, + planargraph_partitionedges, update_sequence, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, default_message_update, contraction_sequence, gauge_move, ortho_gauge, insert_linkinds, partitioned_tensornetwork, default_message using OMEinsumContractionOrders @@ -41,13 +41,13 @@ end Random.seed!(1834) ITensors.disable_warn_order() -L = 4 -g = named_grid((10,3)) +L = 5 +g = named_grid((L,L)) g = rem_vertex(g, (2,2)) #g = named_hexagonal_lattice_graph(L, L) vc = first(center(g)) s = siteinds("S=1/2", g) -ψ = random_tensornetwork(ComplexF64, s; link_space = 2) +ψ = random_tensornetwork(ComplexF64, s; link_space = 4) bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) #Run BP first to normalize and put in a stable gauge @@ -60,13 +60,9 @@ bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eig ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> last(v)) +ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> last(v), message_rank = 6) -ψIψ = set_messages(ψIψ; message_rank = 4) - -ψIψ = mps_update(ψIψ; niters = 15) - -ψIψ = partition_update(ψIψ, vc) +ψIψ = orthogonal_mps_update(ψIψ; niters = 25) ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 1280325f..20318196 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -1,6 +1,6 @@ using NamedGraphs: NamedGraphs using NamedGraphs.GraphsExtensions: add_edges -using ITensorNetworks: ITensorNetworks, BeliefPropagationCache +using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, region_scalar using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition using SplitApplyCombine: group @@ -22,49 +22,50 @@ end planargraph_vertices(bmpsc::BoundaryMPSCache, pv::PartitionVertex) = vertices(ppg(bmpsc), pv) planargraph_partitionvertex(bmpsc::BoundaryMPSCache, vertex) = partitionvertex(ppg(bmpsc), vertex) +planargraph_partitionvertices(bmpsc::BoundaryMPSCache, verts) = partitionvertices(ppg(bmpsc), verts) planargraph_partitionedge(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = partitionedge(ppg(bmpsc), parent(pe)) -function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v)) +function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v), + message_rank::Int64=1) bpc = insert_missing_planar_edges(bpc; sort_f) planar_graph = partitioned_graph(bpc) vertex_groups = group(sort_f, collect(vertices(planar_graph))) ppg = PartitionedGraph(planar_graph, vertex_groups) - return BoundaryMPSCache(bpc, ppg) + bmpsc = BoundaryMPSCache(bpc, ppg) + return initialize_messages(bmpsc, message_rank) end -#Get all partitionedges within a partition, sorted top to bottom +#Get all partitionedges within a column/row partition, sorted top to bottom function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex) vs = sort(planargraph_vertices(bmpsc, pv)) return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end -#Edges between pe1 and pe2 along an interpartition +#Sequence of Edges from pe1 to pe2 along a column/row interpartition function update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) ppgpe1, ppgpe2 = planargraph_partitionedge(bmpsc, pe1), planargraph_partitionedge(bmpsc, pe2) @assert ppgpe1 == ppgpe2 pes = planargraph_partitionedges(bmpsc, ppgpe1) - pes = sort(pes; by = x -> src(parent(x))) pe1_pos, pe2_pos = only(findall(x->x==pe1, pes)), only(findall(x->x==pe2, pes)) pe1_pos < pe2_pos && return PartitionEdge.([parent(pes[i]) => parent(pes[i+1]) for i in pe1_pos:(pe2_pos-1)]) return PartitionEdge.([parent(pes[i]) => parent(pes[i-1]) for i in pe1_pos:-1:(pe2_pos+1)]) end -#Edges toward pe1 along an interpartition +#Edges toward pe1 along a column/row interpartition function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ppgpe = planargraph_partitionedge(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, ppgpe) - pes = sort(pes; by = x -> src(parent(x))) return vcat(update_sequence(bmpsc, last(pes), pe), update_sequence(bmpsc, first(pes), pe)) end -#Get all partitionedges from p1 to p2, flowing from top to bottom +#Get all partitionedges from src(pe) to dst(pe), sorted top to bottom #TODO: Bring in line with NamedGraphs change function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) pg = planargraph(bmpsc) src_vs, dst_vs = planargraph_vertices(bmpsc, src(pe)), planargraph_vertices(bmpsc, dst(pe)) es = filter(x -> !isempty(last(x)), [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs]) es = map(x -> first(x) => only(last(x)), es) - return PartitionEdge.(NamedEdge.(es)) + return sort(PartitionEdge.(NamedEdge.(es)); by = x -> src(parent(x))) end function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) @@ -74,11 +75,10 @@ function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITens return bmpsc end -function set_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge; message_rank::Int64 = 1) +function initialize_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge, message_rank::Int64) bmpsc = copy(bmpsc) ms = messages(bmpsc) pes = planargraph_partitionedges(bmpsc, pe) - pes = sort(pes; by = x -> src(parent(x))) prev_virtual_ind = nothing for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) @@ -90,20 +90,20 @@ function set_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge; message_rank:: return bmpsc end -function set_messages(bmpsc::BoundaryMPSCache; message_rank::Int64 = 1) +function initialize_messages(bmpsc::BoundaryMPSCache, message_rank::Int64 = 1) bmpsc = copy(bmpsc) pes = partitionedges(ppg(bmpsc)) for pe in vcat(pes, reverse(reverse.(pes))) - bmpsc = set_messages(bmpsc, pe; message_rank) + bmpsc = initialize_messages(bmpsc, pe, message_rank) end return bmpsc end +#Switch the messages from column/row i -> i + 1 with those from i + 1 -> i function switch_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge) bmpsc = copy(bmpsc) ms = messages(bmpsc) pes = planargraph_partitionedges(bmpsc, pe) - pes = sort(pes; by = x -> src(parent(x))) for pe_i in pes me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) set!(ms, pe_i, dag.(mer)) @@ -115,8 +115,8 @@ end #Update all messages flowing within a partition function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) vs = sort(planargraph_vertices(bmpsc, pv)) - bmpsc = update(bmpsc, first(vs); kwargs...) - bmpsc = update(bmpsc, last(vs); kwargs...) + bmpsc = partition_update(bmpsc, first(vs); kwargs...) + bmpsc = partition_update(bmpsc, last(vs); kwargs...) return bmpsc end @@ -132,51 +132,51 @@ function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) return update(bmpsc, PartitionEdge.(post_order_dfs_edges(g, v)); kwargs...) end -function ortho_gauge(a::ITensor, b::ITensor; kwargs...) - @assert !isempty(commoninds(a,b)) - left_inds = uniqueinds(a, b) - X, Y = factorize(a, left_inds; ortho="left", kwargs...) - return X, b*Y -end - -function gauge_move(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) +#Move the orthogonality centre one step from message tensor on pe1 to that on pe2 +function orthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) m1, m2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) - m1, m2 = ortho_gauge(m1, m2; kwargs...) - bmpsc = set_message(bmpsc, pe1, ITensor[m1]) - bmpsc = set_message(bmpsc, pe2, ITensor[m2]) + @assert !isempty(commoninds(m1,m2)) + left_inds = uniqueinds(m1, m2) + m1, Y = factorize(m1, left_inds; ortho="left", kwargs...) + m2 = m2 * Y + set!(ms, pe1, ITensor[m1]) + set!(ms, pe2, ITensor[m2]) return bmpsc end -function ortho_gauge(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) - pe_seq = update_sequence(bmpsc, pe) - for pe_pair in pe_seq +#Move the orthogonality centre via a sequence of steps between message tensors +function orthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) + for pe_pair in seq pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) - bmpsc = gauge_move(bmpsc, pe1, pe2) + bmpsc = orthogonal_gauge_step(bmpsc, pe1, pe2) end return bmpsc end -function ortho_gauge(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) - pe_seq = update_sequence(bmpsc, pe1, pe2) - for pe_pair in pe_seq - pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) - bmpsc = gauge_move(bmpsc, pe1, pe2) - end - return bmpsc +#Move the orthogonality centre to pe +function ITensorNetworks.orthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) + return orthogonal_gauge_walk(bmpsc, update_sequence(bmpsc, pe); kwargs...) end +#Move the orthogonality centre from pe1 to pe2 +function ITensorNetworks.orthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + return orthogonal_gauge_walk(bmpsc, update_sequence(bmpsc, pe1, pe2); kwargs...) +end -function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 1) +function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 25, normalize = true) bmpsc = switch_messages(bmpsc, pe) pes = planargraph_partitionedges(bmpsc, pe) - update_seq = vcat(pes, reverse(pes)) + update_seq = vcat(pes, reverse(pes)[2:length(pes)]) prev_v, prev_pe = nothing, nothing + message_update = ms -> default_message_update(ms; normalize) for i in 1:niters for update_pe in update_seq cur_v = parent(src(update_pe)) - bmpsc = !isnothing(prev_pe) ? ortho_gauge(bmpsc, reverse(prev_pe), reverse(update_pe)) : ortho_gauge(bmpsc, reverse(update_pe)) - bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v) : partition_update(bmpsc, cur_v) - me = update_message(bmpsc, update_pe) + bmpsc = !isnothing(prev_pe) ? orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) : orthogonalize(bmpsc, reverse(update_pe)) + bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update) : partition_update(bmpsc, cur_v; message_update) + me = update_message(bmpsc, update_pe; message_update) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) prev_v, prev_pe = cur_v, update_pe end @@ -184,24 +184,34 @@ function mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = return switch_messages(bmpsc, pe) end -function mps_update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge} = default_edge_sequence(ppg(bmpsc)); maxiter::Int64 = 1, niters::Int64 = 1) +function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge} = default_edge_sequence(ppg(bmpsc)); maxiter::Int64 = 1, kwargs...) bmpsc = copy(bmpsc) for i in 1:maxiter for pe in pes - bmpsc = mps_update(bmpsc, pe; niters) + bmpsc = orthogonal_mps_update(bmpsc, pe; kwargs...) end end return bmpsc end +function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) + vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) + pv = only(planargraph_partitionvertices(bmpsc, vs)) + bmpsc = partition_update(bmpsc, pv) + return environment(bp_cache(bmpsc), verts; kwargs...) +end + +function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, vertex; kwargs...) + return environment(bmpsc, [vertex]; kwargs...) +end + #Forward onto beliefpropagationcache for f in [ :messages, :message, :update_message, :(ITensorNetworks.linkinds), - :default_edge_sequence, - :environment + :default_edge_sequence ] @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) From d095c6fc78948258b7c7d50c321ebeef463b161c Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 30 Dec 2024 16:27:44 -0500 Subject: [PATCH 16/47] Improved nomenclature --- examples/test_boundarymps.jl | 64 +++++++----- src/caches/boundarympscache.jl | 177 ++++++++++++++++++++------------- 2 files changed, 150 insertions(+), 91 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 6a2d2933..5a03ca0a 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,7 +1,7 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, planargraph_partitionedges, update_sequence, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, gauge_move, ortho_gauge, insert_linkinds, + default_message_update, contraction_sequence, insert_linkinds, partitioned_tensornetwork, default_message using OMEinsumContractionOrders using ITensorNetworks.ITensorsExtensions: map_eigvals @@ -17,6 +17,17 @@ using Graphs: center using Random +function lieb_lattice_grid(nx::Int64, ny::Int64; periodic=false) + @assert (!periodic && isodd(nx) && isodd(ny)) || (periodic && iseven(nx) && iseven(ny)) + g = named_grid((nx, ny); periodic) + for v in vertices(g) + if iseven(first(v)) && iseven(last(v)) + g = rem_vertex(g, v) + end + end + return g +end + function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) s = siteinds(ψ) ψIψ = QuadraticFormNetwork(ψ) @@ -41,34 +52,39 @@ end Random.seed!(1834) ITensors.disable_warn_order() -L = 5 -g = named_grid((L,L)) -g = rem_vertex(g, (2,2)) -#g = named_hexagonal_lattice_graph(L, L) -vc = first(center(g)) -s = siteinds("S=1/2", g) -ψ = random_tensornetwork(ComplexF64, s; link_space = 4) -bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) -#Run BP first to normalize and put in a stable gauge -#bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -#bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) -ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -ψIψ = update(ψIψ; bp_update_kwargs...) -ψ = VidalITensorNetwork(ψ; cache! = Ref(ψIψ), update_cache = false, cache_update_kwargs = (; maxiter = 0)) -ψ = ITensorNetwork(ψ) +function main() + L = 4 + #g = lieb_lattice_grid(L, L) + g = named_hexagonal_lattice_graph(L, L) + vc = first(center(g)) + s = siteinds("S=1/2", g) + ψ = random_tensornetwork(ComplexF64, s; link_space = 2) + bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) -ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + #Run BP first to normalize and put in a stable gauge + #bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + #bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) + ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + ψIψ = update(ψIψ; bp_update_kwargs...) + ψ = VidalITensorNetwork(ψ; cache! = Ref(ψIψ), update_cache = false, cache_update_kwargs = (; maxiter = 0)) + ψ = ITensorNetwork(ψ) -ψIψ = BoundaryMPSCache(ψIψ; sort_f = v -> last(v), message_rank = 6) + fit_kwargs = (;niters = 50, tolerance = 1e-12) -ψIψ = orthogonal_mps_update(ψIψ; niters = 25) + ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) -ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") -sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] + rs = [2] -@show sz + @show exact_expect(ψ, "Z", vc) -@show expect(ψ, "Z", [vc]; alg = "bp") + for r in rs + ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = r) + ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) + ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") + sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] + @show sz + end +end -@show exact_expect(ψ, "Z", vc) \ No newline at end of file +main() \ No newline at end of file diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 20318196..437a3f8f 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -1,11 +1,21 @@ using NamedGraphs: NamedGraphs using NamedGraphs.GraphsExtensions: add_edges using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, region_scalar +using ITensorMPS: ITensorMPS, orthogonalize using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition using SplitApplyCombine: group using ITensors: commoninds +pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) + +#Return a sequence of pairs to go from item1 to item2 in an ordered_list +function pair_sequence(ordered_list::Vector, item1, item2) + item1_pos, item2_pos = only(findall(x->x==item1, ordered_list)), only(findall(x->x==item2, ordered_list)) + item1_pos < item2_pos && return [ordered_list[i] => ordered_list[i+1] for i in item1_pos:(item2_pos-1)] + return [ordered_list[i] => ordered_list[i-1] for i in item1_pos:-1:(item2_pos+1)] +end + struct BoundaryMPSCache{BPC,PG} bp_cache::BPC partitionedplanargraph::PG @@ -15,54 +25,58 @@ bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph ppg(bmpsc) = partitionedplanargraph(bmpsc) planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) +tensornetwork(bmpsc::BoundaryMPSCache) = tensornetwork(bp_cache(bmpsc)) +partitioned_tensornetwork(bmpsc::BoundaryMPSCache) = partitioned_tensornetwork(bp_cache(bmpsc)) + +default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) +default_bp_maxiter(bmpsc::BoundaryMPSCache) = default_bp_maxiter(partitioned_graph(ppg(bmpsc))) +default_mps_fit_kwargs(bmpsc::BoundaryMPSCache) = (; niters = 25, tolerance = 1e-10) function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) end -planargraph_vertices(bmpsc::BoundaryMPSCache, pv::PartitionVertex) = vertices(ppg(bmpsc), pv) -planargraph_partitionvertex(bmpsc::BoundaryMPSCache, vertex) = partitionvertex(ppg(bmpsc), vertex) -planargraph_partitionvertices(bmpsc::BoundaryMPSCache, verts) = partitionvertices(ppg(bmpsc), verts) -planargraph_partitionedge(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = partitionedge(ppg(bmpsc), parent(pe)) +planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) = vertices(ppg(bmpsc), PartitionVertex(partition)) +planargraph_partition(bmpsc::BoundaryMPSCache, vertex) = parent(partitionvertex(ppg(bmpsc), vertex)) +planargraph_partitions(bmpsc::BoundaryMPSCache, verts) = parent.(partitionvertices(ppg(bmpsc), verts)) +planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = pair(partitionedge(ppg(bmpsc), parent(pe))) function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v), message_rank::Int64=1) - bpc = insert_missing_planar_edges(bpc; sort_f) + bpc = insert_pseudo_planar_edges(bpc; sort_f) planar_graph = partitioned_graph(bpc) vertex_groups = group(sort_f, collect(vertices(planar_graph))) ppg = PartitionedGraph(planar_graph, vertex_groups) bmpsc = BoundaryMPSCache(bpc, ppg) - return initialize_messages(bmpsc, message_rank) + return set_interpartition_messages(bmpsc, message_rank) end -#Get all partitionedges within a column/row partition, sorted top to bottom -function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pv::PartitionVertex) - vs = sort(planargraph_vertices(bmpsc, pv)) +#Get all partitionedges within a column/row, ordered top to bottom +function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) + vs = sort(planargraph_vertices(bmpsc, partition)) return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) end -#Sequence of Edges from pe1 to pe2 along a column/row interpartition -function update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) - ppgpe1, ppgpe2 = planargraph_partitionedge(bmpsc, pe1), planargraph_partitionedge(bmpsc, pe2) +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 +function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) + ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), planargraph_partitionpair(bmpsc, pe2) @assert ppgpe1 == ppgpe2 - pes = planargraph_partitionedges(bmpsc, ppgpe1) - pe1_pos, pe2_pos = only(findall(x->x==pe1, pes)), only(findall(x->x==pe2, pes)) - pe1_pos < pe2_pos && return PartitionEdge.([parent(pes[i]) => parent(pes[i+1]) for i in pe1_pos:(pe2_pos-1)]) - return PartitionEdge.([parent(pes[i]) => parent(pes[i-1]) for i in pe1_pos:-1:(pe2_pos+1)]) + pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe1) + return pair_sequence(pes, pe1, pe2) end -#Edges toward pe1 along a column/row interpartition -function update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) - ppgpe = planargraph_partitionedge(bmpsc, pe) - pes = planargraph_partitionedges(bmpsc, ppgpe) - return vcat(update_sequence(bmpsc, last(pes), pe), update_sequence(bmpsc, first(pes), pe)) +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge onto pe1 +function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + ppgpe = planargraph_partitionpair(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe) + return vcat(mps_gauge_update_sequence(bmpsc, last(pes), pe), mps_gauge_update_sequence(bmpsc, first(pes), pe)) end -#Get all partitionedges from src(pe) to dst(pe), sorted top to bottom +#Get all partitionedges between the pair of neighboring partitions, sorted top to bottom #TODO: Bring in line with NamedGraphs change -function planargraph_partitionedges(bmpsc::BoundaryMPSCache, pe::PartitionEdge) +function planargraph_partitionpair_partitionedges(bmpsc::BoundaryMPSCache, pe::Pair) pg = planargraph(bmpsc) - src_vs, dst_vs = planargraph_vertices(bmpsc, src(pe)), planargraph_vertices(bmpsc, dst(pe)) + src_vs, dst_vs = planargraph_vertices(bmpsc, first(pe)), planargraph_vertices(bmpsc, last(pe)) es = filter(x -> !isempty(last(x)), [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs]) es = map(x -> first(x) => only(last(x)), es) return sort(PartitionEdge.(NamedEdge.(es)); by = x -> src(parent(x))) @@ -75,10 +89,11 @@ function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITens return bmpsc end -function initialize_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge, message_rank::Int64) +#Initialise all the message tensors for the pair of neighboring partitions, with virtual rank given by message rank +function set_interpartition_messages(bmpsc::BoundaryMPSCache, pe::Pair, message_rank::Int64) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionedges(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, pe) prev_virtual_ind = nothing for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) @@ -90,20 +105,21 @@ function initialize_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge, message return bmpsc end -function initialize_messages(bmpsc::BoundaryMPSCache, message_rank::Int64 = 1) +#Initialise all the inerpartition message tensors with virtual rank given by message rank +function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64 = 1) bmpsc = copy(bmpsc) pes = partitionedges(ppg(bmpsc)) for pe in vcat(pes, reverse(reverse.(pes))) - bmpsc = initialize_messages(bmpsc, pe, message_rank) + bmpsc = set_interpartition_messages(bmpsc, parent(src(pe)) => parent(dst(pe)), message_rank) end return bmpsc end -#Switch the messages from column/row i -> i + 1 with those from i + 1 -> i -function switch_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge) +#Switch the message tensors from column/row i -> i + 1 with those from i + 1 -> i +function switch_messages(bmpsc::BoundaryMPSCache, pe::Pair) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionedges(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, pe) for pe_i in pes me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) set!(ms, pe_i, dag.(mer)) @@ -112,27 +128,27 @@ function switch_messages(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return bmpsc end -#Update all messages flowing within a partition -function partition_update(bmpsc::BoundaryMPSCache, pv::PartitionVertex; kwargs...) - vs = sort(planargraph_vertices(bmpsc, pv)) +#Update all messages tensors within a partition +function partition_update(bmpsc::BoundaryMPSCache, partition::Int64; kwargs...) + vs = sort(planargraph_vertices(bmpsc, partition)) bmpsc = partition_update(bmpsc, first(vs); kwargs...) bmpsc = partition_update(bmpsc, last(vs); kwargs...) return bmpsc end -#Update all messages flowing within a partition from v1 to v2 +#Update all messages within a partition along the path from from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) return update(bmpsc, PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); kwargs...) end -#Update all messages flowing within a partition from v1 to v2 +#Update all messages within a partition towards v function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) - pv = planargraph_partitionvertex(bmpsc, v) + pv = planargraph_partition(bmpsc, v) g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) return update(bmpsc, PartitionEdge.(post_order_dfs_edges(g, v)); kwargs...) end -#Move the orthogonality centre one step from message tensor on pe1 to that on pe2 +#Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 function orthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) bmpsc = copy(bmpsc) ms = messages(bmpsc) @@ -146,49 +162,64 @@ function orthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2: return bmpsc end -#Move the orthogonality centre via a sequence of steps between message tensors +#Move the orthogonality centre on an interpartition via a sequence of steps between message tensors function orthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) - for pe_pair in seq - pe1, pe2 = PartitionEdge(parent(src(pe_pair))), PartitionEdge(parent(dst(pe_pair))) + for (pe1, pe2) in seq bmpsc = orthogonal_gauge_step(bmpsc, pe1, pe2) end return bmpsc end -#Move the orthogonality centre to pe -function ITensorNetworks.orthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) - return orthogonal_gauge_walk(bmpsc, update_sequence(bmpsc, pe); kwargs...) +#Move the orthogonality centre on an interpartition to the message tensor on pe +function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) + return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe); kwargs...) end -#Move the orthogonality centre from pe1 to pe2 -function ITensorNetworks.orthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) - return orthogonal_gauge_walk(bmpsc, update_sequence(bmpsc, pe1, pe2); kwargs...) +#Move the orthogonality centre on an interpartition from the message tensor on pe1 to that on pe2 +function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe1, pe2); kwargs...) end -function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::PartitionEdge; niters::Int64 = 25, normalize = true) +#Update all the message tensors on an interpartition via an orthogonal fitting procedure +function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; niters::Int64 = 25, tolerance = 1e-10, normalize = true) bmpsc = switch_messages(bmpsc, pe) - pes = planargraph_partitionedges(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, pe) update_seq = vcat(pes, reverse(pes)[2:length(pes)]) prev_v, prev_pe = nothing, nothing - message_update = ms -> default_message_update(ms; normalize) + prev_costfunction = 0 for i in 1:niters + costfunction = 0 for update_pe in update_seq cur_v = parent(src(update_pe)) bmpsc = !isnothing(prev_pe) ? orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) : orthogonalize(bmpsc, reverse(update_pe)) - bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update) : partition_update(bmpsc, cur_v; message_update) - me = update_message(bmpsc, update_pe; message_update) + bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) : partition_update(bmpsc, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) + me = update_message(bmpsc, update_pe; message_update = ms -> default_message_update(ms; normalize)) + costfunction += region_scalar(bp_cache(bmpsc), src(update_pe)) / norm(me) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) prev_v, prev_pe = cur_v, update_pe end + epsilon = abs(costfunction - prev_costfunction) / length(update_seq) + if !isnothing(tolerance) && epsilon < tolerance + return switch_messages(bmpsc, pe) + else + prev_costfunction = costfunction + end end return switch_messages(bmpsc, pe) end -function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge} = default_edge_sequence(ppg(bmpsc)); maxiter::Int64 = 1, kwargs...) - bmpsc = copy(bmpsc) +""" +More generic interface for update, with default params +""" +function update( + bmpsc::BoundaryMPSCache; + pes=default_edge_sequence(bmpsc), + maxiter=default_bp_maxiter(bmpsc), + mps_fit_kwargs = default_mps_fit_kwargs(bmpsc) +) for i in 1:maxiter for pe in pes - bmpsc = orthogonal_mps_update(bmpsc, pe; kwargs...) + bmpsc = orthogonal_mps_update(bmpsc, pe; mps_fit_kwargs...) end end return bmpsc @@ -196,7 +227,7 @@ end function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) - pv = only(planargraph_partitionvertices(bmpsc, vs)) + pv = only(planargraph_partitions(bmpsc, vs)) bmpsc = partition_update(bmpsc, pv) return environment(bp_cache(bmpsc), verts; kwargs...) end @@ -211,7 +242,9 @@ for f in [ :message, :update_message, :(ITensorNetworks.linkinds), - :default_edge_sequence + :default_edge_sequence, + :factor, + :factors, ] @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) @@ -222,38 +255,48 @@ end #Wrap around beliefpropagationcache for f in [ - :update + :update_factors, + :update_factor, ] @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) - bpc = $f(bp_cache(bmpsc), args...; kwargs...) - return BoundaryMPSCache(bpc, ppg(bmpsc)) + bmpsc = copy(bmpsc) + bpc = bp_cache(bmpsc) + bpc = $f(bpc, args...; kwargs...) + return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) end end end -function NamedGraphs.GraphsExtensions.add_edges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) +function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs...) + bmpsc = copy(bmpsc) + bpc = bp_cache(bmpsc) + bpc = update(bpc, pes; kwargs...) + return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) +end + +function add_pseudo_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) g = partitioned_graph(pg) g = add_edges(g, parent.(pes)) return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) end -function NamedGraphs.GraphsExtensions.add_edges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) - pg = add_edges(partitioned_tensornetwork(bpc), pes) +function add_pseudo_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) + pg = add_pseudo_partitionedges(partitioned_tensornetwork(bpc), pes) return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) end -function insert_missing_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) +function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) pg = partitioned_graph(bpc) partitions = unique(sort_f.(collect(vertices(pg)))) - es_to_add = PartitionEdge[] + pseudo_edges = PartitionEdge[] for p in partitions vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) for i in 1:(length(vs) - 1) if vs[i] ∉ neighbors(pg, vs[i+1]) - push!(es_to_add, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) + push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) end end end - return add_edges(bpc, es_to_add) + return add_pseudo_partitionedges(bpc, pseudo_edges) end \ No newline at end of file From 6e8d7b861c048525af06c68623a6fa04a50b9b3f Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 30 Dec 2024 18:38:40 -0500 Subject: [PATCH 17/47] Adding biorthogonalization feature --- examples/test_boundarymps.jl | 25 +++++-- src/caches/boundarympscache.jl | 126 +++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 7 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 5a03ca0a..4b1952ae 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,7 +1,7 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, - planargraph_partitionedges, update_sequence, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, insert_linkinds, + planargraph_partitionedges, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, + default_message_update, contraction_sequence, insert_linkinds, biorthognal_update partitioned_tensornetwork, default_message using OMEinsumContractionOrders using ITensorNetworks.ITensorsExtensions: map_eigvals @@ -9,7 +9,7 @@ using ITensorNetworks.ModelHamiltonians: ising using ITensors: ITensor, ITensors, Index, OpSum, terms, sites, contract, commonind, replaceind, replaceinds, prime, dag, noncommonind, noncommoninds, inds using NamedGraphs: NamedGraphs, AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph -using NamedGraphs.GraphsExtensions: rem_vertex, add_edges +using NamedGraphs.GraphsExtensions: rem_vertex, add_edges, add_edge using NamedGraphs.PartitionedGraphs: PartitionedGraph, partitioned_graph, PartitionVertex, PartitionEdge, unpartitioned_graph, partitioned_vertices, which_partition using LinearAlgebra: normalize @@ -28,6 +28,15 @@ function lieb_lattice_grid(nx::Int64, ny::Int64; periodic=false) return g end +function named_grid_periodic_x(nxny::Tuple) + nx, ny = nxny + g = named_grid((nx, ny)) + for i in 1:ny + g = add_edge(g, NamedEdge((nx, i) => (1, i))) + end + return g +end + function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) s = siteinds(ψ) ψIψ = QuadraticFormNetwork(ψ) @@ -54,9 +63,10 @@ ITensors.disable_warn_order() function main() - L = 4 + L = 12 #g = lieb_lattice_grid(L, L) - g = named_hexagonal_lattice_graph(L, L) + #g = named_hexagonal_lattice_graph(L, L) + g = named_grid_periodic_x((L,2)) vc = first(center(g)) s = siteinds("S=1/2", g) ψ = random_tensornetwork(ComplexF64, s; link_space = 2) @@ -74,13 +84,14 @@ function main() ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - rs = [2] + rs = [16] @show exact_expect(ψ, "Z", vc) for r in rs ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = r) - ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) + #ψIψ = update(ψIψ; maxiter = 5, mps_fit_kwargs = fit_kwargs) + ψIψ = biorthognal_update(ψIψ; maxiter = 5) ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] @show sz diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 437a3f8f..f12ab15c 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -1,11 +1,13 @@ using NamedGraphs: NamedGraphs using NamedGraphs.GraphsExtensions: add_edges using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, region_scalar +using ITensorNetworks.ITensorsExtensions: map_diag using ITensorMPS: ITensorMPS, orthogonalize using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition using SplitApplyCombine: group using ITensors: commoninds +using LinearAlgebra: pinv pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) @@ -170,6 +172,14 @@ function orthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) return bmpsc end +#Move the biorthogonality centre on an interpartition via a sequence of steps between message tensors +function biorthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) + for (pe1, pe2) in seq + bmpsc = biorthogonal_gauge_step(bmpsc, pe1, pe2) + end + return bmpsc +end + #Move the orthogonality centre on an interpartition to the message tensor on pe function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe); kwargs...) @@ -180,6 +190,16 @@ function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, p return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe1, pe2); kwargs...) end +#Move the biorthogonality centre on an interpartition to the message tensor on pe +function biorthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) + return biorthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe); kwargs...) +end + +#Move the biorthogonality centre on an interpartition from the message tensor on pe1 to that on pe2 +function biorthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) + return biorthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe1, pe2); kwargs...) +end + #Update all the message tensors on an interpartition via an orthogonal fitting procedure function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; niters::Int64 = 25, tolerance = 1e-10, normalize = true) bmpsc = switch_messages(bmpsc, pe) @@ -208,6 +228,26 @@ function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; niters::Int64 return switch_messages(bmpsc, pe) end +#Update all the message tensors on an interpartition via a biorthogonal fitting procedure +function biorthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; normalize = true) + pes = planargraph_partitionpair_partitionedges(bmpsc, pe) + update_seq = pes + prev_v, prev_pe = nothing, nothing + for update_pe in update_seq + cur_v = parent(src(update_pe)) + bmpsc = !isnothing(prev_pe) ? biorthogonalize(bmpsc, prev_pe, update_pe) : biorthogonalize(bmpsc, update_pe) + bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) : partition_update(bmpsc, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) + me = only(update_message(bmpsc, update_pe; message_update = ms -> default_message_update(ms; normalize))) + mer = only(message(bmpsc, reverse(update_pe))) + me = replaceinds(me, noncommoninds(me, mer), noncommoninds(mer, me)) + bmpsc = set_message(bmpsc, update_pe, ITensor[me]) + prev_v, prev_pe = cur_v, update_pe + end + + return bmpsc +end + + """ More generic interface for update, with default params """ @@ -217,6 +257,9 @@ function update( maxiter=default_bp_maxiter(bmpsc), mps_fit_kwargs = default_mps_fit_kwargs(bmpsc) ) + if isnothing(maxiter) + error("You need to specify a number of iterations for Boundary MPS!") + end for i in 1:maxiter for pe in pes bmpsc = orthogonal_mps_update(bmpsc, pe; mps_fit_kwargs...) @@ -225,6 +268,28 @@ function update( return bmpsc end +""" +More generic interface for update, with default params +""" +function biorthognal_update( + bmpsc::BoundaryMPSCache; + pes=default_edge_sequence(bmpsc), + maxiter=25, +) + if isnothing(maxiter) + error("You need to specify a number of iterations for Boundary MPS!") + end + for i in 1:maxiter + for pe in pes + bmpsc = biorthogonal_mps_update(bmpsc, pe) + end + end + return bmpsc +end + + + + function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) pv = only(planargraph_partitions(bmpsc, vs)) @@ -268,6 +333,7 @@ for f in [ end end +#Wrap around beliefpropagationcache but only for specific argument function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs...) bmpsc = copy(bmpsc) bpc = bp_cache(bmpsc) @@ -275,17 +341,20 @@ function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs... return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) end +#Add partition edges that may not have meaning in the underlying graph function add_pseudo_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) g = partitioned_graph(pg) g = add_edges(g, parent.(pes)) return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) end +#Add partition edges that may not have meaning in the underlying graph function add_pseudo_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) pg = add_pseudo_partitionedges(partitioned_tensornetwork(bpc), pes) return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) end +#Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) pg = partitioned_graph(bpc) partitions = unique(sort_f.(collect(vertices(pg)))) @@ -299,4 +368,61 @@ function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> f end end return add_pseudo_partitionedges(bpc, pseudo_edges) +end + + +function biorthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; regularization = 0) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + + m1, m1r = only(message(bmpsc, pe1)), only(message(bmpsc, reverse(pe1))) + m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) + top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) + m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) + top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), setdiff(inds(m1r), [m1_siteinds; bottom_cind]) + + E = isempty(top_ncind) ? m1 * m1r : m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) + U, S, V = svd(E, bottom_cind; alg = "recursive") + S_sqrtinv = map_diag(x -> pinv(sqrt(x + regularization)), S) + S_sqrt = map_diag(x -> sqrt(x + regularization), S) + + set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) + set!(ms, reverse(pe1), ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)]) + set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) + set!(ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)]) + + @show inds(only(ms[pe1])), inds(only(ms[reverse(pe1)])) + @show inds(only(ms[pe2])), inds(only(ms[reverse(pe2)])) + return bmpsc +end + +function biorthogonalize_walk(ψ::ITensorNetwork, ϕ::ITensorNetwork, path::Vector{<:NamedEdge}; + regularization = 0) + ψ, ϕ = copy(ψ), copy(ϕ) + ep = first(path) + for e in path + vsrc, vdst = src(e), dst(e) + top_cind = commonind(ψ[vsrc], ψ[vdst]) + bottom_cind = commonind(ϕ[vsrc], ϕ[vdst]) + vsrc_siteinds = commoninds(ψ[vsrc], ϕ[vsrc]) + top_src_ncind = setdiff(inds(ψ[vsrc]), [vsrc_siteinds; top_cind]) + bottom_src_ncind = setdiff(inds(ϕ[vsrc]), [vsrc_siteinds; bottom_cind]) + if isempty(top_src_ncind) + E = ψ[vsrc] * ϕ[vsrc] + else + E = ψ[vsrc] * replaceind(ϕ[vsrc], only(bottom_src_ncind), only(top_src_ncind)) + end + + U, S, V = svd(E, bottom_cind; alg = "recursive") + S_sqrtinv = map_diag(x -> pinv(sqrt(x + regularization)), S) + S_sqrt = map_diag(x -> sqrt(x + regularization), S) + ψ[vsrc] = replaceind((ψ[vsrc] * dag(V)) * S_sqrtinv, commonind(U, S), top_cind) + ϕ[vsrc] = replaceind((ϕ[vsrc] * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind) + + ψ[vdst] = replaceind((ψ[vdst] * V) * S_sqrt, commonind(U, S), top_cind) + ϕ[vdst] = replaceind((ϕ[vdst] * U) * S_sqrt, commonind(V, S), bottom_cind) + + end + + return ψ, ϕ end \ No newline at end of file From 2ca6404fba2d2d271facaf7328831dd514b90008 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 31 Dec 2024 12:36:11 -0500 Subject: [PATCH 18/47] Biorthogonal algorithm --- examples/test_boundarymps.jl | 19 +-- src/caches/boundarympscache.jl | 205 ++++++++++++--------------------- 2 files changed, 84 insertions(+), 140 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index 4b1952ae..a2e817ff 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -1,8 +1,8 @@ using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, partitionedges, messages, update, partition_update, set_messages, message, planargraph_partitionedges, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, insert_linkinds, biorthognal_update - partitioned_tensornetwork, default_message + default_message_update, contraction_sequence, insert_linkinds, biorthognal_update, + partitioned_tensornetwork, default_message, biorthogonalize using OMEinsumContractionOrders using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensorNetworks.ModelHamiltonians: ising @@ -63,13 +63,14 @@ ITensors.disable_warn_order() function main() - L = 12 - #g = lieb_lattice_grid(L, L) + L = 5 + g = lieb_lattice_grid(L, L) #g = named_hexagonal_lattice_graph(L, L) - g = named_grid_periodic_x((L,2)) + #g = named_grid_periodic_x((L,2)) + #g = named_grid((L, 3)) vc = first(center(g)) s = siteinds("S=1/2", g) - ψ = random_tensornetwork(ComplexF64, s; link_space = 2) + ψ = random_tensornetwork(s; link_space = 4) bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) #Run BP first to normalize and put in a stable gauge @@ -83,15 +84,15 @@ function main() fit_kwargs = (;niters = 50, tolerance = 1e-12) ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - + #ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = 4) rs = [16] @show exact_expect(ψ, "Z", vc) for r in rs ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = r) - #ψIψ = update(ψIψ; maxiter = 5, mps_fit_kwargs = fit_kwargs) - ψIψ = biorthognal_update(ψIψ; maxiter = 5) + #ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) + ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] @show sz diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index f12ab15c..b9165ec8 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -6,7 +6,7 @@ using ITensorMPS: ITensorMPS, orthogonalize using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition using SplitApplyCombine: group -using ITensors: commoninds +using ITensors: commoninds, random_itensor using LinearAlgebra: pinv pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) @@ -31,8 +31,10 @@ tensornetwork(bmpsc::BoundaryMPSCache) = tensornetwork(bp_cache(bmpsc)) partitioned_tensornetwork(bmpsc::BoundaryMPSCache) = partitioned_tensornetwork(bp_cache(bmpsc)) default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) -default_bp_maxiter(bmpsc::BoundaryMPSCache) = default_bp_maxiter(partitioned_graph(ppg(bmpsc))) -default_mps_fit_kwargs(bmpsc::BoundaryMPSCache) = (; niters = 25, tolerance = 1e-10) +default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) = default_bp_maxiter(partitioned_graph(ppg(bmpsc))) +default_bp_maxiter(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = 50 +default_mps_fit_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) = (; niters = 50, tolerance = 1e-10) +default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (; ) function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) @@ -76,9 +78,9 @@ end #Get all partitionedges between the pair of neighboring partitions, sorted top to bottom #TODO: Bring in line with NamedGraphs change -function planargraph_partitionpair_partitionedges(bmpsc::BoundaryMPSCache, pe::Pair) +function planargraph_partitionpair_partitionedges(bmpsc::BoundaryMPSCache, partitionpair::Pair) pg = planargraph(bmpsc) - src_vs, dst_vs = planargraph_vertices(bmpsc, first(pe)), planargraph_vertices(bmpsc, last(pe)) + src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), planargraph_vertices(bmpsc, last(partitionpair)) es = filter(x -> !isempty(last(x)), [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs]) es = map(x -> first(x) => only(last(x)), es) return sort(PartitionEdge.(NamedEdge.(es)); by = x -> src(parent(x))) @@ -92,22 +94,29 @@ function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITens end #Initialise all the message tensors for the pair of neighboring partitions, with virtual rank given by message rank -function set_interpartition_messages(bmpsc::BoundaryMPSCache, pe::Pair, message_rank::Int64) +function set_interpartition_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair, message_rank::Int64) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionpair_partitionedges(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) prev_virtual_ind = nothing for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing - inds = filter(x -> !isnothing(x), [siteinds; prev_virtual_ind; next_virtual_index]) - set!(ms, pg_pe, ITensor[delta(inds)]) + me = delta(siteinds) + if !isnothing(prev_virtual_ind) + me *= ITensor(1.0, prev_virtual_ind) + end + if !isnothing(next_virtual_index) + me *= ITensor(1.0, next_virtual_index) + end + + set!(ms, pg_pe, ITensor[me]) prev_virtual_ind = next_virtual_index end return bmpsc end -#Initialise all the inerpartition message tensors with virtual rank given by message rank +#Initialise all the interpartition message tensors with virtual rank given by message rank function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64 = 1) bmpsc = copy(bmpsc) pes = partitionedges(ppg(bmpsc)) @@ -118,10 +127,10 @@ function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int6 end #Switch the message tensors from column/row i -> i + 1 with those from i + 1 -> i -function switch_messages(bmpsc::BoundaryMPSCache, pe::Pair) +function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionpair_partitionedges(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) for pe_i in pes me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) set!(ms, pe_i, dag.(mer)) @@ -151,7 +160,7 @@ function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) end #Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 -function orthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) +function gauge_step(alg::Algorithm"orthogonalize", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) bmpsc = copy(bmpsc) ms = messages(bmpsc) m1, m2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) @@ -164,46 +173,53 @@ function orthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2: return bmpsc end -#Move the orthogonality centre on an interpartition via a sequence of steps between message tensors -function orthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) - for (pe1, pe2) in seq - bmpsc = orthogonal_gauge_step(bmpsc, pe1, pe2) - end +#Move the biorthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 +function gauge_step(alg::Algorithm"biorthogonalize", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; regularization = 1e-12) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + + m1, m1r = only(message(bmpsc, pe1)), only(message(bmpsc, reverse(pe1))) + m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) + top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) + m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) + top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), setdiff(inds(m1r), [m1_siteinds; bottom_cind]) + + E = isempty(top_ncind) ? m1 * m1r : m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) + U, S, V = svd(E, bottom_cind; alg = "recursive") + + S_sqrtinv = map_diag(x -> pinv(sqrt(x)), S) + S_sqrt = map_diag(x -> sqrt(x), S) + + set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) + set!(ms, reverse(pe1), ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)]) + set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) + set!(ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)]) + return bmpsc end -#Move the biorthogonality centre on an interpartition via a sequence of steps between message tensors -function biorthogonal_gauge_walk(bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) +#Move the orthogonality / biorthogonality centre on an interpartition via a sequence of steps between message tensors +function gauge_walk(alg::Algorithm, bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) for (pe1, pe2) in seq - bmpsc = biorthogonal_gauge_step(bmpsc, pe1, pe2) + bmpsc = gauge_step(alg::Algorithm, bmpsc, pe1, pe2) end return bmpsc end -#Move the orthogonality centre on an interpartition to the message tensor on pe -function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) - return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe); kwargs...) +#Move the orthogonality centre on an interpartition to the message tensor on pe or between two pes +function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) + return gauge_walk(Algorithm("orthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs...) end -#Move the orthogonality centre on an interpartition from the message tensor on pe1 to that on pe2 -function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) - return orthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe1, pe2); kwargs...) -end - -#Move the biorthogonality centre on an interpartition to the message tensor on pe -function biorthogonalize(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) - return biorthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe); kwargs...) -end - -#Move the biorthogonality centre on an interpartition from the message tensor on pe1 to that on pe2 -function biorthogonalize(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) - return biorthogonal_gauge_walk(bmpsc, mps_gauge_update_sequence(bmpsc, pe1, pe2); kwargs...) +#Move the biorthogonality centre on an interpartition to the message tensor or between two pes +function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) + return gauge_walk(Algorithm("biorthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs...) end #Update all the message tensors on an interpartition via an orthogonal fitting procedure -function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; niters::Int64 = 25, tolerance = 1e-10, normalize = true) - bmpsc = switch_messages(bmpsc, pe) - pes = planargraph_partitionpair_partitionedges(bmpsc, pe) +function mps_update(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; niters::Int64 = 25, tolerance = 1e-10, normalize = true) + bmpsc = switch_messages(bmpsc, partitionpair) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) update_seq = vcat(pes, reverse(pes)[2:length(pes)]) prev_v, prev_pe = nothing, nothing prev_costfunction = 0 @@ -220,26 +236,28 @@ function orthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; niters::Int64 end epsilon = abs(costfunction - prev_costfunction) / length(update_seq) if !isnothing(tolerance) && epsilon < tolerance - return switch_messages(bmpsc, pe) + return switch_messages(bmpsc, partitionpair) else prev_costfunction = costfunction end end - return switch_messages(bmpsc, pe) + return switch_messages(bmpsc, partitionpair) end #Update all the message tensors on an interpartition via a biorthogonal fitting procedure -function biorthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; normalize = true) - pes = planargraph_partitionpair_partitionedges(bmpsc, pe) - update_seq = pes +function mps_update(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize = true) prev_v, prev_pe = nothing, nothing - for update_pe in update_seq + for update_pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) cur_v = parent(src(update_pe)) bmpsc = !isnothing(prev_pe) ? biorthogonalize(bmpsc, prev_pe, update_pe) : biorthogonalize(bmpsc, update_pe) bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) : partition_update(bmpsc, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) + me_prev = only(message(bmpsc, update_pe)) me = only(update_message(bmpsc, update_pe; message_update = ms -> default_message_update(ms; normalize))) - mer = only(message(bmpsc, reverse(update_pe))) - me = replaceinds(me, noncommoninds(me, mer), noncommoninds(mer, me)) + is, is_to_replace = uniqueinds(me, me_prev), uniqueinds(me_prev, me) + for i in is + i_replacement = only(filter(ip -> tags(ip) == tags(i), is_to_replace)) + me = replaceind(me, i, i_replacement) + end bmpsc = set_message(bmpsc, update_pe, ITensor[me]) prev_v, prev_pe = cur_v, update_pe end @@ -247,49 +265,31 @@ function biorthogonal_mps_update(bmpsc::BoundaryMPSCache, pe::Pair; normalize = return bmpsc end - """ More generic interface for update, with default params """ function update( + alg::Algorithm, bmpsc::BoundaryMPSCache; - pes=default_edge_sequence(bmpsc), - maxiter=default_bp_maxiter(bmpsc), - mps_fit_kwargs = default_mps_fit_kwargs(bmpsc) + partitionpairs=default_edge_sequence(bmpsc), + maxiter=default_bp_maxiter(alg, bmpsc), + mps_fit_kwargs = default_mps_fit_kwargs(alg, bmpsc) ) if isnothing(maxiter) error("You need to specify a number of iterations for Boundary MPS!") end for i in 1:maxiter - for pe in pes - bmpsc = orthogonal_mps_update(bmpsc, pe; mps_fit_kwargs...) + for partitionpair in partitionpairs + bmpsc = mps_update(alg, bmpsc, partitionpair; mps_fit_kwargs...) end end return bmpsc end -""" -More generic interface for update, with default params -""" -function biorthognal_update( - bmpsc::BoundaryMPSCache; - pes=default_edge_sequence(bmpsc), - maxiter=25, -) - if isnothing(maxiter) - error("You need to specify a number of iterations for Boundary MPS!") - end - for i in 1:maxiter - for pe in pes - bmpsc = biorthogonal_mps_update(bmpsc, pe) - end - end - return bmpsc +function update(bmpsc::BoundaryMPSCache; alg::String = "orthogonal", kwargs...) + return update(Algorithm(alg), bmpsc; kwargs...) end - - - function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) pv = only(planargraph_partitions(bmpsc, vs)) @@ -368,61 +368,4 @@ function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> f end end return add_pseudo_partitionedges(bpc, pseudo_edges) -end - - -function biorthogonal_gauge_step(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; regularization = 0) - bmpsc = copy(bmpsc) - ms = messages(bmpsc) - - m1, m1r = only(message(bmpsc, pe1)), only(message(bmpsc, reverse(pe1))) - m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) - top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) - m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) - top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), setdiff(inds(m1r), [m1_siteinds; bottom_cind]) - - E = isempty(top_ncind) ? m1 * m1r : m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) - U, S, V = svd(E, bottom_cind; alg = "recursive") - S_sqrtinv = map_diag(x -> pinv(sqrt(x + regularization)), S) - S_sqrt = map_diag(x -> sqrt(x + regularization), S) - - set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) - set!(ms, reverse(pe1), ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)]) - set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) - set!(ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)]) - - @show inds(only(ms[pe1])), inds(only(ms[reverse(pe1)])) - @show inds(only(ms[pe2])), inds(only(ms[reverse(pe2)])) - return bmpsc -end - -function biorthogonalize_walk(ψ::ITensorNetwork, ϕ::ITensorNetwork, path::Vector{<:NamedEdge}; - regularization = 0) - ψ, ϕ = copy(ψ), copy(ϕ) - ep = first(path) - for e in path - vsrc, vdst = src(e), dst(e) - top_cind = commonind(ψ[vsrc], ψ[vdst]) - bottom_cind = commonind(ϕ[vsrc], ϕ[vdst]) - vsrc_siteinds = commoninds(ψ[vsrc], ϕ[vsrc]) - top_src_ncind = setdiff(inds(ψ[vsrc]), [vsrc_siteinds; top_cind]) - bottom_src_ncind = setdiff(inds(ϕ[vsrc]), [vsrc_siteinds; bottom_cind]) - if isempty(top_src_ncind) - E = ψ[vsrc] * ϕ[vsrc] - else - E = ψ[vsrc] * replaceind(ϕ[vsrc], only(bottom_src_ncind), only(top_src_ncind)) - end - - U, S, V = svd(E, bottom_cind; alg = "recursive") - S_sqrtinv = map_diag(x -> pinv(sqrt(x + regularization)), S) - S_sqrt = map_diag(x -> sqrt(x + regularization), S) - ψ[vsrc] = replaceind((ψ[vsrc] * dag(V)) * S_sqrtinv, commonind(U, S), top_cind) - ϕ[vsrc] = replaceind((ϕ[vsrc] * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind) - - ψ[vdst] = replaceind((ψ[vdst] * V) * S_sqrt, commonind(U, S), top_cind) - ϕ[vdst] = replaceind((ϕ[vdst] * U) * S_sqrt, commonind(V, S), bottom_cind) - - end - - return ψ, ϕ end \ No newline at end of file From 05ae49aecbbb3c56ed6a4bcfb8a34c7f35e75b81 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 31 Dec 2024 12:49:09 -0500 Subject: [PATCH 19/47] File Structure --- examples/test_boundarymps.jl | 6 +++--- src/ITensorNetworks.jl | 1 + src/caches/boundarympscache.jl | 39 +--------------------------------- 3 files changed, 5 insertions(+), 41 deletions(-) diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl index a2e817ff..a356db6e 100644 --- a/examples/test_boundarymps.jl +++ b/examples/test_boundarymps.jl @@ -85,14 +85,14 @@ function main() ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) #ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = 4) - rs = [16] + rs = [1,2,4,8,16] @show exact_expect(ψ, "Z", vc) for r in rs ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = r) - #ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) - ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) + ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) + #ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] @show sz diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index b7d56fa7..85126258 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -29,6 +29,7 @@ include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") include("caches/beliefpropagationcache.jl") +include("caches/boundarympscacheutils.jl") include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index b9165ec8..b93bcef0 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -9,15 +9,6 @@ using SplitApplyCombine: group using ITensors: commoninds, random_itensor using LinearAlgebra: pinv -pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) - -#Return a sequence of pairs to go from item1 to item2 in an ordered_list -function pair_sequence(ordered_list::Vector, item1, item2) - item1_pos, item2_pos = only(findall(x->x==item1, ordered_list)), only(findall(x->x==item2, ordered_list)) - item1_pos < item2_pos && return [ordered_list[i] => ordered_list[i+1] for i in item1_pos:(item2_pos-1)] - return [ordered_list[i] => ordered_list[i-1] for i in item1_pos:-1:(item2_pos+1)] -end - struct BoundaryMPSCache{BPC,PG} bp_cache::BPC partitionedplanargraph::PG @@ -290,6 +281,7 @@ function update(bmpsc::BoundaryMPSCache; alg::String = "orthogonal", kwargs...) return update(Algorithm(alg), bmpsc; kwargs...) end +#Assume all vertices live in the same partition for now function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) pv = only(planargraph_partitions(bmpsc, vs)) @@ -339,33 +331,4 @@ function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs... bpc = bp_cache(bmpsc) bpc = update(bpc, pes; kwargs...) return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) -end - -#Add partition edges that may not have meaning in the underlying graph -function add_pseudo_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) - g = partitioned_graph(pg) - g = add_edges(g, parent.(pes)) - return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) -end - -#Add partition edges that may not have meaning in the underlying graph -function add_pseudo_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) - pg = add_pseudo_partitionedges(partitioned_tensornetwork(bpc), pes) - return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) -end - -#Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function -function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) - pg = partitioned_graph(bpc) - partitions = unique(sort_f.(collect(vertices(pg)))) - pseudo_edges = PartitionEdge[] - for p in partitions - vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) - for i in 1:(length(vs) - 1) - if vs[i] ∉ neighbors(pg, vs[i+1]) - push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) - end - end - end - return add_pseudo_partitionedges(bpc, pseudo_edges) end \ No newline at end of file From 9c5f6c433dd044f0c90dbae20de19e0f5525eb3b Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 31 Dec 2024 12:49:37 -0500 Subject: [PATCH 20/47] Utils --- src/caches/boundarympscacheutils.jl | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/caches/boundarympscacheutils.jl diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl new file mode 100644 index 00000000..63a2feb2 --- /dev/null +++ b/src/caches/boundarympscacheutils.jl @@ -0,0 +1,41 @@ +using ITensorNetworks: ITensorNetworks, BeliefPropagationCache +using NamedGraphs.PartitionedGraphs: PartitionedGraph + +#Add partition edges that may not have meaning in the underlying graph +function add_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) + g = partitioned_graph(pg) + g = add_edges(g, parent.(pes)) + return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) +end + +#Add partition edges that may not have meaning in the underlying graph +function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) + pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) + return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) +end + +#Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function +function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) + pg = partitioned_graph(bpc) + partitions = unique(sort_f.(collect(vertices(pg)))) + pseudo_edges = PartitionEdge[] + for p in partitions + vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) + for i in 1:(length(vs) - 1) + if vs[i] ∉ neighbors(pg, vs[i+1]) + push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) + end + end + end + return add_partitionedges(bpc, pseudo_edges) +end + + +pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) + +#Return a sequence of pairs to go from item1 to item2 in an ordered_list +function pair_sequence(ordered_list::Vector, item1, item2) + item1_pos, item2_pos = only(findall(x->x==item1, ordered_list)), only(findall(x->x==item2, ordered_list)) + item1_pos < item2_pos && return [ordered_list[i] => ordered_list[i+1] for i in item1_pos:(item2_pos-1)] + return [ordered_list[i] => ordered_list[i-1] for i in item1_pos:-1:(item2_pos+1)] +end \ No newline at end of file From 12722dadf1e3540c87f3aa30c0ca2ee7ce8c4815 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 31 Dec 2024 16:04:09 -0500 Subject: [PATCH 21/47] Add test --- src/caches/boundarympscache.jl | 4 ++ test/test_boundarymps.jl | 99 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 test/test_boundarymps.jl diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index b93bcef0..56f8081c 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -46,6 +46,10 @@ function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> f return set_interpartition_messages(bmpsc, message_rank) end +function BoundaryMPSCache(tn::AbstractITensorNetwork; kwargs...) + return BoundaryMPSCache(BeliefPropagationCache(tn); kwargs...) +end + #Get all partitionedges within a column/row, ordered top to bottom function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) vs = sort(planargraph_vertices(bmpsc, partition)) diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl new file mode 100644 index 00000000..39dd33a6 --- /dev/null +++ b/test/test_boundarymps.jl @@ -0,0 +1,99 @@ +@eval module $(gensym()) +using Compat: Compat +using Graphs: vertices, center +# Trigger package extension. +using ITensorNetworks: + ITensorNetworks, + BeliefPropagationCache, + BoundaryMPSCache, + QuadraticFormNetwork, + ⊗, + combine_linkinds, + contract, + contract_boundary_mps, + contraction_sequence, + eachtensor, + environment, + inner_network, + linkinds_combiners, + message, + partitioned_tensornetwork, + random_tensornetwork, + scalar, + siteinds, + split_index, + tensornetwork, + update, + update_factor, + update_message, + message_diff +using ITensors: ITensors, ITensor, combiner, dag, inds, inner, op, prime, random_itensor +using ITensorNetworks.ModelNetworks: ModelNetworks +using ITensors.NDTensors: array +using LinearAlgebra: eigvals, tr +using NamedGraphs: NamedEdge, NamedGraph, subgraph +using NamedGraphs.GraphsExtensions: rem_vertices +using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid +using NamedGraphs.PartitionedGraphs: PartitionVertex, partitionedges +using SplitApplyCombine: group +using StableRNGs: StableRNG +using Test: @test, @testset +using OMEinsumContractionOrders +using LinearAlgebra: norm + +@testset "boundarymps (eltype=$elt)" for elt in ( + Float32, Float64, Complex{Float32}, Complex{Float64} +) + begin + ITensors.disable_warn_order() + mps_fit_kwargs = (; niters = 50, tolerance = 1e-10) + + #First a comb tree (which is still a planar graph) + g = named_comb_tree((4,4)) + s = siteinds("S=1/2", g) + χ = 2 + rng = StableRNG(1234) + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + ψIψ = QuadraticFormNetwork(ψ) + vc = (first(center(g)), "operator") + + ρ_bp = contract(environment(ψIψ, [vc]; alg = "bp"); sequence = "automatic") + ρ_bp /= tr(ρ_bp) + + ρ = subgraph(ψIψ, setdiff(vertices(ψIψ), [vc])) + ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) + ρ_exact /= tr(ρ_exact) + + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= eps(real(elt)) + @test norm(ρ_boundaryMPS - ρ_bp) <= eps(real(elt)) + + #Now a square graph with a few vertices missing for added complexity + g = named_grid((5, 5)) + g = rem_vertices(g, [(2,2), (3,3)]) + s = siteinds("S=1/2", g) + χ = 3 + rng = StableRNG(1234) + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + ψIψ = QuadraticFormNetwork(ψ) + vc = (first(center(g)), "operator") + ρ = subgraph(ψIψ, setdiff(vertices(ψIψ), [vc])) + ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) + ρ_exact /= tr(ρ_exact) + + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= 10*eps(real(elt)) + + + + end +end +end From f7c6bebb421849e0185cafde3ee948876d7c691b Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 1 Jan 2025 13:21:49 +0000 Subject: [PATCH 22/47] More tests --- test/test_boundarymps.jl | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 39dd33a6..c6907763 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -6,7 +6,9 @@ using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, BoundaryMPSCache, + ITensorNetwork, QuadraticFormNetwork, + VidalITensorNetwork, ⊗, combine_linkinds, contract, @@ -17,6 +19,7 @@ using ITensorNetworks: inner_network, linkinds_combiners, message, + messages, partitioned_tensornetwork, random_tensornetwork, scalar, @@ -27,14 +30,14 @@ using ITensorNetworks: update_factor, update_message, message_diff -using ITensors: ITensors, ITensor, combiner, dag, inds, inner, op, prime, random_itensor +using ITensors: ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor using ITensorNetworks.ModelNetworks: ModelNetworks using ITensors.NDTensors: array using LinearAlgebra: eigvals, tr using NamedGraphs: NamedEdge, NamedGraph, subgraph using NamedGraphs.GraphsExtensions: rem_vertices -using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid -using NamedGraphs.PartitionedGraphs: PartitionVertex, partitionedges +using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid, named_hexagonal_lattice_graph +using NamedGraphs.PartitionedGraphs: PartitionEdge, PartitionVertex, partitionedges using SplitApplyCombine: group using StableRNGs: StableRNG using Test: @test, @testset @@ -47,12 +50,12 @@ using LinearAlgebra: norm begin ITensors.disable_warn_order() mps_fit_kwargs = (; niters = 50, tolerance = 1e-10) + rng = StableRNG(1234) #First a comb tree (which is still a planar graph) g = named_comb_tree((4,4)) s = siteinds("S=1/2", g) χ = 2 - rng = StableRNG(1234) ψ = random_tensornetwork(rng, elt, s; link_space=χ) ψIψ = QuadraticFormNetwork(ψ) vc = (first(center(g)), "operator") @@ -77,7 +80,6 @@ using LinearAlgebra: norm g = rem_vertices(g, [(2,2), (3,3)]) s = siteinds("S=1/2", g) χ = 3 - rng = StableRNG(1234) ψ = random_tensornetwork(rng, elt, s; link_space=χ) ψIψ = QuadraticFormNetwork(ψ) vc = (first(center(g)), "operator") @@ -91,6 +93,31 @@ using LinearAlgebra: norm ρ_boundaryMPS /= tr(ρ_boundaryMPS) @test norm(ρ_boundaryMPS - ρ_exact) <= 10*eps(real(elt)) + + #Now test BP and Boundary MPS are equivalent in the symmetric gauge + g = named_hexagonal_lattice_graph(5, 5) + s = siteinds("S=1/2", g) + χ = 2 + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + + #Move to symmetric gauge + ψ_vidal = VidalITensorNetwork(ψ; cache_update_kwargs=(; maxiter=30)) + cache_ref = Ref{BeliefPropagationCache}() + ψ_symm = ITensorNetwork(ψ_vidal; (cache!) = cache_ref) + bp_cache = cache_ref[] + + #Do Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank = 1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) + + for pe in keys(messages(bp_cache)) + m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) + m_boundarymps = m_boundarymps * ITensor(1.0, filter(i -> dim(i) ==1, inds(m_boundarymps))) + m_bp = only(message(bp_cache, pe)) + m_bp /= norm(m_bp) + m_boundarymps /= norm(m_boundarymps) + @show norm(m_bp - m_boundarymps) + end From 7d4465b5fcc556f686a147cc10d8a089b262b511 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 1 Jan 2025 15:47:34 +0000 Subject: [PATCH 23/47] Testing --- src/caches/boundarympscache.jl | 5 ++- test/test_boundarymps.jl | 57 ++++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 56f8081c..f8c7399c 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -108,6 +108,7 @@ function set_interpartition_messages(bmpsc::BoundaryMPSCache, partitionpair::Pai set!(ms, pg_pe, ITensor[me]) prev_virtual_ind = next_virtual_index end + return bmpsc end @@ -335,4 +336,6 @@ function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs... bpc = bp_cache(bmpsc) bpc = update(bpc, pes; kwargs...) return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) -end \ No newline at end of file +end + +#TODO: Function to get the virtual indices coming from above and below an interparititon message tensor \ No newline at end of file diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index c6907763..1604bde4 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -14,6 +14,7 @@ using ITensorNetworks: contract, contract_boundary_mps, contraction_sequence, + default_message_update, eachtensor, environment, inner_network, @@ -32,6 +33,7 @@ using ITensorNetworks: message_diff using ITensors: ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor using ITensorNetworks.ModelNetworks: ModelNetworks +using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensors.NDTensors: array using LinearAlgebra: eigvals, tr using NamedGraphs: NamedEdge, NamedGraph, subgraph @@ -67,7 +69,8 @@ using LinearAlgebra: norm ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) ρ_exact /= tr(ρ_exact) - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) + #Orthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank =1) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") ρ_boundaryMPS /= tr(ρ_boundaryMPS) @@ -75,6 +78,15 @@ using LinearAlgebra: norm @test norm(ρ_boundaryMPS - ρ_exact) <= eps(real(elt)) @test norm(ρ_boundaryMPS - ρ_bp) <= eps(real(elt)) + #Biorthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank =1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal") + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= eps(real(elt)) + @test norm(ρ_boundaryMPS - ρ_bp) <= eps(real(elt)) + #Now a square graph with a few vertices missing for added complexity g = named_grid((5, 5)) g = rem_vertices(g, [(2,2), (3,3)]) @@ -87,6 +99,7 @@ using LinearAlgebra: norm ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) ρ_exact /= tr(ρ_exact) + #Orthogonal Boundary MPS ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") @@ -94,32 +107,56 @@ using LinearAlgebra: norm @test norm(ρ_boundaryMPS - ρ_exact) <= 10*eps(real(elt)) + #BiOrthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal", maxiter = 100) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= 100000*eps(real(elt)) + #Now test BP and Boundary MPS are equivalent in the symmetric gauge - g = named_hexagonal_lattice_graph(5, 5) + g = named_hexagonal_lattice_graph(3,3) s = siteinds("S=1/2", g) χ = 2 ψ = random_tensornetwork(rng, elt, s; link_space=χ) - #Move to symmetric gauge - ψ_vidal = VidalITensorNetwork(ψ; cache_update_kwargs=(; maxiter=30)) + #Move wavefunction to symmetric gauge, enforce posdefness for complex number stability + function make_posdef(A::ITensor) + return map_eigvals(x -> abs(real(x)), A, first(inds(A)), last(inds(A)); ishermitian=true) + end + message_update_f = ms -> make_posdef.(default_message_update(ms)) + ψ_vidal = VidalITensorNetwork(ψ; cache_update_kwargs=(; message_update = message_update_f, maxiter=100, tol = 1e-16)) cache_ref = Ref{BeliefPropagationCache}() ψ_symm = ITensorNetwork(ψ_vidal; (cache!) = cache_ref) bp_cache = cache_ref[] - #Do Boundary MPS + #Do Orthogonal Boundary MPS ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank = 1) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) for pe in keys(messages(bp_cache)) m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) m_boundarymps = m_boundarymps * ITensor(1.0, filter(i -> dim(i) ==1, inds(m_boundarymps))) m_bp = only(message(bp_cache, pe)) - m_bp /= norm(m_bp) - m_boundarymps /= norm(m_boundarymps) - @show norm(m_bp - m_boundarymps) + m_bp /= tr(m_bp) + m_boundarymps /= tr(m_boundarymps) + @test norm(m_bp - m_boundarymps) <= 1e-4 end - + #Do Biorthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank = 1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal") + + for pe in keys(messages(bp_cache)) + m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) + #Prune the dimension 1 virtual index from boundary MPS + m_boundarymps = m_boundarymps * ITensor(1.0, filter(i -> dim(i) ==1, inds(m_boundarymps))) + m_bp = only(message(bp_cache, pe)) + m_bp /= tr(m_bp) + m_boundarymps /= tr(m_boundarymps) + @test norm(m_bp - m_boundarymps) <= 1e-4 + end end end From 1b23fbb8ca853bf06140085403cc26877ab6f790 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 10:52:01 +0000 Subject: [PATCH 24/47] Working Commit --- examples/test_boundarymps.jl | 102 --------- src/caches/boundarympscache.jl | 314 ++++++++++++++++++++-------- src/caches/boundarympscacheutils.jl | 51 ++--- test/test_boundarymps.jl | 102 ++++----- 4 files changed, 305 insertions(+), 264 deletions(-) delete mode 100644 examples/test_boundarymps.jl diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl deleted file mode 100644 index a356db6e..00000000 --- a/examples/test_boundarymps.jl +++ /dev/null @@ -1,102 +0,0 @@ -using ITensorNetworks: BoundaryMPSCache, BeliefPropagationCache, QuadraticFormNetwork, IndsNetwork, siteinds, ttn, random_tensornetwork, - partitionedges, messages, update, partition_update, set_messages, message, - planargraph_partitionedges, switch_messages, orthogonal_mps_update, environment, VidalITensorNetwork, ITensorNetwork, expect, - default_message_update, contraction_sequence, insert_linkinds, biorthognal_update, - partitioned_tensornetwork, default_message, biorthogonalize -using OMEinsumContractionOrders -using ITensorNetworks.ITensorsExtensions: map_eigvals -using ITensorNetworks.ModelHamiltonians: ising -using ITensors: ITensor, ITensors, Index, OpSum, terms, sites, contract, commonind, replaceind, replaceinds, prime, dag, noncommonind, noncommoninds, inds -using NamedGraphs: NamedGraphs, AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors -using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph -using NamedGraphs.GraphsExtensions: rem_vertex, add_edges, add_edge -using NamedGraphs.PartitionedGraphs: PartitionedGraph, partitioned_graph, PartitionVertex, PartitionEdge, unpartitioned_graph, - partitioned_vertices, which_partition -using LinearAlgebra: normalize -using Graphs: center - -using Random - -function lieb_lattice_grid(nx::Int64, ny::Int64; periodic=false) - @assert (!periodic && isodd(nx) && isodd(ny)) || (periodic && iseven(nx) && iseven(ny)) - g = named_grid((nx, ny); periodic) - for v in vertices(g) - if iseven(first(v)) && iseven(last(v)) - g = rem_vertex(g, v) - end - end - return g -end - -function named_grid_periodic_x(nxny::Tuple) - nx, ny = nxny - g = named_grid((nx, ny)) - for i in 1:ny - g = add_edge(g, NamedEdge((nx, i) => (1, i))) - end - return g -end - -function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) - s = siteinds(ψ) - ψIψ = QuadraticFormNetwork(ψ) - ψOψ = QuadraticFormNetwork(ψ) - for (op_string, v) in zip(ops, vs) - ψOψ[(v, "operator")] = ITensors.op(op_string, s[v]) - end - numer_seq = contraction_sequence(ψOψ; alg="sa_bipartite") - denom_seq = contraction_sequence(ψIψ; alg="sa_bipartite") - numer, denom = contract(ψOψ; sequence=numer_seq)[], contract(ψIψ; sequence=denom_seq)[] - return numer / denom -end - -function exact_expect(ψ::ITensorNetwork, op_string::String, v) - return exact_expect(ψ, [op_string], [v]) -end - -function make_eigs_real(A::ITensor) - return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) -end - -Random.seed!(1834) -ITensors.disable_warn_order() - - -function main() - L = 5 - g = lieb_lattice_grid(L, L) - #g = named_hexagonal_lattice_graph(L, L) - #g = named_grid_periodic_x((L,2)) - #g = named_grid((L, 3)) - vc = first(center(g)) - s = siteinds("S=1/2", g) - ψ = random_tensornetwork(s; link_space = 4) - bp_update_kwargs = (; maxiter = 50, tol = 1e-14, message_update = ms -> make_eigs_real.(default_message_update(ms))) - - #Run BP first to normalize and put in a stable gauge - #bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - #bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) - ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - ψIψ = update(ψIψ; bp_update_kwargs...) - ψ = VidalITensorNetwork(ψ; cache! = Ref(ψIψ), update_cache = false, cache_update_kwargs = (; maxiter = 0)) - ψ = ITensorNetwork(ψ) - - fit_kwargs = (;niters = 50, tolerance = 1e-12) - - ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - #ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = 4) - rs = [1,2,4,8,16] - - @show exact_expect(ψ, "Z", vc) - - for r in rs - ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = r) - ψIψ = update(ψIψ; mps_fit_kwargs = fit_kwargs) - #ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) - ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence = "automatic") - sz = contract([ρ, ITensors.op("Z", s[vc])])[] /contract([ρ, ITensors.op("I", s[vc])])[] - @show sz - end -end - -main() \ No newline at end of file diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index f8c7399c..c5c57b4b 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -3,15 +3,15 @@ using NamedGraphs.GraphsExtensions: add_edges using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, region_scalar using ITensorNetworks.ITensorsExtensions: map_diag using ITensorMPS: ITensorMPS, orthogonalize -using NamedGraphs.PartitionedGraphs: partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, - which_partition +using NamedGraphs.PartitionedGraphs: + partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition using SplitApplyCombine: group using ITensors: commoninds, random_itensor using LinearAlgebra: pinv struct BoundaryMPSCache{BPC,PG} - bp_cache::BPC - partitionedplanargraph::PG + bp_cache::BPC + partitionedplanargraph::PG end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache @@ -19,25 +19,40 @@ partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph ppg(bmpsc) = partitionedplanargraph(bmpsc) planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) tensornetwork(bmpsc::BoundaryMPSCache) = tensornetwork(bp_cache(bmpsc)) -partitioned_tensornetwork(bmpsc::BoundaryMPSCache) = partitioned_tensornetwork(bp_cache(bmpsc)) +function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) + return partitioned_tensornetwork(bp_cache(bmpsc)) +end default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) -default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) = default_bp_maxiter(partitioned_graph(ppg(bmpsc))) +function default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) + return default_bp_maxiter(partitioned_graph(ppg(bmpsc))) +end default_bp_maxiter(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = 50 -default_mps_fit_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) = (; niters = 50, tolerance = 1e-10) -default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (; ) +function default_mps_fit_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) + return (; niters=50, tolerance=1e-10) +end +default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) end -planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) = vertices(ppg(bmpsc), PartitionVertex(partition)) -planargraph_partition(bmpsc::BoundaryMPSCache, vertex) = parent(partitionvertex(ppg(bmpsc), vertex)) -planargraph_partitions(bmpsc::BoundaryMPSCache, verts) = parent.(partitionvertices(ppg(bmpsc), verts)) -planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) = pair(partitionedge(ppg(bmpsc), parent(pe))) +function planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) + return vertices(ppg(bmpsc), PartitionVertex(partition)) +end +function planargraph_partition(bmpsc::BoundaryMPSCache, vertex) + return parent(partitionvertex(ppg(bmpsc), vertex)) +end +function planargraph_partitions(bmpsc::BoundaryMPSCache, verts) + return parent.(partitionvertices(ppg(bmpsc), verts)) +end +function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + return pair(partitionedge(ppg(bmpsc), parent(pe))) +end -function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> first(v), - message_rank::Int64=1) +function BoundaryMPSCache( + bpc::BeliefPropagationCache; sort_f::Function=v -> first(v), message_rank::Int64=1 +) bpc = insert_pseudo_planar_edges(bpc; sort_f) planar_graph = partitioned_graph(bpc) vertex_groups = group(sort_f, collect(vertices(planar_graph))) @@ -46,39 +61,70 @@ function BoundaryMPSCache(bpc::BeliefPropagationCache; sort_f::Function = v -> f return set_interpartition_messages(bmpsc, message_rank) end -function BoundaryMPSCache(tn::AbstractITensorNetwork; kwargs...) - return BoundaryMPSCache(BeliefPropagationCache(tn); kwargs...) +function BoundaryMPSCache(tn::AbstractITensorNetwork, args...; kwargs...) + return BoundaryMPSCache(BeliefPropagationCache(tn, args...); kwargs...) end #Get all partitionedges within a column/row, ordered top to bottom function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) vs = sort(planargraph_vertices(bmpsc, partition)) - return PartitionEdge.([vs[i] => vs[i+1] for i in 1:(length(vs)-1)]) + return PartitionEdge.([vs[i] => vs[i + 1] for i in 1:(length(vs) - 1)]) +end + +#Functions to get the partitionedge sitting parallel above and below a message tensor +function partitionedge_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes = planargraph_partitionpair_partitionedges( + bmpsc, planargraph_partitionpair(bmpsc, pe) + ) + pe_pos = only(findall(x -> x == pe, pes)) + pe_pos == length(pes) && return nothing + return pes[pe_pos + 1] +end + +function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes = planargraph_partitionpair_partitionedges( + bmpsc, planargraph_partitionpair(bmpsc, pe) + ) + pe_pos = only(findall(x -> x == pe, pes)) + pe_pos == 1 && return nothing + return pes[pe_pos - 1] end #Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 -function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) - ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), planargraph_partitionpair(bmpsc, pe2) +function mps_gauge_update_sequence( + bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge +) + ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), + planargraph_partitionpair(bmpsc, pe2) @assert ppgpe1 == ppgpe2 pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe1) return pair_sequence(pes, pe1, pe2) end -#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge onto pe1 +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge onto pe function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ppgpe = planargraph_partitionpair(bmpsc, pe) - pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe) - return vcat(mps_gauge_update_sequence(bmpsc, last(pes), pe), mps_gauge_update_sequence(bmpsc, first(pes), pe)) + pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe) + return vcat( + mps_gauge_update_sequence(bmpsc, last(pes), pe), + mps_gauge_update_sequence(bmpsc, first(pes), pe), + ) end #Get all partitionedges between the pair of neighboring partitions, sorted top to bottom #TODO: Bring in line with NamedGraphs change -function planargraph_partitionpair_partitionedges(bmpsc::BoundaryMPSCache, partitionpair::Pair) +function planargraph_partitionpair_partitionedges( + bmpsc::BoundaryMPSCache, partitionpair::Pair +) pg = planargraph(bmpsc) - src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), planargraph_vertices(bmpsc, last(partitionpair)) - es = filter(x -> !isempty(last(x)), [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs]) + src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), + planargraph_vertices(bmpsc, last(partitionpair)) + es = filter( + x -> !isempty(last(x)), + [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs], + ) es = map(x -> first(x) => only(last(x)), es) - return sort(PartitionEdge.(NamedEdge.(es)); by = x -> src(parent(x))) + return sort(PartitionEdge.(NamedEdge.(es)); by=x -> src(parent(x))) end function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) @@ -89,7 +135,9 @@ function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITens end #Initialise all the message tensors for the pair of neighboring partitions, with virtual rank given by message rank -function set_interpartition_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair, message_rank::Int64) +function set_interpartition_messages( + bmpsc::BoundaryMPSCache, partitionpair::Pair, message_rank::Int64 +) bmpsc = copy(bmpsc) ms = messages(bmpsc) pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) @@ -97,14 +145,11 @@ function set_interpartition_messages(bmpsc::BoundaryMPSCache, partitionpair::Pai for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing - me = delta(siteinds) - if !isnothing(prev_virtual_ind) - me *= ITensor(1.0, prev_virtual_ind) + me = denseblocks(delta(siteinds)) + virt_inds = filter(x -> !isnothing(x), [prev_virtual_ind, next_virtual_index]) + if !isempty(virt_inds) + me *= delta(virt_inds) end - if !isnothing(next_virtual_index) - me *= ITensor(1.0, next_virtual_index) - end - set!(ms, pg_pe, ITensor[me]) prev_virtual_ind = next_virtual_index end @@ -113,24 +158,31 @@ function set_interpartition_messages(bmpsc::BoundaryMPSCache, partitionpair::Pai end #Initialise all the interpartition message tensors with virtual rank given by message rank -function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64 = 1) +function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64=1) bmpsc = copy(bmpsc) pes = partitionedges(ppg(bmpsc)) for pe in vcat(pes, reverse(reverse.(pes))) - bmpsc = set_interpartition_messages(bmpsc, parent(src(pe)) => parent(dst(pe)), message_rank) + bmpsc = set_interpartition_messages( + bmpsc, parent(src(pe)) => parent(dst(pe)), message_rank + ) end return bmpsc end -#Switch the message tensors from column/row i -> i + 1 with those from i + 1 -> i -function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) +#Switch the message on partition edge pe with its reverse (and dagger them) +function switch_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) - for pe_i in pes - me, mer = message(bmpsc, pe_i), message(bmpsc, reverse(pe_i)) - set!(ms, pe_i, dag.(mer)) - set!(ms, reverse(pe_i), dag.(me)) + me, mer = message(bmpsc, pe), message(bmpsc, reverse(pe)) + set!(ms, pe, dag.(mer)) + set!(ms, reverse(pe), dag.(me)) + return bmpsc +end + +#Switch the message tensors from partitionpair i -> i + 1 with those from i + 1 -> i +function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) + for pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + bmpsc = switch_message(bmpsc, pe) end return bmpsc end @@ -148,7 +200,7 @@ function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) return update(bmpsc, PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); kwargs...) end -#Update all messages within a partition towards v +#Update all message tensors within a partition pointing towards v function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) pv = planargraph_partition(bmpsc, v) g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) @@ -156,11 +208,17 @@ function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) end #Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 -function gauge_step(alg::Algorithm"orthogonalize", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; kwargs...) +function gauge_step( + alg::Algorithm"orthogonalize", + bmpsc::BoundaryMPSCache, + pe1::PartitionEdge, + pe2::PartitionEdge; + kwargs..., +) bmpsc = copy(bmpsc) ms = messages(bmpsc) m1, m2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) - @assert !isempty(commoninds(m1,m2)) + @assert !isempty(commoninds(m1, m2)) left_inds = uniqueinds(m1, m2) m1, Y = factorize(m1, left_inds; ortho="left", kwargs...) m2 = m2 * Y @@ -169,8 +227,14 @@ function gauge_step(alg::Algorithm"orthogonalize", bmpsc::BoundaryMPSCache, pe1: return bmpsc end -#Move the biorthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 -function gauge_step(alg::Algorithm"biorthogonalize", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; regularization = 1e-12) +#Move the biorthogonality centre one step on an interpartition from the partition edge pe1 (and its reverse) to that on pe2 +function gauge_step( + alg::Algorithm"biorthogonalize", + bmpsc::BoundaryMPSCache, + pe1::PartitionEdge, + pe2::PartitionEdge; + regularization=1e-12, +) bmpsc = copy(bmpsc) ms = messages(bmpsc) @@ -178,18 +242,29 @@ function gauge_step(alg::Algorithm"biorthogonalize", bmpsc::BoundaryMPSCache, pe m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) - top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), setdiff(inds(m1r), [m1_siteinds; bottom_cind]) + top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), + setdiff(inds(m1r), [m1_siteinds; bottom_cind]) - E = isempty(top_ncind) ? m1 * m1r : m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) - U, S, V = svd(E, bottom_cind; alg = "recursive") + E = if isempty(top_ncind) + m1 * m1r + else + m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) + end + U, S, V = svd(E, bottom_cind; alg="recursive") S_sqrtinv = map_diag(x -> pinv(sqrt(x)), S) S_sqrt = map_diag(x -> sqrt(x), S) set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) - set!(ms, reverse(pe1), ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)]) + set!( + ms, + reverse(pe1), + ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)], + ) set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) - set!(ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)]) + set!( + ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)] + ) return bmpsc end @@ -204,16 +279,30 @@ end #Move the orthogonality centre on an interpartition to the message tensor on pe or between two pes function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) - return gauge_walk(Algorithm("orthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs...) + return gauge_walk( + Algorithm("orthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs... + ) end #Move the biorthogonality centre on an interpartition to the message tensor or between two pes function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) - return gauge_walk(Algorithm("biorthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs...) + return gauge_walk( + Algorithm("biorthogonalize"), + bmpsc, + mps_gauge_update_sequence(bmpsc, args...); + kwargs..., + ) end -#Update all the message tensors on an interpartition via an orthogonal fitting procedure -function mps_update(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; niters::Int64 = 25, tolerance = 1e-10, normalize = true) +#Update all the message tensors on an interpartition via an orthogonal fitting procedure +function mps_update( + alg::Algorithm"orthogonal", + bmpsc::BoundaryMPSCache, + partitionpair::Pair; + niters::Int64=25, + tolerance=1e-10, + normalize=true, +) bmpsc = switch_messages(bmpsc, partitionpair) pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) update_seq = vcat(pes, reverse(pes)[2:length(pes)]) @@ -223,9 +312,26 @@ function mps_update(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partiti costfunction = 0 for update_pe in update_seq cur_v = parent(src(update_pe)) - bmpsc = !isnothing(prev_pe) ? orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) : orthogonalize(bmpsc, reverse(update_pe)) - bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) : partition_update(bmpsc, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) - me = update_message(bmpsc, update_pe; message_update = ms -> default_message_update(ms; normalize)) + bmpsc = if !isnothing(prev_pe) + orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) + else + orthogonalize(bmpsc, reverse(update_pe)) + end + bmpsc = if !isnothing(prev_v) + partition_update( + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) + else + partition_update( + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) + end + me = update_message( + bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) + ) costfunction += region_scalar(bp_cache(bmpsc), src(update_pe)) / norm(me) bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) prev_v, prev_pe = cur_v, update_pe @@ -238,28 +344,61 @@ function mps_update(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partiti end end return switch_messages(bmpsc, partitionpair) -end +end #Update all the message tensors on an interpartition via a biorthogonal fitting procedure -function mps_update(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize = true) +function mps_update( + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize=true +) prev_v, prev_pe = nothing, nothing for update_pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) cur_v = parent(src(update_pe)) - bmpsc = !isnothing(prev_pe) ? biorthogonalize(bmpsc, prev_pe, update_pe) : biorthogonalize(bmpsc, update_pe) - bmpsc = !isnothing(prev_v) ? partition_update(bmpsc, prev_v, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) : partition_update(bmpsc, cur_v; message_update = ms -> default_message_update(ms; normalize = false)) + bmpsc = if !isnothing(prev_pe) + biorthogonalize(bmpsc, prev_pe, update_pe) + else + biorthogonalize(bmpsc, update_pe) + end + bmpsc = if !isnothing(prev_v) + partition_update( + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) + else + partition_update( + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) + end + me_prev = only(message(bmpsc, update_pe)) - me = only(update_message(bmpsc, update_pe; message_update = ms -> default_message_update(ms; normalize))) - is, is_to_replace = uniqueinds(me, me_prev), uniqueinds(me_prev, me) - for i in is - i_replacement = only(filter(ip -> tags(ip) == tags(i), is_to_replace)) - me = replaceind(me, i, i_replacement) + me = only( + update_message( + bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) + ), + ) + p_above, p_below = partitionedge_above(bmpsc, update_pe), + partitionedge_below(bmpsc, update_pe) + if !isnothing(p_above) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_above)))), + commonind(me_prev, only(message(bmpsc, p_above))), + ) + end + if !isnothing(p_below) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_below)))), + commonind(me_prev, only(message(bmpsc, p_below))), + ) end bmpsc = set_message(bmpsc, update_pe, ITensor[me]) prev_v, prev_pe = cur_v, update_pe end return bmpsc -end +end """ More generic interface for update, with default params @@ -269,7 +408,7 @@ function update( bmpsc::BoundaryMPSCache; partitionpairs=default_edge_sequence(bmpsc), maxiter=default_bp_maxiter(alg, bmpsc), - mps_fit_kwargs = default_mps_fit_kwargs(alg, bmpsc) + mps_fit_kwargs=default_mps_fit_kwargs(alg, bmpsc), ) if isnothing(maxiter) error("You need to specify a number of iterations for Boundary MPS!") @@ -282,7 +421,7 @@ function update( return bmpsc end -function update(bmpsc::BoundaryMPSCache; alg::String = "orthogonal", kwargs...) +function update(bmpsc::BoundaryMPSCache; alg::String="orthogonal", kwargs...) return update(Algorithm(alg), bmpsc; kwargs...) end @@ -300,26 +439,23 @@ end #Forward onto beliefpropagationcache for f in [ - :messages, - :message, - :update_message, - :(ITensorNetworks.linkinds), - :default_edge_sequence, - :factor, - :factors, - ] - @eval begin - function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) - return $f(bp_cache(bmpsc), args...; kwargs...) - end + :messages, + :message, + :update_message, + :(ITensorNetworks.linkinds), + :default_edge_sequence, + :factor, + :factors, +] + @eval begin + function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) + return $f(bp_cache(bmpsc), args...; kwargs...) end + end end #Wrap around beliefpropagationcache -for f in [ - :update_factors, - :update_factor, -] +for f in [:update_factors, :update_factor] @eval begin function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) bmpsc = copy(bmpsc) @@ -337,5 +473,3 @@ function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs... bpc = update(bpc, pes; kwargs...) return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) end - -#TODO: Function to get the virtual indices coming from above and below an interparititon message tensor \ No newline at end of file diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index 63a2feb2..fa3840e9 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -3,39 +3,42 @@ using NamedGraphs.PartitionedGraphs: PartitionedGraph #Add partition edges that may not have meaning in the underlying graph function add_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) - g = partitioned_graph(pg) - g = add_edges(g, parent.(pes)) - return PartitionedGraph(unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg)) + g = partitioned_graph(pg) + g = add_edges(g, parent.(pes)) + return PartitionedGraph( + unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg) + ) end - + #Add partition edges that may not have meaning in the underlying graph function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) - pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) - return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) + pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) + return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) end - + #Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function -function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f = v -> first(v)) - pg = partitioned_graph(bpc) - partitions = unique(sort_f.(collect(vertices(pg)))) - pseudo_edges = PartitionEdge[] - for p in partitions - vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) - for i in 1:(length(vs) - 1) - if vs[i] ∉ neighbors(pg, vs[i+1]) - push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i+1]))) - end - end +function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f=v -> first(v)) + pg = partitioned_graph(bpc) + partitions = unique(sort_f.(collect(vertices(pg)))) + pseudo_edges = PartitionEdge[] + for p in partitions + vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) + for i in 1:(length(vs) - 1) + if vs[i] ∉ neighbors(pg, vs[i + 1]) + push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i + 1]))) + end end - return add_partitionedges(bpc, pseudo_edges) + end + return add_partitionedges(bpc, pseudo_edges) end - pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) #Return a sequence of pairs to go from item1 to item2 in an ordered_list function pair_sequence(ordered_list::Vector, item1, item2) - item1_pos, item2_pos = only(findall(x->x==item1, ordered_list)), only(findall(x->x==item2, ordered_list)) - item1_pos < item2_pos && return [ordered_list[i] => ordered_list[i+1] for i in item1_pos:(item2_pos-1)] - return [ordered_list[i] => ordered_list[i-1] for i in item1_pos:-1:(item2_pos+1)] -end \ No newline at end of file + item1_pos, item2_pos = only(findall(x -> x == item1, ordered_list)), + only(findall(x -> x == item2, ordered_list)) + item1_pos < item2_pos && + return [ordered_list[i] => ordered_list[i + 1] for i in item1_pos:(item2_pos - 1)] + return [ordered_list[i] => ordered_list[i - 1] for i in item1_pos:-1:(item2_pos + 1)] +end diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 1604bde4..4ea4f607 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -31,14 +31,16 @@ using ITensorNetworks: update_factor, update_message, message_diff -using ITensors: ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor +using ITensors: + ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor using ITensorNetworks.ModelNetworks: ModelNetworks using ITensorNetworks.ITensorsExtensions: map_eigvals using ITensors.NDTensors: array using LinearAlgebra: eigvals, tr using NamedGraphs: NamedEdge, NamedGraph, subgraph using NamedGraphs.GraphsExtensions: rem_vertices -using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid, named_hexagonal_lattice_graph +using NamedGraphs.NamedGraphGenerators: + named_comb_tree, named_grid, named_hexagonal_lattice_graph using NamedGraphs.PartitionedGraphs: PartitionEdge, PartitionVertex, partitionedges using SplitApplyCombine: group using StableRNGs: StableRNG @@ -51,93 +53,97 @@ using LinearAlgebra: norm ) begin ITensors.disable_warn_order() - mps_fit_kwargs = (; niters = 50, tolerance = 1e-10) + mps_fit_kwargs = (; niters=50, tolerance=1e-10) rng = StableRNG(1234) - #First a comb tree (which is still a planar graph) - g = named_comb_tree((4,4)) - s = siteinds("S=1/2", g) + #First a comb tree (which is still a planar graph) and a flat tensor network + g = named_comb_tree((4, 4)) χ = 2 - ψ = random_tensornetwork(rng, elt, s; link_space=χ) - ψIψ = QuadraticFormNetwork(ψ) - vc = (first(center(g)), "operator") + tn = random_tensornetwork(rng, elt, g; link_space=χ) + vc = first(center(g)) - ρ_bp = contract(environment(ψIψ, [vc]; alg = "bp"); sequence = "automatic") - ρ_bp /= tr(ρ_bp) + ∂tn_∂vc_bp = contract(environment(tn, [vc]; alg="bp"); sequence="automatic") + ∂tn_∂vc_bp /= norm(∂tn_∂vc_bp) - ρ = subgraph(ψIψ, setdiff(vertices(ψIψ), [vc])) - ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) - ρ_exact /= tr(ρ_exact) + ∂tn_∂vc = subgraph(tn, setdiff(vertices(tn), [vc])) + ∂tn_∂vc_exact = contract(∂tn_∂vc; sequence=contraction_sequence(∂tn_∂vc; alg="greedy")) + ∂tn_∂vc_exact /= norm(∂tn_∂vc_exact) #Orthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank =1) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") - ρ_boundaryMPS /= tr(ρ_boundaryMPS) + tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + tn_boundaryMPS = update(tn_boundaryMPS; mps_fit_kwargs) + ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") + ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) - @test norm(ρ_boundaryMPS - ρ_exact) <= eps(real(elt)) - @test norm(ρ_boundaryMPS - ρ_bp) <= eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) #Biorthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank =1) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal") - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") - ρ_boundaryMPS /= tr(ρ_boundaryMPS) + tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + tn_boundaryMPS = update(tn_boundaryMPS; alg="biorthogonal") + ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") + ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) - @test norm(ρ_boundaryMPS - ρ_exact) <= eps(real(elt)) - @test norm(ρ_boundaryMPS - ρ_bp) <= eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) - #Now a square graph with a few vertices missing for added complexity + #Now the norm tensor network of a square graph with a few vertices missing for added complexity g = named_grid((5, 5)) - g = rem_vertices(g, [(2,2), (3,3)]) + g = rem_vertices(g, [(2, 2), (3, 3)]) s = siteinds("S=1/2", g) χ = 3 ψ = random_tensornetwork(rng, elt, s; link_space=χ) ψIψ = QuadraticFormNetwork(ψ) vc = (first(center(g)), "operator") ρ = subgraph(ψIψ, setdiff(vertices(ψIψ), [vc])) - ρ_exact = contract(ρ; sequence = contraction_sequence(ρ; alg ="greedy")) + ρ_exact = contract(ρ; sequence=contraction_sequence(ρ; alg="greedy")) ρ_exact /= tr(ρ_exact) #Orthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") ρ_boundaryMPS /= tr(ρ_boundaryMPS) - @test norm(ρ_boundaryMPS - ρ_exact) <= 10*eps(real(elt)) + @test norm(ρ_boundaryMPS - ρ_exact) <= 10 * eps(real(elt)) #BiOrthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank = χ*χ) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal", maxiter = 100) - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence = "automatic") + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal", maxiter=50) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") ρ_boundaryMPS /= tr(ρ_boundaryMPS) - @test norm(ρ_boundaryMPS - ρ_exact) <= 100000*eps(real(elt)) + @test norm(ρ_boundaryMPS - ρ_exact) <= 2e3 * eps(real(elt)) - #Now test BP and Boundary MPS are equivalent in the symmetric gauge - g = named_hexagonal_lattice_graph(3,3) + #Now we test BP and orthogonal and biorthogonl Boundary MPS are equivalent when run from in the symmetric gauge + g = named_hexagonal_lattice_graph(3, 3) s = siteinds("S=1/2", g) χ = 2 ψ = random_tensornetwork(rng, elt, s; link_space=χ) #Move wavefunction to symmetric gauge, enforce posdefness for complex number stability function make_posdef(A::ITensor) - return map_eigvals(x -> abs(real(x)), A, first(inds(A)), last(inds(A)); ishermitian=true) + return map_eigvals( + x -> abs(real(x)), A, first(inds(A)), last(inds(A)); ishermitian=true + ) end message_update_f = ms -> make_posdef.(default_message_update(ms)) - ψ_vidal = VidalITensorNetwork(ψ; cache_update_kwargs=(; message_update = message_update_f, maxiter=100, tol = 1e-16)) + ψ_vidal = VidalITensorNetwork( + ψ; cache_update_kwargs=(; message_update=message_update_f, maxiter=100, tol=1e-16) + ) cache_ref = Ref{BeliefPropagationCache}() - ψ_symm = ITensorNetwork(ψ_vidal; (cache!) = cache_ref) + ψ_symm = ITensorNetwork(ψ_vidal; (cache!)=cache_ref) bp_cache = cache_ref[] #Do Orthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank = 1) + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank=1) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) for pe in keys(messages(bp_cache)) m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) - m_boundarymps = m_boundarymps * ITensor(1.0, filter(i -> dim(i) ==1, inds(m_boundarymps))) + #Prune the dimension 1 virtual index from boundary MPS message tensor + m_boundarymps = + m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) m_bp = only(message(bp_cache, pe)) m_bp /= tr(m_bp) m_boundarymps /= tr(m_boundarymps) @@ -145,19 +151,19 @@ using LinearAlgebra: norm end #Do Biorthogonal Boundary MPS - ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank = 1) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg = "biorthogonal") + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank=1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal") for pe in keys(messages(bp_cache)) m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) - #Prune the dimension 1 virtual index from boundary MPS - m_boundarymps = m_boundarymps * ITensor(1.0, filter(i -> dim(i) ==1, inds(m_boundarymps))) + #Prune the dimension 1 virtual index from boundary MPS message tensor + m_boundarymps = + m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) m_bp = only(message(bp_cache, pe)) m_bp /= tr(m_bp) m_boundarymps /= tr(m_boundarymps) @test norm(m_bp - m_boundarymps) <= 1e-4 end - end end end From 338a41f5699353ec22308d06e682f6a31b92d488 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 11:10:34 +0000 Subject: [PATCH 25/47] BoundaryMPS --- Project.toml | 14 +- README.md | 144 +++--- examples/test.jl | 21 + src/ITensorNetworks.jl | 2 + src/abstractitensornetwork.jl | 56 ++- src/apply.jl | 4 +- src/caches/beliefpropagationcache.jl | 38 +- src/caches/boundarympscache.jl | 475 ++++++++++++++++++ src/caches/boundarympscacheutils.jl | 44 ++ src/inner.jl | 15 +- .../alternating_update/alternating_update.jl | 6 +- .../alternating_update/region_update.jl | 49 +- src/solvers/contract.jl | 3 +- src/solvers/defaults.jl | 18 +- src/solvers/extract/extract.jl | 16 +- src/solvers/insert/insert.jl | 25 +- src/solvers/sweep_plans/sweep_plans.jl | 30 +- src/tebd.jl | 2 +- .../abstracttreetensornetwork.jl | 53 +- test/Project.toml | 2 +- test/test_belief_propagation.jl | 87 ++-- test/test_boundarymps.jl | 169 +++++++ test/test_gauging.jl | 6 +- test/test_itensornetwork.jl | 5 +- .../solvers.jl | 4 +- .../test_solvers/Project.toml | 3 +- .../test_solvers/test_dmrg.jl | 40 +- .../test_solvers/test_tdvp_time_dependent.jl | 4 +- 28 files changed, 1035 insertions(+), 300 deletions(-) create mode 100644 examples/test.jl create mode 100644 src/caches/boundarympscache.jl create mode 100644 src/caches/boundarympscacheutils.jl create mode 100644 test/test_boundarymps.jl diff --git a/Project.toml b/Project.toml index 877465cc..e1dc76cb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ITensorNetworks" uuid = "2919e153-833c-4bdc-8836-1ea460a35fc7" authors = ["Matthew Fishman , Joseph Tindall and contributors"] -version = "0.11.13" +version = "0.11.24" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -62,20 +62,20 @@ DocStringExtensions = "0.9" EinExprs = "0.6.4" Graphs = "1.8" GraphsFlows = "0.1.1" -ITensorMPS = "0.2.2" -ITensors = "0.6.8" -IsApprox = "0.1" +ITensorMPS = "0.3" +ITensors = "0.7" +IsApprox = "0.1, 1, 2" IterTools = "1.4.0" -KrylovKit = "0.6, 0.7" +KrylovKit = "0.6, 0.7, 0.8" MacroTools = "0.5" NDTensors = "0.3" NamedGraphs = "0.6.0" -OMEinsumContractionOrders = "0.8.3" +OMEinsumContractionOrders = "0.8.3, 0.9" Observers = "0.2.4" PackageExtensionCompat = "1" SerializedElementArrays = "0.1" SimpleTraits = "0.9" -SparseArrayKit = "0.3" +SparseArrayKit = "0.3, 0.4" SplitApplyCombine = "1.2" StaticArrays = "1.5.12" StructWalk = "0.2" diff --git a/README.md b/README.md index 6ce69026..3e0dbb7c 100644 --- a/README.md +++ b/README.md @@ -51,17 +51,17 @@ and 3 edge(s): with vertex data: 4-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=739|"1,2"),) - 2 │ ((dim=2|id=739|"1,2"), (dim=2|id=920|"2,3")) - 3 │ ((dim=2|id=920|"2,3"), (dim=2|id=761|"3,4")) - 4 │ ((dim=2|id=761|"3,4"),) + 1 │ ((dim=2|id=664|"1,2"),) + 2 │ ((dim=2|id=664|"1,2"), (dim=2|id=561|"2,3")) + 3 │ ((dim=2|id=561|"2,3"), (dim=2|id=47|"3,4")) + 4 │ ((dim=2|id=47|"3,4"),) julia> tn[1] -ITensor ord=1 (dim=2|id=739|"1,2") +ITensor ord=1 (dim=2|id=664|"1,2") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> tn[2] -ITensor ord=2 (dim=2|id=739|"1,2") (dim=2|id=920|"2,3") +ITensor ord=2 (dim=2|id=664|"1,2") (dim=2|id=561|"2,3") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> neighbors(tn, 1) @@ -107,13 +107,13 @@ and 4 edge(s): with vertex data: 4-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (1, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=723|"1×1,1×2")) - (2, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=823|"2×1,2×2")) - (1, 2) │ ((dim=2|id=723|"1×1,1×2"), (dim=2|id=712|"1×2,2×2")) - (2, 2) │ ((dim=2|id=823|"2×1,2×2"), (dim=2|id=712|"1×2,2×2")) + (1, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=516|"1×1,1×2")) + (2, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=538|"2×1,2×2")) + (1, 2) │ ((dim=2|id=516|"1×1,1×2"), (dim=2|id=278|"1×2,2×2")) + (2, 2) │ ((dim=2|id=538|"2×1,2×2"), (dim=2|id=278|"1×2,2×2")) julia> tn[1, 1] -ITensor ord=2 (dim=2|id=74|"1×1,2×1") (dim=2|id=723|"1×1,1×2") +ITensor ord=2 (dim=2|id=68|"1×1,2×1") (dim=2|id=516|"1×1,1×2") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> neighbors(tn, (1, 1)) @@ -137,8 +137,8 @@ and 1 edge(s): with vertex data: 2-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (1, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=723|"1×1,1×2")) - (1, 2) │ ((dim=2|id=723|"1×1,1×2"), (dim=2|id=712|"1×2,2×2")) + (1, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=516|"1×1,1×2")) + (1, 2) │ ((dim=2|id=516|"1×1,1×2"), (dim=2|id=278|"1×2,2×2")) julia> tn_2 = subgraph(v -> v[1] == 2, tn) ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 2 vertices: @@ -151,8 +151,8 @@ and 1 edge(s): with vertex data: 2-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (2, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=823|"2×1,2×2")) - (2, 2) │ ((dim=2|id=823|"2×1,2×2"), (dim=2|id=712|"1×2,2×2")) + (2, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=538|"2×1,2×2")) + (2, 2) │ ((dim=2|id=538|"2×1,2×2"), (dim=2|id=278|"1×2,2×2")) ``` @@ -178,9 +178,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Vector{ITensors.Index}} - 1 │ ITensors.Index[(dim=2|id=683|"S=1/2,Site,n=1")] - 2 │ ITensors.Index[(dim=2|id=123|"S=1/2,Site,n=2")] - 3 │ ITensors.Index[(dim=2|id=656|"S=1/2,Site,n=3")] + 1 │ ITensors.Index[(dim=2|id=549|"S=1/2,Site,n=1")] + 2 │ ITensors.Index[(dim=2|id=718|"S=1/2,Site,n=2")] + 3 │ ITensors.Index[(dim=2|id=254|"S=1/2,Site,n=3")] and edge data: 0-element Dictionaries.Dictionary{NamedGraphs.NamedEdge{Int64}, Vector{ITensors.Index}} @@ -198,9 +198,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=683|"S=1/2,Site,n=1"), (dim=2|id=382|"1,2")) - 2 │ ((dim=2|id=123|"S=1/2,Site,n=2"), (dim=2|id=382|"1,2"), (dim=2|id=190|"2,3… - 3 │ ((dim=2|id=656|"S=1/2,Site,n=3"), (dim=2|id=190|"2,3")) + 1 │ ((dim=2|id=549|"S=1/2,Site,n=1"), (dim=2|id=149|"1,2")) + 2 │ ((dim=2|id=718|"S=1/2,Site,n=2"), (dim=2|id=149|"1,2"), (dim=2|id=113|"2,3… + 3 │ ((dim=2|id=254|"S=1/2,Site,n=3"), (dim=2|id=113|"2,3")) julia> tn2 = ITensorNetwork(s; link_space=2) ITensorNetworks.ITensorNetwork{Int64} with 3 vertices: @@ -215,9 +215,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=683|"S=1/2,Site,n=1"), (dim=2|id=934|"1,2")) - 2 │ ((dim=2|id=123|"S=1/2,Site,n=2"), (dim=2|id=934|"1,2"), (dim=2|id=614|"2,3… - 3 │ ((dim=2|id=656|"S=1/2,Site,n=3"), (dim=2|id=614|"2,3")) + 1 │ ((dim=2|id=549|"S=1/2,Site,n=1"), (dim=2|id=407|"1,2")) + 2 │ ((dim=2|id=718|"S=1/2,Site,n=2"), (dim=2|id=407|"1,2"), (dim=2|id=205|"2,3… + 3 │ ((dim=2|id=254|"S=1/2,Site,n=3"), (dim=2|id=205|"2,3")) julia> @visualize tn1; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -228,19 +228,19 @@ julia> @visualize tn1; ⠀⠀⠀⠀⠀⡇⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠈⠑⠢2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn12⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn13⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ julia> @visualize tn2; @@ -252,19 +252,19 @@ julia> @visualize tn2; ⠀⠀⠀⠀⠀⡇⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠈⠑⠢2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn22⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn23⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ julia> Z = prime(tn1; sites=[]) ⊗ tn2; @@ -274,20 +274,20 @@ julia> @visualize Z; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀Z(1, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠔⠁⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⡠2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⠀⠀⢀⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 2)⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠁⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⢄⡀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀Z(3, 1)⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 2)⠤⠔⠒⠒⠒⠒2⠉⠉⠉⠉⠁⠀⣀⠔⠉⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀2⠀⠀⠀Z(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡠⢼⠔⠒⠊2⠥⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡠⠤(2)'⠁⠀⠀⡜⢀⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀Z(1, 1)⠒⠒⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀2⢆⠀⠀⠀⠀⠀⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀Z(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -305,20 +305,20 @@ julia> @visualize Z̃; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠤⠤⠔⠒⠒⠉⠉⠀⠀⢱⠀⠈⠉⠑⠒⠢⠤⢄⣀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⣀⣀⠤⠤⠔⠒⠊⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠒⠒⠤⠤⢄⣀⡀⠀⠀⠀⠀⠀ - ⠀Z̃(3, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(1, 2)⠀⠀ - ⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⡠2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡠⠤⠒⠊⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠤2⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⣀⡠⠤⠒⠊⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀Z̃(3, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(3, 2)⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠑⠒⠒⠒⠒⠢2⠤⠤⠤⠤⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉Z̃(3, 1)⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⡔2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀Z̃(2, 2)⠤⠤⠤⠤⢄⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉2⠉⠉⠉⠒⠒⠒⠒⠒⠢⠤Z̃(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠤⠒⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀2⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⣀⡠⠤⠒⠉2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⣀⡠⠔⠒⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀Z̃(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ diff --git a/examples/test.jl b/examples/test.jl new file mode 100644 index 00000000..6bf1bfcf --- /dev/null +++ b/examples/test.jl @@ -0,0 +1,21 @@ +using ITensorNetworks: IndsNetwork, siteinds, ttn +using ITensorNetworks.ModelHamiltonians: ising +using ITensors: Index, OpSum, terms, sites +using NamedGraphs.NamedGraphGenerators: named_grid +using NamedGraphs.GraphsExtensions: rem_vertex + +function filter_terms(H, verts) + H_new = OpSum() + for term in terms(H) + if isempty(filter(v -> v ∈ verts, sites(term))) + H_new += term + end + end + return H_new +end + +g = named_grid((8,1)) +s = siteinds("S=1/2", g) +H = ising(s) +H_mod = filter_terms(H, [(4,1)]) +ttno = ttn(H_mod, s) \ No newline at end of file diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 28da183a..85126258 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -29,6 +29,8 @@ include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") include("caches/beliefpropagationcache.jl") +include("caches/boundarympscacheutils.jl") +include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/abstractitensornetwork.jl b/src/abstractitensornetwork.jl index 6f6ee164..73c915a1 100644 --- a/src/abstractitensornetwork.jl +++ b/src/abstractitensornetwork.jl @@ -7,6 +7,7 @@ using Graphs: add_edge!, add_vertex!, bfs_tree, + center, dst, edges, edgetype, @@ -18,6 +19,7 @@ using Graphs: using ITensors: ITensors, ITensor, + @Algorithm_str, addtags, combiner, commoninds, @@ -40,10 +42,10 @@ using ITensorMPS: ITensorMPS, add, linkdim, linkinds, siteinds using .ITensorsExtensions: ITensorsExtensions, indtype, promote_indtype using LinearAlgebra: LinearAlgebra, factorize using MacroTools: @capture -using NamedGraphs: NamedGraphs, NamedGraph, not_implemented +using NamedGraphs: NamedGraphs, NamedGraph, not_implemented, steiner_tree using NamedGraphs.GraphsExtensions: ⊔, directed_graph, incident_edges, rename_vertices, vertextype -using NDTensors: NDTensors, dim +using NDTensors: NDTensors, dim, Algorithm using SplitApplyCombine: flatten abstract type AbstractITensorNetwork{V} <: AbstractDataGraph{V,ITensor,ITensor} end @@ -584,7 +586,9 @@ function LinearAlgebra.factorize(tn::AbstractITensorNetwork, edge::Pair; kwargs. end # For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? -function _orthogonalize_edge(tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs...) +function gauge_edge( + alg::Algorithm"orthogonalize", tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs... +) # tn = factorize(tn, edge; kwargs...) # # TODO: Implement as `only(common_neighbors(tn, src(edge), dst(edge)))` # new_vertex = only(neighbors(tn, src(edge)) ∩ neighbors(tn, dst(edge))) @@ -598,23 +602,43 @@ function _orthogonalize_edge(tn::AbstractITensorNetwork, edge::AbstractEdge; kwa return tn end -function ITensorMPS.orthogonalize(tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs...) - return _orthogonalize_edge(tn, edge; kwargs...) +# For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edges::Vector{<:AbstractEdge}; kwargs... +) + tn = copy(tn) + for edge in edges + tn = gauge_edge(alg, tn, edge; kwargs...) + end + return tn +end + +function gauge_walk(alg::Algorithm, tn::AbstractITensorNetwork, edge::Pair; kwargs...) + return gauge_edge(alg::Algorithm, tn, edgetype(tn)(edge); kwargs...) end -function ITensorMPS.orthogonalize(tn::AbstractITensorNetwork, edge::Pair; kwargs...) - return orthogonalize(tn, edgetype(tn)(edge); kwargs...) +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edges::Vector{<:Pair}; kwargs... +) + return gauge_walk(alg, tn, edgetype(tn).(edges); kwargs...) end -# Orthogonalize an ITensorNetwork towards a source vertex, treating +# Gauge a ITensorNetwork towards a region, treating # the network as a tree spanned by a spanning tree. -# TODO: Rename `tree_orthogonalize`. -function ITensorMPS.orthogonalize(ψ::AbstractITensorNetwork, source_vertex) - spanning_tree_edges = post_order_dfs_edges(bfs_tree(ψ, source_vertex), source_vertex) - for e in spanning_tree_edges - ψ = orthogonalize(ψ, e) - end - return ψ +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region::Vector) + region_center = + length(region) != 1 ? first(center(steiner_tree(ψ, region))) : only(region) + path = post_order_dfs_edges(bfs_tree(ψ, region_center), region_center) + path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) + return gauge_walk(alg, ψ, path) +end + +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region) + return tree_gauge(alg, ψ, [region]) +end + +function tree_orthogonalize(ψ::AbstractITensorNetwork, region; kwargs...) + return tree_gauge(Algorithm("orthogonalize"), ψ, region; kwargs...) end # TODO: decide whether to use graph mutating methods when resulting graph is unchanged? @@ -759,7 +783,7 @@ end # Link dimensions # -function ITensors.maxlinkdim(tn::AbstractITensorNetwork) +function ITensorMPS.maxlinkdim(tn::AbstractITensorNetwork) md = 1 for e in edges(tn) md = max(md, linkdim(tn, e)) diff --git a/src/apply.jl b/src/apply.jl index d38f04f9..6a55f45f 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -200,7 +200,7 @@ function ITensors.apply( v⃗ = neighbor_vertices(ψ, o) if length(v⃗) == 1 if ortho - ψ = orthogonalize(ψ, v⃗[1]) + ψ = tree_orthogonalize(ψ, v⃗[1]) end oψᵥ = apply(o, ψ[v⃗[1]]) if normalize @@ -215,7 +215,7 @@ function ITensors.apply( error("Vertices where the gates are being applied must be neighbors for now.") end if ortho - ψ = orthogonalize(ψ, v⃗[1]) + ψ = tree_orthogonalize(ψ, v⃗[1]) end if variational_optimization_only || !is_product_env ψᵥ₁, ψᵥ₂ = full_update_bp( diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index b980a52f..73f3c521 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -1,6 +1,6 @@ using Graphs: IsDirected using SplitApplyCombine: group -using LinearAlgebra: diag +using LinearAlgebra: diag, dot using ITensors: dir using ITensorMPS: ITensorMPS using NamedGraphs.PartitionedGraphs: @@ -12,15 +12,15 @@ using NamedGraphs.PartitionedGraphs: partitionedges, unpartitioned_graph using SimpleTraits: SimpleTraits, Not, @traitfn +using NDTensors: NDTensors -default_message(inds_e) = ITensor[denseblocks(delta(i)) for i in inds_e] +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, inds_e))] default_messages(ptn::PartitionedGraph) = Dictionary() -default_message_norm(m::ITensor) = norm(m) -function default_message_update(contract_list::Vector{ITensor}; kwargs...) +function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) sequence = optimal_contraction_sequence(contract_list) updated_messages = contract(contract_list; sequence, kwargs...) message_norm = norm(updated_messages) - if !iszero(message_norm) + if normalize && !iszero(message_norm) updated_messages /= message_norm end return ITensor[updated_messages] @@ -33,17 +33,16 @@ default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertice function default_partitioned_vertices(f::AbstractFormNetwork) return group(v -> original_state_vertex(f, v), vertices(f)) end -default_cache_update_kwargs(cache) = (; maxiter=20, tol=1e-5) +default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) function default_cache_construction_kwargs(alg::Algorithm"bp", ψ::AbstractITensorNetwork) return (; partitioned_vertices=default_partitioned_vertices(ψ)) end -function message_diff( - message_a::Vector{ITensor}, message_b::Vector{ITensor}; message_norm=default_message_norm -) +#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages +function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) lhs, rhs = contract(message_a), contract(message_b) - norm_lhs, norm_rhs = message_norm(lhs), message_norm(rhs) - return 0.5 * norm((denseblocks(lhs) / norm_lhs) - (denseblocks(rhs) / norm_rhs)) + f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) + return 1 - f end struct BeliefPropagationCache{PTN,MTS,DM} @@ -99,13 +98,15 @@ for f in [ end end +NDTensors.scalartype(bp_cache) = scalartype(tensornetwork(bp_cache)) + function default_message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - return default_message(bp_cache)(linkinds(bp_cache, edge)) + return default_message(bp_cache)(scalartype(bp_cache), linkinds(bp_cache, edge)) end function message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) mts = messages(bp_cache) - return get(mts, edge, default_message(bp_cache, edge)) + return get(() -> default_message(bp_cache, edge), mts, edge) end function messages(bp_cache::BeliefPropagationCache, edges; kwargs...) return map(edge -> message(bp_cache, edge; kwargs...), edges) @@ -151,15 +152,16 @@ end function environment(bp_cache::BeliefPropagationCache, verts::Vector) partition_verts = partitionvertices(bp_cache, verts) messages = environment(bp_cache, partition_verts) - central_tensors = ITensor[ - tensornetwork(bp_cache)[v] for v in setdiff(vertices(bp_cache, partition_verts), verts) - ] + central_tensors = factors(bp_cache, setdiff(vertices(bp_cache, partition_verts), verts)) return vcat(messages, central_tensors) end +function factors(bp_cache::BeliefPropagationCache, verts::Vector) + return ITensor[tensornetwork(bp_cache)[v] for v in verts] +end + function factor(bp_cache::BeliefPropagationCache, vertex::PartitionVertex) - ptn = partitioned_tensornetwork(bp_cache) - return collect(eachtensor(subgraph(ptn, vertex))) + return factors(bp_cache, vertices(bp_cache, vertex)) end """ diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl new file mode 100644 index 00000000..c5c57b4b --- /dev/null +++ b/src/caches/boundarympscache.jl @@ -0,0 +1,475 @@ +using NamedGraphs: NamedGraphs +using NamedGraphs.GraphsExtensions: add_edges +using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, region_scalar +using ITensorNetworks.ITensorsExtensions: map_diag +using ITensorMPS: ITensorMPS, orthogonalize +using NamedGraphs.PartitionedGraphs: + partitioned_graph, PartitionVertex, partitionvertex, partitioned_vertices, which_partition +using SplitApplyCombine: group +using ITensors: commoninds, random_itensor +using LinearAlgebra: pinv + +struct BoundaryMPSCache{BPC,PG} + bp_cache::BPC + partitionedplanargraph::PG +end + +bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache +partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph +ppg(bmpsc) = partitionedplanargraph(bmpsc) +planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) +tensornetwork(bmpsc::BoundaryMPSCache) = tensornetwork(bp_cache(bmpsc)) +function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) + return partitioned_tensornetwork(bp_cache(bmpsc)) +end + +default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) +function default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) + return default_bp_maxiter(partitioned_graph(ppg(bmpsc))) +end +default_bp_maxiter(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = 50 +function default_mps_fit_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) + return (; niters=50, tolerance=1e-10) +end +default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) + +function Base.copy(bmpsc::BoundaryMPSCache) + return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) +end + +function planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) + return vertices(ppg(bmpsc), PartitionVertex(partition)) +end +function planargraph_partition(bmpsc::BoundaryMPSCache, vertex) + return parent(partitionvertex(ppg(bmpsc), vertex)) +end +function planargraph_partitions(bmpsc::BoundaryMPSCache, verts) + return parent.(partitionvertices(ppg(bmpsc), verts)) +end +function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + return pair(partitionedge(ppg(bmpsc), parent(pe))) +end + +function BoundaryMPSCache( + bpc::BeliefPropagationCache; sort_f::Function=v -> first(v), message_rank::Int64=1 +) + bpc = insert_pseudo_planar_edges(bpc; sort_f) + planar_graph = partitioned_graph(bpc) + vertex_groups = group(sort_f, collect(vertices(planar_graph))) + ppg = PartitionedGraph(planar_graph, vertex_groups) + bmpsc = BoundaryMPSCache(bpc, ppg) + return set_interpartition_messages(bmpsc, message_rank) +end + +function BoundaryMPSCache(tn::AbstractITensorNetwork, args...; kwargs...) + return BoundaryMPSCache(BeliefPropagationCache(tn, args...); kwargs...) +end + +#Get all partitionedges within a column/row, ordered top to bottom +function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) + vs = sort(planargraph_vertices(bmpsc, partition)) + return PartitionEdge.([vs[i] => vs[i + 1] for i in 1:(length(vs) - 1)]) +end + +#Functions to get the partitionedge sitting parallel above and below a message tensor +function partitionedge_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes = planargraph_partitionpair_partitionedges( + bmpsc, planargraph_partitionpair(bmpsc, pe) + ) + pe_pos = only(findall(x -> x == pe, pes)) + pe_pos == length(pes) && return nothing + return pes[pe_pos + 1] +end + +function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes = planargraph_partitionpair_partitionedges( + bmpsc, planargraph_partitionpair(bmpsc, pe) + ) + pe_pos = only(findall(x -> x == pe, pes)) + pe_pos == 1 && return nothing + return pes[pe_pos - 1] +end + +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 +function mps_gauge_update_sequence( + bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge +) + ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), + planargraph_partitionpair(bmpsc, pe2) + @assert ppgpe1 == ppgpe2 + pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe1) + return pair_sequence(pes, pe1, pe2) +end + +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge onto pe +function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + ppgpe = planargraph_partitionpair(bmpsc, pe) + pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe) + return vcat( + mps_gauge_update_sequence(bmpsc, last(pes), pe), + mps_gauge_update_sequence(bmpsc, first(pes), pe), + ) +end + +#Get all partitionedges between the pair of neighboring partitions, sorted top to bottom +#TODO: Bring in line with NamedGraphs change +function planargraph_partitionpair_partitionedges( + bmpsc::BoundaryMPSCache, partitionpair::Pair +) + pg = planargraph(bmpsc) + src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), + planargraph_vertices(bmpsc, last(partitionpair)) + es = filter( + x -> !isempty(last(x)), + [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs], + ) + es = map(x -> first(x) => only(last(x)), es) + return sort(PartitionEdge.(NamedEdge.(es)); by=x -> src(parent(x))) +end + +function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + set!(ms, pe, m) + return bmpsc +end + +#Initialise all the message tensors for the pair of neighboring partitions, with virtual rank given by message rank +function set_interpartition_messages( + bmpsc::BoundaryMPSCache, partitionpair::Pair, message_rank::Int64 +) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + prev_virtual_ind = nothing + for (i, pg_pe) in enumerate(pes) + siteinds = linkinds(bmpsc, pg_pe) + next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing + me = denseblocks(delta(siteinds)) + virt_inds = filter(x -> !isnothing(x), [prev_virtual_ind, next_virtual_index]) + if !isempty(virt_inds) + me *= delta(virt_inds) + end + set!(ms, pg_pe, ITensor[me]) + prev_virtual_ind = next_virtual_index + end + + return bmpsc +end + +#Initialise all the interpartition message tensors with virtual rank given by message rank +function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64=1) + bmpsc = copy(bmpsc) + pes = partitionedges(ppg(bmpsc)) + for pe in vcat(pes, reverse(reverse.(pes))) + bmpsc = set_interpartition_messages( + bmpsc, parent(src(pe)) => parent(dst(pe)), message_rank + ) + end + return bmpsc +end + +#Switch the message on partition edge pe with its reverse (and dagger them) +function switch_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + me, mer = message(bmpsc, pe), message(bmpsc, reverse(pe)) + set!(ms, pe, dag.(mer)) + set!(ms, reverse(pe), dag.(me)) + return bmpsc +end + +#Switch the message tensors from partitionpair i -> i + 1 with those from i + 1 -> i +function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) + for pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + bmpsc = switch_message(bmpsc, pe) + end + return bmpsc +end + +#Update all messages tensors within a partition +function partition_update(bmpsc::BoundaryMPSCache, partition::Int64; kwargs...) + vs = sort(planargraph_vertices(bmpsc, partition)) + bmpsc = partition_update(bmpsc, first(vs); kwargs...) + bmpsc = partition_update(bmpsc, last(vs); kwargs...) + return bmpsc +end + +#Update all messages within a partition along the path from from v1 to v2 +function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) + return update(bmpsc, PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); kwargs...) +end + +#Update all message tensors within a partition pointing towards v +function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) + pv = planargraph_partition(bmpsc, v) + g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) + return update(bmpsc, PartitionEdge.(post_order_dfs_edges(g, v)); kwargs...) +end + +#Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 +function gauge_step( + alg::Algorithm"orthogonalize", + bmpsc::BoundaryMPSCache, + pe1::PartitionEdge, + pe2::PartitionEdge; + kwargs..., +) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + m1, m2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) + @assert !isempty(commoninds(m1, m2)) + left_inds = uniqueinds(m1, m2) + m1, Y = factorize(m1, left_inds; ortho="left", kwargs...) + m2 = m2 * Y + set!(ms, pe1, ITensor[m1]) + set!(ms, pe2, ITensor[m2]) + return bmpsc +end + +#Move the biorthogonality centre one step on an interpartition from the partition edge pe1 (and its reverse) to that on pe2 +function gauge_step( + alg::Algorithm"biorthogonalize", + bmpsc::BoundaryMPSCache, + pe1::PartitionEdge, + pe2::PartitionEdge; + regularization=1e-12, +) + bmpsc = copy(bmpsc) + ms = messages(bmpsc) + + m1, m1r = only(message(bmpsc, pe1)), only(message(bmpsc, reverse(pe1))) + m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) + top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) + m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) + top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), + setdiff(inds(m1r), [m1_siteinds; bottom_cind]) + + E = if isempty(top_ncind) + m1 * m1r + else + m1 * replaceind(m1r, only(bottom_ncind), only(top_ncind)) + end + U, S, V = svd(E, bottom_cind; alg="recursive") + + S_sqrtinv = map_diag(x -> pinv(sqrt(x)), S) + S_sqrt = map_diag(x -> sqrt(x), S) + + set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) + set!( + ms, + reverse(pe1), + ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)], + ) + set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) + set!( + ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)] + ) + + return bmpsc +end + +#Move the orthogonality / biorthogonality centre on an interpartition via a sequence of steps between message tensors +function gauge_walk(alg::Algorithm, bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) + for (pe1, pe2) in seq + bmpsc = gauge_step(alg::Algorithm, bmpsc, pe1, pe2) + end + return bmpsc +end + +#Move the orthogonality centre on an interpartition to the message tensor on pe or between two pes +function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) + return gauge_walk( + Algorithm("orthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs... + ) +end + +#Move the biorthogonality centre on an interpartition to the message tensor or between two pes +function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) + return gauge_walk( + Algorithm("biorthogonalize"), + bmpsc, + mps_gauge_update_sequence(bmpsc, args...); + kwargs..., + ) +end + +#Update all the message tensors on an interpartition via an orthogonal fitting procedure +function mps_update( + alg::Algorithm"orthogonal", + bmpsc::BoundaryMPSCache, + partitionpair::Pair; + niters::Int64=25, + tolerance=1e-10, + normalize=true, +) + bmpsc = switch_messages(bmpsc, partitionpair) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + update_seq = vcat(pes, reverse(pes)[2:length(pes)]) + prev_v, prev_pe = nothing, nothing + prev_costfunction = 0 + for i in 1:niters + costfunction = 0 + for update_pe in update_seq + cur_v = parent(src(update_pe)) + bmpsc = if !isnothing(prev_pe) + orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) + else + orthogonalize(bmpsc, reverse(update_pe)) + end + bmpsc = if !isnothing(prev_v) + partition_update( + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) + else + partition_update( + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) + end + me = update_message( + bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) + ) + costfunction += region_scalar(bp_cache(bmpsc), src(update_pe)) / norm(me) + bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) + prev_v, prev_pe = cur_v, update_pe + end + epsilon = abs(costfunction - prev_costfunction) / length(update_seq) + if !isnothing(tolerance) && epsilon < tolerance + return switch_messages(bmpsc, partitionpair) + else + prev_costfunction = costfunction + end + end + return switch_messages(bmpsc, partitionpair) +end + +#Update all the message tensors on an interpartition via a biorthogonal fitting procedure +function mps_update( + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize=true +) + prev_v, prev_pe = nothing, nothing + for update_pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + cur_v = parent(src(update_pe)) + bmpsc = if !isnothing(prev_pe) + biorthogonalize(bmpsc, prev_pe, update_pe) + else + biorthogonalize(bmpsc, update_pe) + end + bmpsc = if !isnothing(prev_v) + partition_update( + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) + else + partition_update( + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) + end + + me_prev = only(message(bmpsc, update_pe)) + me = only( + update_message( + bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) + ), + ) + p_above, p_below = partitionedge_above(bmpsc, update_pe), + partitionedge_below(bmpsc, update_pe) + if !isnothing(p_above) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_above)))), + commonind(me_prev, only(message(bmpsc, p_above))), + ) + end + if !isnothing(p_below) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_below)))), + commonind(me_prev, only(message(bmpsc, p_below))), + ) + end + bmpsc = set_message(bmpsc, update_pe, ITensor[me]) + prev_v, prev_pe = cur_v, update_pe + end + + return bmpsc +end + +""" +More generic interface for update, with default params +""" +function update( + alg::Algorithm, + bmpsc::BoundaryMPSCache; + partitionpairs=default_edge_sequence(bmpsc), + maxiter=default_bp_maxiter(alg, bmpsc), + mps_fit_kwargs=default_mps_fit_kwargs(alg, bmpsc), +) + if isnothing(maxiter) + error("You need to specify a number of iterations for Boundary MPS!") + end + for i in 1:maxiter + for partitionpair in partitionpairs + bmpsc = mps_update(alg, bmpsc, partitionpair; mps_fit_kwargs...) + end + end + return bmpsc +end + +function update(bmpsc::BoundaryMPSCache; alg::String="orthogonal", kwargs...) + return update(Algorithm(alg), bmpsc; kwargs...) +end + +#Assume all vertices live in the same partition for now +function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) + vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) + pv = only(planargraph_partitions(bmpsc, vs)) + bmpsc = partition_update(bmpsc, pv) + return environment(bp_cache(bmpsc), verts; kwargs...) +end + +function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, vertex; kwargs...) + return environment(bmpsc, [vertex]; kwargs...) +end + +#Forward onto beliefpropagationcache +for f in [ + :messages, + :message, + :update_message, + :(ITensorNetworks.linkinds), + :default_edge_sequence, + :factor, + :factors, +] + @eval begin + function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) + return $f(bp_cache(bmpsc), args...; kwargs...) + end + end +end + +#Wrap around beliefpropagationcache +for f in [:update_factors, :update_factor] + @eval begin + function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) + bmpsc = copy(bmpsc) + bpc = bp_cache(bmpsc) + bpc = $f(bpc, args...; kwargs...) + return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) + end + end +end + +#Wrap around beliefpropagationcache but only for specific argument +function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs...) + bmpsc = copy(bmpsc) + bpc = bp_cache(bmpsc) + bpc = update(bpc, pes; kwargs...) + return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) +end diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl new file mode 100644 index 00000000..fa3840e9 --- /dev/null +++ b/src/caches/boundarympscacheutils.jl @@ -0,0 +1,44 @@ +using ITensorNetworks: ITensorNetworks, BeliefPropagationCache +using NamedGraphs.PartitionedGraphs: PartitionedGraph + +#Add partition edges that may not have meaning in the underlying graph +function add_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) + g = partitioned_graph(pg) + g = add_edges(g, parent.(pes)) + return PartitionedGraph( + unpartitioned_graph(pg), g, partitioned_vertices(pg), which_partition(pg) + ) +end + +#Add partition edges that may not have meaning in the underlying graph +function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) + pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) + return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) +end + +#Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function +function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f=v -> first(v)) + pg = partitioned_graph(bpc) + partitions = unique(sort_f.(collect(vertices(pg)))) + pseudo_edges = PartitionEdge[] + for p in partitions + vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) + for i in 1:(length(vs) - 1) + if vs[i] ∉ neighbors(pg, vs[i + 1]) + push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i + 1]))) + end + end + end + return add_partitionedges(bpc, pseudo_edges) +end + +pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) + +#Return a sequence of pairs to go from item1 to item2 in an ordered_list +function pair_sequence(ordered_list::Vector, item1, item2) + item1_pos, item2_pos = only(findall(x -> x == item1, ordered_list)), + only(findall(x -> x == item2, ordered_list)) + item1_pos < item2_pos && + return [ordered_list[i] => ordered_list[i + 1] for i in item1_pos:(item2_pos - 1)] + return [ordered_list[i] => ordered_list[i - 1] for i in item1_pos:-1:(item2_pos + 1)] +end diff --git a/src/inner.jl b/src/inner.jl index 166a2c6f..43486703 100644 --- a/src/inner.jl +++ b/src/inner.jl @@ -1,4 +1,5 @@ -using ITensors: inner, scalar, loginner +using ITensors: inner, scalar +using ITensorMPS: ITensorMPS, loginner using LinearAlgebra: norm, norm_sqr default_contract_alg(tns::Tuple) = "bp" @@ -53,7 +54,7 @@ function ITensors.inner( return scalar(tn; sequence) end -function ITensors.loginner( +function ITensorMPS.loginner( ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; alg=default_contract_alg((ϕ, ψ)), @@ -62,7 +63,7 @@ function ITensors.loginner( return loginner(Algorithm(alg), ϕ, ψ; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, ψ::AbstractITensorNetwork; @@ -72,13 +73,13 @@ function ITensors.loginner( return loginner(Algorithm(alg), ϕ, A, ψ; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"exact", ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; kwargs... ) return log(inner(alg, ϕ, ψ); kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"exact", ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, @@ -88,7 +89,7 @@ function ITensors.loginner( return log(inner(alg, ϕ, A, ψ); kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"bp", ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; @@ -99,7 +100,7 @@ function ITensors.loginner( return logscalar(alg, tn; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"bp", ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, diff --git a/src/solvers/alternating_update/alternating_update.jl b/src/solvers/alternating_update/alternating_update.jl index 2cd5de71..750f3f36 100644 --- a/src/solvers/alternating_update/alternating_update.jl +++ b/src/solvers/alternating_update/alternating_update.jl @@ -9,8 +9,8 @@ function alternating_update( nsites, # define default for each level of solver implementation updater, # this specifies the update performed locally outputlevel=default_outputlevel(), - region_printer=nothing, - sweep_printer=nothing, + region_printer=default_region_printer, + sweep_printer=default_sweep_printer, (sweep_observer!)=nothing, (region_observer!)=nothing, root_vertex=GraphsExtensions.default_root_vertex(init_state), @@ -59,7 +59,7 @@ function alternating_update( (sweep_observer!)=nothing, sweep_printer=default_sweep_printer,#? (region_observer!)=nothing, - region_printer=nothing, + region_printer=default_region_printer, ) state = copy(init_state) @assert !isnothing(sweep_plans) diff --git a/src/solvers/alternating_update/region_update.jl b/src/solvers/alternating_update/region_update.jl index b92adc8c..c741c82a 100644 --- a/src/solvers/alternating_update/region_update.jl +++ b/src/solvers/alternating_update/region_update.jl @@ -1,44 +1,3 @@ -#ToDo: generalize beyond 2-site -#ToDo: remove concept of orthogonality center for generality -function current_ortho(sweep_plan, which_region_update) - regions = first.(sweep_plan) - region = regions[which_region_update] - current_verts = support(region) - if !isa(region, AbstractEdge) && length(region) == 1 - return only(current_verts) - end - if which_region_update == length(regions) - # look back by one should be sufficient, but may be brittle? - overlapping_vertex = only( - intersect(current_verts, support(regions[which_region_update - 1])) - ) - return overlapping_vertex - else - # look forward - other_regions = filter( - x -> !(issetequal(x, current_verts)), support.(regions[(which_region_update + 1):end]) - ) - # find the first region that has overlapping support with current region - ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - if isnothing(ind) - # look backward - other_regions = reverse( - filter( - x -> !(issetequal(x, current_verts)), - support.(regions[1:(which_region_update - 1)]), - ), - ) - ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - end - @assert !isnothing(ind) - future_verts = union(support(other_regions[ind])) - # return ortho_ceter as the vertex in current region that does not overlap with following one - overlapping_vertex = intersect(current_verts, future_verts) - nonoverlapping_vertex = only(setdiff(current_verts, overlapping_vertex)) - return nonoverlapping_vertex - end -end - function region_update( projected_operator, state; @@ -64,14 +23,13 @@ function region_update( # ToDo: remove orthogonality center on vertex for generality # region carries same information - ortho_vertex = current_ortho(sweep_plan, which_region_update) if !isnothing(transform_operator) projected_operator = transform_operator( state, projected_operator; outputlevel, transform_operator_kwargs... ) end state, projected_operator, phi = extracter( - state, projected_operator, region, ortho_vertex; extracter_kwargs..., internal_kwargs + state, projected_operator, region; extracter_kwargs..., internal_kwargs ) # create references, in case solver does (out-of-place) modify PH or state state! = Ref(state) @@ -97,9 +55,8 @@ function region_update( # drho = noise * noiseterm(PH, phi, ortho) # TODO: actually implement this for trees... # so noiseterm is a solver #end - state, spec = inserter( - state, phi, region, ortho_vertex; inserter_kwargs..., internal_kwargs - ) + #if isa(region, AbstractEdge) && + state, spec = inserter(state, phi, region; inserter_kwargs..., internal_kwargs) all_kwargs = (; which_region_update, sweep_plan, diff --git a/src/solvers/contract.jl b/src/solvers/contract.jl index 1e6ddeec..0fff3894 100644 --- a/src/solvers/contract.jl +++ b/src/solvers/contract.jl @@ -1,5 +1,6 @@ using Graphs: nv, vertices -using ITensors: ITensors, linkinds, sim +using ITensors: ITensors, sim +using ITensorMPS: linkinds using ITensors.NDTensors: Algorithm, @Algorithm_str, contract using NamedGraphs: vertextype diff --git a/src/solvers/defaults.jl b/src/solvers/defaults.jl index b5d315ff..09c2ae2f 100644 --- a/src/solvers/defaults.jl +++ b/src/solvers/defaults.jl @@ -1,4 +1,4 @@ -using Printf: @printf +using Printf: @printf, @sprintf using ITensorMPS: maxlinkdim default_outputlevel() = 0 default_nsites() = 2 @@ -7,10 +7,12 @@ default_extracter() = default_extracter default_inserter() = default_inserter default_checkdone() = (; kws...) -> false default_transform_operator() = nothing + +format(x) = @sprintf("%s", x) +format(x::AbstractFloat) = @sprintf("%.1E", x) + function default_region_printer(; - cutoff, - maxdim, - mindim, + inserter_kwargs, outputlevel, state, sweep_plan, @@ -23,9 +25,11 @@ function default_region_printer(; region = first(sweep_plan[which_region_update]) @printf("Sweep %d, region=%s \n", which_sweep, region) print(" Truncated using") - @printf(" cutoff=%.1E", cutoff) - @printf(" maxdim=%d", maxdim) - @printf(" mindim=%d", mindim) + for key in [:cutoff, :maxdim, :mindim] + if haskey(inserter_kwargs, key) + print(" ", key, "=", format(inserter_kwargs[key])) + end + end println() if spec != nothing @printf( diff --git a/src/solvers/extract/extract.jl b/src/solvers/extract/extract.jl index feb57c2f..1013d1bd 100644 --- a/src/solvers/extract/extract.jl +++ b/src/solvers/extract/extract.jl @@ -7,18 +7,20 @@ # insert_local_tensors takes that tensor and factorizes it back # apart and puts it back into the network. # -function default_extracter(state, projected_operator, region, ortho; internal_kwargs) - state = orthogonalize(state, ortho) + +function default_extracter(state, projected_operator, region; internal_kwargs) if isa(region, AbstractEdge) - other_vertex = only(setdiff(support(region), [ortho])) - left_inds = uniqueinds(state[ortho], state[other_vertex]) - #ToDo: replace with call to factorize + # TODO: add functionality for orthogonalizing onto a bond so that can be called instead + vsrc, vdst = src(region), dst(region) + state = orthogonalize(state, vsrc) + left_inds = uniqueinds(state[vsrc], state[vdst]) U, S, V = svd( - state[ortho], left_inds; lefttags=tags(state, region), righttags=tags(state, region) + state[vsrc], left_inds; lefttags=tags(state, region), righttags=tags(state, region) ) - state[ortho] = U + state[vsrc] = U local_tensor = S * V else + state = orthogonalize(state, region) local_tensor = prod(state[v] for v in region) end projected_operator = position(projected_operator, state, region) diff --git a/src/solvers/insert/insert.jl b/src/solvers/insert/insert.jl index 11aed223..01fb35bd 100644 --- a/src/solvers/insert/insert.jl +++ b/src/solvers/insert/insert.jl @@ -6,8 +6,7 @@ function default_inserter( state::AbstractTTN, phi::ITensor, - region, - ortho_vert; + region; normalize=false, maxdim=nothing, mindim=nothing, @@ -16,16 +15,14 @@ function default_inserter( ) state = copy(state) spec = nothing - other_vertex = setdiff(support(region), [ortho_vert]) - if !isempty(other_vertex) - v = only(other_vertex) - e = edgetype(state)(ortho_vert, v) - indsTe = inds(state[ortho_vert]) + if length(region) == 2 + v = last(region) + e = edgetype(state)(first(region), last(region)) + indsTe = inds(state[first(region)]) L, phi, spec = factorize(phi, indsTe; tags=tags(state, e), maxdim, mindim, cutoff) - state[ortho_vert] = L - + state[first(region)] = L else - v = ortho_vert + v = only(region) end state[v] = phi state = set_ortho_region(state, [v]) @@ -36,16 +33,14 @@ end function default_inserter( state::AbstractTTN, phi::ITensor, - region::NamedEdge, - ortho; + region::NamedEdge; cutoff=nothing, maxdim=nothing, mindim=nothing, normalize=false, internal_kwargs, ) - v = only(setdiff(support(region), [ortho])) - state[v] *= phi - state = set_ortho_region(state, [v]) + state[dst(region)] *= phi + state = set_ortho_region(state, [dst(region)]) return state, nothing end diff --git a/src/solvers/sweep_plans/sweep_plans.jl b/src/solvers/sweep_plans/sweep_plans.jl index 69221995..52915e2b 100644 --- a/src/solvers/sweep_plans/sweep_plans.jl +++ b/src/solvers/sweep_plans/sweep_plans.jl @@ -13,10 +13,11 @@ end support(r) = r -function reverse_region(edges, which_edge; nsites=1, region_kwargs=(;)) +function reverse_region(edges, which_edge; reverse_edge=false, nsites=1, region_kwargs=(;)) current_edge = edges[which_edge] if nsites == 1 - return [(current_edge, region_kwargs)] + !reverse_edge && return [(current_edge, region_kwargs)] + reverse_edge && return [(reverse(current_edge), region_kwargs)] elseif nsites == 2 if last(edges) == current_edge return () @@ -62,25 +63,24 @@ function forward_sweep( dir::Base.ForwardOrdering, graph::AbstractGraph; root_vertex=GraphsExtensions.default_root_vertex(graph), + reverse_edges=false, region_kwargs, reverse_kwargs=region_kwargs, reverse_step=false, kwargs..., ) edges = post_order_dfs_edges(graph, root_vertex) - regions = collect( - flatten(map(i -> forward_region(edges, i; region_kwargs, kwargs...), eachindex(edges))) - ) - + regions = map(eachindex(edges)) do i + forward_region(edges, i; region_kwargs, kwargs...) + end + regions = collect(flatten(regions)) if reverse_step - reverse_regions = collect( - flatten( - map( - i -> reverse_region(edges, i; region_kwargs=reverse_kwargs, kwargs...), - eachindex(edges), - ), - ), - ) + reverse_regions = map(eachindex(edges)) do i + reverse_region( + edges, i; reverse_edge=reverse_edges, region_kwargs=reverse_kwargs, kwargs... + ) + end + reverse_regions = collect(flatten(reverse_regions)) _check_reverse_sweeps(regions, reverse_regions, graph; kwargs...) regions = interleave(regions, reverse_regions) end @@ -90,7 +90,7 @@ end #ToDo: is there a better name for this? unidirectional_sweep? traversal? function forward_sweep(dir::Base.ReverseOrdering, args...; kwargs...) - return reverse(forward_sweep(Base.Forward, args...; kwargs...)) + return reverse(forward_sweep(Base.Forward, args...; reverse_edges=true, kwargs...)) end function default_sweep_plans( diff --git a/src/tebd.jl b/src/tebd.jl index edf5a188..d1d96017 100644 --- a/src/tebd.jl +++ b/src/tebd.jl @@ -23,7 +23,7 @@ function tebd( ψ = apply(u⃗, ψ; cutoff, maxdim, normalize=true, ortho, kwargs...) if ortho for v in vertices(ψ) - ψ = orthogonalize(ψ, v) + ψ = tree_orthogonalize(ψ, v) end end end diff --git a/src/treetensornetworks/abstracttreetensornetwork.jl b/src/treetensornetworks/abstracttreetensornetwork.jl index c8dccb1f..f6c8f49f 100644 --- a/src/treetensornetworks/abstracttreetensornetwork.jl +++ b/src/treetensornetworks/abstracttreetensornetwork.jl @@ -1,9 +1,15 @@ using Graphs: has_vertex using NamedGraphs.GraphsExtensions: - GraphsExtensions, edge_path, leaf_vertices, post_order_dfs_edges, post_order_dfs_vertices + GraphsExtensions, + edge_path, + leaf_vertices, + post_order_dfs_edges, + post_order_dfs_vertices, + a_star +using NamedGraphs: namedgraph_a_star, steiner_tree using IsApprox: IsApprox, Approx -using ITensors: @Algorithm_str, directsum, hasinds, permute, plev -using ITensorMPS: linkind, loginner, lognorm, orthogonalize +using ITensors: ITensors, Algorithm, @Algorithm_str, directsum, hasinds, permute, plev +using ITensorMPS: ITensorMPS, linkind, loginner, lognorm, orthogonalize using TupleTools: TupleTools abstract type AbstractTreeTensorNetwork{V} <: AbstractITensorNetwork{V} end @@ -29,30 +35,27 @@ function set_ortho_region(tn::AbstractTTN, new_region) return error("Not implemented") end -# -# Orthogonalization -# - -function ITensorMPS.orthogonalize(tn::AbstractTTN, ortho_center; kwargs...) - if isone(length(ortho_region(tn))) && ortho_center == only(ortho_region(tn)) - return tn - end - # TODO: Rewrite this in a more general way. - if isone(length(ortho_region(tn))) - edge_list = edge_path(tn, only(ortho_region(tn)), ortho_center) - else - edge_list = post_order_dfs_edges(tn, ortho_center) - end - for e in edge_list - tn = orthogonalize(tn, e) +function gauge(alg::Algorithm, ttn::AbstractTTN, region::Vector; kwargs...) + issetequal(region, ortho_region(ttn)) && return ttn + st = steiner_tree(ttn, union(region, ortho_region(ttn))) + path = post_order_dfs_edges(st, first(region)) + path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) + if !isempty(path) + ttn = typeof(ttn)(gauge_walk(alg, ITensorNetwork(ttn), path; kwargs...)) end - return set_ortho_region(tn, typeof(ortho_region(tn))([ortho_center])) + return set_ortho_region(ttn, region) end -# For ambiguity error +function gauge(alg::Algorithm, ttn::AbstractTTN, region; kwargs...) + return gauge(alg, ttn, [region]; kwargs...) +end + +function ITensorMPS.orthogonalize(ttn::AbstractTTN, region; kwargs...) + return gauge(Algorithm("orthogonalize"), ttn, region; kwargs...) +end -function ITensorMPS.orthogonalize(tn::AbstractTTN, edge::AbstractEdge; kwargs...) - return typeof(tn)(orthogonalize(ITensorNetwork(tn), edge; kwargs...)) +function tree_orthogonalize(ttn::AbstractTTN, args...; kwargs...) + return orthogonalize(ttn, args...; kwargs...) end # @@ -273,13 +276,13 @@ end Base.:+(tn::AbstractTTN) = tn -ITensors.add(tns::AbstractTTN...; kwargs...) = +(tns...; kwargs...) +ITensorMPS.add(tns::AbstractTTN...; kwargs...) = +(tns...; kwargs...) function Base.:-(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) return +(tn1, -tn2; kwargs...) end -function ITensors.add(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) +function ITensorMPS.add(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) return +(tn1, tn2; kwargs...) end diff --git a/test/Project.toml b/test/Project.toml index 70eb14d3..923e3bbe 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -21,7 +21,7 @@ NDTensors = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" OMEinsumContractionOrders = "6f22d1fd-8eed-4bb7-9776-e7d684900715" Observers = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index 728b06d6..a151e4d1 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -23,7 +23,8 @@ using ITensorNetworks: tensornetwork, update, update_factor, - update_message + update_message, + message_diff using ITensors: ITensors, ITensor, combiner, dag, inds, inner, op, prime, random_itensor using ITensorNetworks.ModelNetworks: ModelNetworks using ITensors.NDTensors: array @@ -34,50 +35,56 @@ using NamedGraphs.PartitionedGraphs: PartitionVertex, partitionedges using SplitApplyCombine: group using StableRNGs: StableRNG using Test: @test, @testset -@testset "belief_propagation" begin - ITensors.disable_warn_order() - g = named_grid((3, 3)) - s = siteinds("S=1/2", g) - χ = 2 - rng = StableRNG(1234) - ψ = random_tensornetwork(rng, s; link_space=χ) - ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - bpc = BeliefPropagationCache(ψψ) - bpc = update(bpc; maxiter=50, tol=1e-10) - #Test messages are converged - for pe in partitionedges(partitioned_tensornetwork(bpc)) - @test update_message(bpc, pe) ≈ message(bpc, pe) atol = 1e-8 - end - #Test updating the underlying tensornetwork in the cache - v = first(vertices(ψψ)) - rng = StableRNG(1234) - new_tensor = random_itensor(rng, inds(ψψ[v])) - bpc_updated = update_factor(bpc, v, new_tensor) - ψψ_updated = tensornetwork(bpc_updated) - @test ψψ_updated[v] == new_tensor - #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD - vs = [(2, 2), (2, 3)] +@testset "belief_propagation (eltype=$elt)" for elt in ( + Float32, Float64, Complex{Float32}, Complex{Float64} +) + begin + ITensors.disable_warn_order() + g = named_grid((3, 3)) + s = siteinds("S=1/2", g) + χ = 2 + rng = StableRNG(1234) + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + ψψ = ψ ⊗ prime(dag(ψ); sites=[]) + bpc = BeliefPropagationCache(ψψ, group(v -> first(v), vertices(ψψ))) + bpc = update(bpc; maxiter=25, tol=eps(real(elt))) + #Test messages are converged + for pe in partitionedges(partitioned_tensornetwork(bpc)) + @test message_diff(update_message(bpc, pe), message(bpc, pe)) < 10 * eps(real(elt)) + @test eltype(only(message(bpc, pe))) == elt + end + #Test updating the underlying tensornetwork in the cache + v = first(vertices(ψψ)) + rng = StableRNG(1234) + new_tensor = random_itensor(rng, inds(ψψ[v])) + bpc_updated = update_factor(bpc, v, new_tensor) + ψψ_updated = tensornetwork(bpc_updated) + @test ψψ_updated[v] == new_tensor + + #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD + vs = [(2, 2), (2, 3)] - ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) - env_tensors = environment(bpc, [(v, 2) for v in vs]) - rdm = contract(vcat(env_tensors, ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]])) + ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) + env_tensors = environment(bpc, [(v, 2) for v in vs]) + rdm = contract(vcat(env_tensors, ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]])) - rdm = array((rdm * combiner(inds(rdm; plev=0)...)) * combiner(inds(rdm; plev=1)...)) - rdm /= tr(rdm) + rdm = array((rdm * combiner(inds(rdm; plev=0)...)) * combiner(inds(rdm; plev=1)...)) + rdm /= tr(rdm) - eigs = eigvals(rdm) - @test size(rdm) == (2^length(vs), 2^length(vs)) + eigs = eigvals(rdm) + @test size(rdm) == (2^length(vs), 2^length(vs)) - @test all(eig -> imag(eig) ≈ 0, eigs) - @test all(eig -> real(eig) >= -eps(eltype(eig)), eigs) + @test all(eig -> abs(imag(eig)) <= eps(real(elt)), eigs) + @test all(eig -> real(eig) >= -eps(real(elt)), eigs) - #Test edge case of network which evalutes to 0 - χ = 2 - g = named_grid((3, 1)) - rng = StableRNG(1234) - ψ = random_tensornetwork(rng, ComplexF64, g; link_space=χ) - ψ[(1, 1)] = 0.0 * ψ[(1, 1)] - @test iszero(scalar(ψ; alg="bp")) + #Test edge case of network which evalutes to 0 + χ = 2 + g = named_grid((3, 1)) + rng = StableRNG(1234) + ψ = random_tensornetwork(rng, elt, g; link_space=χ) + ψ[(1, 1)] = 0 * ψ[(1, 1)] + @test iszero(scalar(ψ; alg="bp")) + end end end diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl new file mode 100644 index 00000000..4ea4f607 --- /dev/null +++ b/test/test_boundarymps.jl @@ -0,0 +1,169 @@ +@eval module $(gensym()) +using Compat: Compat +using Graphs: vertices, center +# Trigger package extension. +using ITensorNetworks: + ITensorNetworks, + BeliefPropagationCache, + BoundaryMPSCache, + ITensorNetwork, + QuadraticFormNetwork, + VidalITensorNetwork, + ⊗, + combine_linkinds, + contract, + contract_boundary_mps, + contraction_sequence, + default_message_update, + eachtensor, + environment, + inner_network, + linkinds_combiners, + message, + messages, + partitioned_tensornetwork, + random_tensornetwork, + scalar, + siteinds, + split_index, + tensornetwork, + update, + update_factor, + update_message, + message_diff +using ITensors: + ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor +using ITensorNetworks.ModelNetworks: ModelNetworks +using ITensorNetworks.ITensorsExtensions: map_eigvals +using ITensors.NDTensors: array +using LinearAlgebra: eigvals, tr +using NamedGraphs: NamedEdge, NamedGraph, subgraph +using NamedGraphs.GraphsExtensions: rem_vertices +using NamedGraphs.NamedGraphGenerators: + named_comb_tree, named_grid, named_hexagonal_lattice_graph +using NamedGraphs.PartitionedGraphs: PartitionEdge, PartitionVertex, partitionedges +using SplitApplyCombine: group +using StableRNGs: StableRNG +using Test: @test, @testset +using OMEinsumContractionOrders +using LinearAlgebra: norm + +@testset "boundarymps (eltype=$elt)" for elt in ( + Float32, Float64, Complex{Float32}, Complex{Float64} +) + begin + ITensors.disable_warn_order() + mps_fit_kwargs = (; niters=50, tolerance=1e-10) + rng = StableRNG(1234) + + #First a comb tree (which is still a planar graph) and a flat tensor network + g = named_comb_tree((4, 4)) + χ = 2 + tn = random_tensornetwork(rng, elt, g; link_space=χ) + vc = first(center(g)) + + ∂tn_∂vc_bp = contract(environment(tn, [vc]; alg="bp"); sequence="automatic") + ∂tn_∂vc_bp /= norm(∂tn_∂vc_bp) + + ∂tn_∂vc = subgraph(tn, setdiff(vertices(tn), [vc])) + ∂tn_∂vc_exact = contract(∂tn_∂vc; sequence=contraction_sequence(∂tn_∂vc; alg="greedy")) + ∂tn_∂vc_exact /= norm(∂tn_∂vc_exact) + + #Orthogonal Boundary MPS + tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + tn_boundaryMPS = update(tn_boundaryMPS; mps_fit_kwargs) + ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") + ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) + + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) + + #Biorthogonal Boundary MPS + tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + tn_boundaryMPS = update(tn_boundaryMPS; alg="biorthogonal") + ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") + ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) + + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) + @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) + + #Now the norm tensor network of a square graph with a few vertices missing for added complexity + g = named_grid((5, 5)) + g = rem_vertices(g, [(2, 2), (3, 3)]) + s = siteinds("S=1/2", g) + χ = 3 + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + ψIψ = QuadraticFormNetwork(ψ) + vc = (first(center(g)), "operator") + ρ = subgraph(ψIψ, setdiff(vertices(ψIψ), [vc])) + ρ_exact = contract(ρ; sequence=contraction_sequence(ρ; alg="greedy")) + ρ_exact /= tr(ρ_exact) + + #Orthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= 10 * eps(real(elt)) + + #BiOrthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal", maxiter=50) + ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") + ρ_boundaryMPS /= tr(ρ_boundaryMPS) + + @test norm(ρ_boundaryMPS - ρ_exact) <= 2e3 * eps(real(elt)) + + #Now we test BP and orthogonal and biorthogonl Boundary MPS are equivalent when run from in the symmetric gauge + g = named_hexagonal_lattice_graph(3, 3) + s = siteinds("S=1/2", g) + χ = 2 + ψ = random_tensornetwork(rng, elt, s; link_space=χ) + + #Move wavefunction to symmetric gauge, enforce posdefness for complex number stability + function make_posdef(A::ITensor) + return map_eigvals( + x -> abs(real(x)), A, first(inds(A)), last(inds(A)); ishermitian=true + ) + end + message_update_f = ms -> make_posdef.(default_message_update(ms)) + ψ_vidal = VidalITensorNetwork( + ψ; cache_update_kwargs=(; message_update=message_update_f, maxiter=100, tol=1e-16) + ) + cache_ref = Ref{BeliefPropagationCache}() + ψ_symm = ITensorNetwork(ψ_vidal; (cache!)=cache_ref) + bp_cache = cache_ref[] + + #Do Orthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank=1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) + + for pe in keys(messages(bp_cache)) + m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) + #Prune the dimension 1 virtual index from boundary MPS message tensor + m_boundarymps = + m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) + m_bp = only(message(bp_cache, pe)) + m_bp /= tr(m_bp) + m_boundarymps /= tr(m_boundarymps) + @test norm(m_bp - m_boundarymps) <= 1e-4 + end + + #Do Biorthogonal Boundary MPS + ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank=1) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal") + + for pe in keys(messages(bp_cache)) + m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) + #Prune the dimension 1 virtual index from boundary MPS message tensor + m_boundarymps = + m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) + m_bp = only(message(bp_cache, pe)) + m_bp /= tr(m_bp) + m_boundarymps /= tr(m_boundarymps) + @test norm(m_bp - m_boundarymps) <= 1e-4 + end + end +end +end diff --git a/test/test_gauging.jl b/test/test_gauging.jl index 9eb4ebbf..67b1b5dd 100644 --- a/test/test_gauging.jl +++ b/test/test_gauging.jl @@ -27,9 +27,7 @@ using Test: @test, @testset ψ = random_tensornetwork(rng, s; link_space=χ) # Move directly to vidal gauge - ψ_vidal = VidalITensorNetwork( - ψ; cache_update_kwargs=(; maxiter=20, tol=1e-12, verbose=true) - ) + ψ_vidal = VidalITensorNetwork(ψ; cache_update_kwargs=(; maxiter=30, verbose=true)) @test gauge_error(ψ_vidal) < 1e-8 # Move to symmetric gauge @@ -38,7 +36,7 @@ using Test: @test, @testset bp_cache = cache_ref[] # Test we just did a gauge transform and didn't change the overall network - @test inner(ψ_symm, ψ) / sqrt(inner(ψ_symm, ψ_symm) * inner(ψ, ψ)) ≈ 1.0 + @test inner(ψ_symm, ψ) / sqrt(inner(ψ_symm, ψ_symm) * inner(ψ, ψ)) ≈ 1.0 atol = 1e-8 #Test all message tensors are approximately diagonal even when we keep running BP bp_cache = update(bp_cache; maxiter=10) diff --git a/test/test_itensornetwork.jl b/test/test_itensornetwork.jl index ba3caa01..53e2928f 100644 --- a/test/test_itensornetwork.jl +++ b/test/test_itensornetwork.jl @@ -51,6 +51,7 @@ using ITensorNetworks: orthogonalize, random_tensornetwork, siteinds, + tree_orthogonalize, ttn using LinearAlgebra: factorize using NamedGraphs: NamedEdge @@ -287,13 +288,13 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test nv(tn_ortho) == 5 @test nv(tn) == 4 @test Z ≈ Z̃ - tn_ortho = orthogonalize(tn, 4 => 3) + tn_ortho = tree_orthogonalize(tn, [3, 4]) Z̃ = norm_sqr(tn_ortho) @test nv(tn_ortho) == 4 @test nv(tn) == 4 @test Z ≈ Z̃ - tn_ortho = orthogonalize(tn, 1) + tn_ortho = tree_orthogonalize(tn, 1) Z̃ = norm_sqr(tn_ortho) @test Z ≈ Z̃ Z̃ = inner(tn_ortho, tn) diff --git a/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl b/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl index 82924f74..9b9568be 100644 --- a/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl +++ b/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl @@ -1,7 +1,7 @@ -using OrdinaryDiffEq: ODEProblem, Tsit5, solve -using ITensors: ITensor using ITensorNetworks: TimeDependentSum, to_vec +using ITensors: ITensor using KrylovKit: exponentiate +using OrdinaryDiffEqTsit5: ODEProblem, Tsit5, solve function ode_solver( H::TimeDependentSum, diff --git a/test/test_treetensornetworks/test_solvers/Project.toml b/test/test_treetensornetworks/test_solvers/Project.toml index 77225041..e4716249 100644 --- a/test/test_treetensornetworks/test_solvers/Project.toml +++ b/test/test_treetensornetworks/test_solvers/Project.toml @@ -7,7 +7,8 @@ ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" Observers = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/test_treetensornetworks/test_solvers/test_dmrg.jl b/test/test_treetensornetworks/test_solvers/test_dmrg.jl index b352d43c..004ec561 100644 --- a/test/test_treetensornetworks/test_solvers/test_dmrg.jl +++ b/test/test_treetensornetworks/test_solvers/test_dmrg.jl @@ -1,7 +1,7 @@ @eval module $(gensym()) using DataGraphs: edge_data, vertex_data using Dictionaries: Dictionary -using Graphs: nv, vertices +using Graphs: nv, vertices, uniform_tree using ITensorMPS: ITensorMPS using ITensorNetworks: ITensorNetworks, @@ -19,9 +19,11 @@ using ITensorNetworks.ITensorsExtensions: replace_vertices using ITensorNetworks.ModelHamiltonians: ModelHamiltonians using ITensors: ITensors using KrylovKit: eigsolve +using NamedGraphs: NamedGraph, rename_vertices using NamedGraphs.NamedGraphGenerators: named_comb_tree using Observers: observer using StableRNGs: StableRNG +using Suppressor: @capture_out using Test: @test, @test_broken, @testset # This is needed since `eigen` is broken @@ -76,6 +78,31 @@ ITensors.disable_auto_fermion() new_E = inner(psi', H, psi) @test new_E ≈ orig_E =# + + # + # Test outputlevels are working + # + prev_output = "" + for outputlevel in 0:2 + output = @capture_out begin + e, psi = dmrg( + H, + psi; + outputlevel, + nsweeps, + maxdim, + cutoff, + nsites, + updater_kwargs=(; krylovdim=3, maxiter=1), + ) + end + if outputlevel == 0 + @test length(output) == 0 + else + @test length(output) > length(prev_output) + end + prev_output = output + end end @testset "Observers" begin @@ -139,7 +166,7 @@ end nsweeps, maxdim, cutoff, - outputlevel=2, + outputlevel=0, transform_operator=ITensorNetworks.cache_operator_to_disk, transform_operator_kwargs=(; write_when_maxdim_exceeds=11), ) @@ -287,11 +314,12 @@ end nsites = 2 nsweeps = 10 - c = named_comb_tree((3, 2)) - s = siteinds("S=1/2", c) - os = ModelHamiltonians.heisenberg(c) - H = ttn(os, s) rng = StableRNG(1234) + g = NamedGraph(uniform_tree(10)) + g = rename_vertices(v -> (v, 1), g) + s = siteinds("S=1/2", g) + os = ModelHamiltonians.heisenberg(g) + H = ttn(os, s) psi = random_ttn(rng, s; link_space=5) e, psi = dmrg(H, psi; nsweeps, maxdim, nsites) diff --git a/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl b/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl index 17f1cc71..4101bc83 100644 --- a/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl +++ b/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl @@ -1,12 +1,12 @@ @eval module $(gensym()) -using ITensors: contract using ITensorNetworks: ITensorNetworks, TimeDependentSum, ttn, mpo, mps, siteinds, tdvp using ITensorNetworks.ModelHamiltonians: ModelHamiltonians -using OrdinaryDiffEq: Tsit5 +using ITensors: contract using KrylovKit: exponentiate using LinearAlgebra: norm using NamedGraphs: AbstractNamedEdge using NamedGraphs.NamedGraphGenerators: named_comb_tree +using OrdinaryDiffEqTsit5: Tsit5 using Test: @test, @test_broken, @testset include( From 9d64029c74cd82cc6c4c94c54fa88202725969fb Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 11:12:59 +0000 Subject: [PATCH 26/47] Revert BP Cache Code --- examples/test.jl | 21 --------------------- src/caches/beliefpropagationcache.jl | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 examples/test.jl diff --git a/examples/test.jl b/examples/test.jl deleted file mode 100644 index 6bf1bfcf..00000000 --- a/examples/test.jl +++ /dev/null @@ -1,21 +0,0 @@ -using ITensorNetworks: IndsNetwork, siteinds, ttn -using ITensorNetworks.ModelHamiltonians: ising -using ITensors: Index, OpSum, terms, sites -using NamedGraphs.NamedGraphGenerators: named_grid -using NamedGraphs.GraphsExtensions: rem_vertex - -function filter_terms(H, verts) - H_new = OpSum() - for term in terms(H) - if isempty(filter(v -> v ∈ verts, sites(term))) - H_new += term - end - end - return H_new -end - -g = named_grid((8,1)) -s = siteinds("S=1/2", g) -H = ising(s) -H_mod = filter_terms(H, [(4,1)]) -ttno = ttn(H_mod, s) \ No newline at end of file diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index 73f3c521..d5886ec7 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -14,7 +14,7 @@ using NamedGraphs.PartitionedGraphs: using SimpleTraits: SimpleTraits, Not, @traitfn using NDTensors: NDTensors -default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, inds_e))] +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] default_messages(ptn::PartitionedGraph) = Dictionary() function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) sequence = optimal_contraction_sequence(contract_list) From dc463394822b15f527b6d3c0298b0c8379947bd8 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 11:25:33 +0000 Subject: [PATCH 27/47] Rename kwarg --- src/caches/boundarympscache.jl | 36 +++++++++++++++-------------- src/caches/boundarympscacheutils.jl | 8 ++++--- test/test_boundarymps.jl | 12 +++++----- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index c5c57b4b..04a05f75 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -51,11 +51,13 @@ function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) end function BoundaryMPSCache( - bpc::BeliefPropagationCache; sort_f::Function=v -> first(v), message_rank::Int64=1 + bpc::BeliefPropagationCache; + grouping_function::Function=v -> first(v), + message_rank::Int64=1, ) - bpc = insert_pseudo_planar_edges(bpc; sort_f) + bpc = insert_pseudo_planar_edges(bpc; grouping_function) planar_graph = partitioned_graph(bpc) - vertex_groups = group(sort_f, collect(vertices(planar_graph))) + vertex_groups = group(grouping_function, collect(vertices(planar_graph))) ppg = PartitionedGraph(planar_graph, vertex_groups) bmpsc = BoundaryMPSCache(bpc, ppg) return set_interpartition_messages(bmpsc, message_rank) @@ -319,15 +321,15 @@ function mps_update( end bmpsc = if !isnothing(prev_v) partition_update( - bmpsc, - prev_v, - cur_v; - message_update=ms -> default_message_update(ms; normalize=false), - ) + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) else partition_update( - bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) - ) + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) end me = update_message( bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) @@ -360,15 +362,15 @@ function mps_update( end bmpsc = if !isnothing(prev_v) partition_update( - bmpsc, - prev_v, - cur_v; - message_update=ms -> default_message_update(ms; normalize=false), - ) + bmpsc, + prev_v, + cur_v; + message_update=ms -> default_message_update(ms; normalize=false), + ) else partition_update( - bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) - ) + bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) + ) end me_prev = only(message(bmpsc, update_pe)) diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index fa3840e9..f64aa9cd 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -17,12 +17,14 @@ function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:Partition end #Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function -function insert_pseudo_planar_edges(bpc::BeliefPropagationCache; sort_f=v -> first(v)) +function insert_pseudo_planar_edges( + bpc::BeliefPropagationCache; grouping_function=v -> first(v) +) pg = partitioned_graph(bpc) - partitions = unique(sort_f.(collect(vertices(pg)))) + partitions = unique(grouping_function.(collect(vertices(pg)))) pseudo_edges = PartitionEdge[] for p in partitions - vs = sort(filter(v -> sort_f(v) == p, collect(vertices(pg)))) + vs = sort(filter(v -> grouping_function(v) == p, collect(vertices(pg)))) for i in 1:(length(vs) - 1) if vs[i] ∉ neighbors(pg, vs[i + 1]) push!(pseudo_edges, PartitionEdge(NamedEdge(vs[i] => vs[i + 1]))) diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 4ea4f607..0f512359 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -69,8 +69,8 @@ using LinearAlgebra: norm ∂tn_∂vc_exact = contract(∂tn_∂vc; sequence=contraction_sequence(∂tn_∂vc; alg="greedy")) ∂tn_∂vc_exact /= norm(∂tn_∂vc_exact) - #Orthogonal Boundary MPS - tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + #Orthogonal Boundary MPS, group by row + tn_boundaryMPS = BoundaryMPSCache(tn; grouping_function=v -> last(v), message_rank=1) tn_boundaryMPS = update(tn_boundaryMPS; mps_fit_kwargs) ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) @@ -78,8 +78,8 @@ using LinearAlgebra: norm @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) - #Biorthogonal Boundary MPS - tn_boundaryMPS = BoundaryMPSCache(tn; message_rank=1) + #Biorthogonal Boundary MPS, , group by row + tn_boundaryMPS = BoundaryMPSCache(tn; grouping_function=v -> last(v), message_rank=1) tn_boundaryMPS = update(tn_boundaryMPS; alg="biorthogonal") ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) @@ -99,7 +99,7 @@ using LinearAlgebra: norm ρ_exact = contract(ρ; sequence=contraction_sequence(ρ; alg="greedy")) ρ_exact /= tr(ρ_exact) - #Orthogonal Boundary MPS + #Orthogonal Boundary MPS, group by column (default) ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") @@ -107,7 +107,7 @@ using LinearAlgebra: norm @test norm(ρ_boundaryMPS - ρ_exact) <= 10 * eps(real(elt)) - #BiOrthogonal Boundary MPS + #BiOrthogonal Boundary MPS, group by column (default) ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal", maxiter=50) ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") From dd228636f7dfd4d154ee04e7afc5390c63ae3e40 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 15:24:28 +0000 Subject: [PATCH 28/47] examples/test_boundarymps.jl --- src/caches/boundarympscache.jl | 77 ++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 04a05f75..d3b8108e 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -73,23 +73,49 @@ function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) return PartitionEdge.([vs[i] => vs[i + 1] for i in 1:(length(vs) - 1)]) end -#Functions to get the partitionedge sitting parallel above and below a message tensor -function partitionedge_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) +#Get all partitionedges between the pair of neighboring partitions, sorted top to bottom +#TODO: Bring in line with NamedGraphs change +function planargraph_partitionpair_partitionedges( + bmpsc::BoundaryMPSCache, partitionpair::Pair +) + pg = planargraph(bmpsc) + src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), + planargraph_vertices(bmpsc, last(partitionpair)) + es = filter( + x -> !isempty(last(x)), + [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs], + ) + es = map(x -> first(x) => only(last(x)), es) + return sort(PartitionEdge.(NamedEdge.(es)); by=x -> src(parent(x))) +end + +#Functions to get the parellel partitionedges sitting above and below a partitionedge +function partitionedges_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) pes = planargraph_partitionpair_partitionedges( bmpsc, planargraph_partitionpair(bmpsc, pe) ) pe_pos = only(findall(x -> x == pe, pes)) - pe_pos == length(pes) && return nothing - return pes[pe_pos + 1] + return PartitionEdge[pes[i] for i in (pe_pos + 1):length(pes)] end -function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) +function partitionedges_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) pes = planargraph_partitionpair_partitionedges( bmpsc, planargraph_partitionpair(bmpsc, pe) ) pe_pos = only(findall(x -> x == pe, pes)) - pe_pos == 1 && return nothing - return pes[pe_pos - 1] + return PartitionEdge[pes[i] for i in 1:(pe_pos - 1)] +end + +function partitionedge_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes_above = partitionedges_above(bmpsc, pe) + isempty(pes_above) && return nothing + return first(pes_above) +end + +function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) + pes_below = partitionedges_below(bmpsc, pe) + isempty(pes_below) && return nothing + return last(pes_below) end #Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 @@ -113,22 +139,6 @@ function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ) end -#Get all partitionedges between the pair of neighboring partitions, sorted top to bottom -#TODO: Bring in line with NamedGraphs change -function planargraph_partitionpair_partitionedges( - bmpsc::BoundaryMPSCache, partitionpair::Pair -) - pg = planargraph(bmpsc) - src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), - planargraph_vertices(bmpsc, last(partitionpair)) - es = filter( - x -> !isempty(last(x)), - [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs], - ) - es = map(x -> first(x) => only(last(x)), es) - return sort(PartitionEdge.(NamedEdge.(es)); by=x -> src(parent(x))) -end - function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) bmpsc = copy(bmpsc) ms = messages(bmpsc) @@ -144,9 +154,23 @@ function set_interpartition_messages( ms = messages(bmpsc) pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) prev_virtual_ind = nothing + maximum_virtual_dim_from_below = 1 for (i, pg_pe) in enumerate(pes) siteinds = linkinds(bmpsc, pg_pe) - next_virtual_index = i != length(pes) ? Index(message_rank, "m$(i)$(i+1)") : nothing + maximum_virtual_dim_from_below *= prod(dim.(siteinds)) + maximum_virtual_dim_from_above = if i != length(pes) + prod( + dim.( + reduce(vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pg_pe)]) + ), + ) + else + 1 + end + virtualind_dim = minimum([ + message_rank, maximum_virtual_dim_from_above, maximum_virtual_dim_from_below + ]) + next_virtual_index = i != length(pes) ? Index(virtualind_dim, "m$(i)$(i+1)") : nothing me = denseblocks(delta(siteinds)) virt_inds = filter(x -> !isnothing(x), [prev_virtual_ind, next_virtual_index]) if !isempty(virt_inds) @@ -234,8 +258,7 @@ function gauge_step( alg::Algorithm"biorthogonalize", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, - pe2::PartitionEdge; - regularization=1e-12, + pe2::PartitionEdge, ) bmpsc = copy(bmpsc) ms = messages(bmpsc) @@ -274,7 +297,7 @@ end #Move the orthogonality / biorthogonality centre on an interpartition via a sequence of steps between message tensors function gauge_walk(alg::Algorithm, bmpsc::BoundaryMPSCache, seq::Vector; kwargs...) for (pe1, pe2) in seq - bmpsc = gauge_step(alg::Algorithm, bmpsc, pe1, pe2) + bmpsc = gauge_step(alg::Algorithm, bmpsc, pe1, pe2; kwargs...) end return bmpsc end From 807739f5f5641ec5eb6d8e1ef838ae129b90fcec Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 15:24:51 +0000 Subject: [PATCH 29/47] Updated tests --- test/test_boundarymps.jl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 0f512359..38edc142 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -107,14 +107,6 @@ using LinearAlgebra: norm @test norm(ρ_boundaryMPS - ρ_exact) <= 10 * eps(real(elt)) - #BiOrthogonal Boundary MPS, group by column (default) - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; alg="biorthogonal", maxiter=50) - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") - ρ_boundaryMPS /= tr(ρ_boundaryMPS) - - @test norm(ρ_boundaryMPS - ρ_exact) <= 2e3 * eps(real(elt)) - #Now we test BP and orthogonal and biorthogonl Boundary MPS are equivalent when run from in the symmetric gauge g = named_hexagonal_lattice_graph(3, 3) s = siteinds("S=1/2", g) @@ -129,7 +121,7 @@ using LinearAlgebra: norm end message_update_f = ms -> make_posdef.(default_message_update(ms)) ψ_vidal = VidalITensorNetwork( - ψ; cache_update_kwargs=(; message_update=message_update_f, maxiter=100, tol=1e-16) + ψ; cache_update_kwargs=(; message_update=message_update_f, maxiter=50, tol=1e-14) ) cache_ref = Ref{BeliefPropagationCache}() ψ_symm = ITensorNetwork(ψ_vidal; (cache!)=cache_ref) From ee078e51116c16e2f95977789ea39430e97bf960 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 15:32:54 +0000 Subject: [PATCH 30/47] Formatting --- src/caches/boundarympscache.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index d3b8108e..f0cee27c 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -160,10 +160,10 @@ function set_interpartition_messages( maximum_virtual_dim_from_below *= prod(dim.(siteinds)) maximum_virtual_dim_from_above = if i != length(pes) prod( - dim.( - reduce(vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pg_pe)]) - ), - ) + dim.( + reduce(vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pg_pe)]) + ), + ) else 1 end From a02842248e5dc123e3c3353375476f9b658d58a7 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 18:25:16 +0000 Subject: [PATCH 31/47] AbstractCache --- src/ITensorNetworks.jl | 5 +- src/caches/abstractbeliefpropagationcache.jl | 236 +++++++++++++++++++ src/caches/beliefpropagationcache.jl | 220 +---------------- 3 files changed, 247 insertions(+), 214 deletions(-) create mode 100644 src/caches/abstractbeliefpropagationcache.jl diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 85126258..4da7f0d3 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -28,9 +28,10 @@ include("edge_sequences.jl") include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") +include("caches/abstractbeliefpropagationcache.jl") include("caches/beliefpropagationcache.jl") -include("caches/boundarympscacheutils.jl") -include("caches/boundarympscache.jl") +#include("caches/boundarympscacheutils.jl") +#include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl new file mode 100644 index 00000000..3646faa5 --- /dev/null +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -0,0 +1,236 @@ +using Graphs: IsDirected +using SplitApplyCombine: group +using LinearAlgebra: diag, dot +using ITensors: dir +using ITensorMPS: ITensorMPS +using NamedGraphs.PartitionedGraphs: + PartitionedGraphs, + PartitionedGraph, + PartitionVertex, + boundary_partitionedges, + partitionvertices, + partitionedges, + unpartitioned_graph +using SimpleTraits: SimpleTraits, Not, @traitfn +using NDTensors: NDTensors + +abstract type AbstractBeliefPropagationCache end + +function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) + sequence = optimal_contraction_sequence(contract_list) + updated_messages = contract(contract_list; sequence, kwargs...) + message_norm = norm(updated_messages) + if normalize && !iszero(message_norm) + updated_messages /= message_norm + end + return ITensor[updated_messages] +end + +#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages +function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) + lhs, rhs = contract(message_a), contract(message_b) + f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) + return 1 - f +end + +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] +default_messages(ptn::PartitionedGraph) = Dictionary() +@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing +@traitfn function default_bp_maxiter(g::::IsDirected) + return default_bp_maxiter(undirected_graph(underlying_graph(g))) +end +default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) +function default_partitioned_vertices(f::AbstractFormNetwork) + return group(v -> original_state_vertex(f, v), vertices(f)) +end +default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) + +partitioned_tensornetwork(bpc::AbstractBeliefPropagationCache) = not_implemented() +messages(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) = not_implemented() +Base.copy(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_bp_maxiter(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_edge_sequence(bpc::AbstractBeliefPropagationCache) = not_implemented() +environment(bpc::AbstractBeliefPropagationCache, partition_vertices::Vector{<:PartitionVertex}; kwargs...) = not_implemented() +region_scalar(bpc::AbstractBeliefPropagationCache, pv::PartitionVertex; kwargs...) = not_implemented() +region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) = not_implemented() + +function factors(bpc::AbstractBeliefPropagationCache, verts::Vector) + return ITensor[tensornetwork(bpc)[v] for v in verts] +end + +function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) + return factors(bpc, vertices(bpc, vertex)) +end + +function vertex_scalars( + bpc::AbstractBeliefPropagationCache, + pvs=partitionvertices(partitioned_tensornetwork(bpc)); + kwargs..., + ) + return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) +end + +function edge_scalars( + bpc::AbstractBeliefPropagationCache, + pes=partitionedges(partitioned_tensornetwork(bpc)); + kwargs..., + ) + return map(pe -> region_scalar(bpc, pe; kwargs...), pes) +end + +function scalar_factors_quotient(bpc::AbstractBeliefPropagationCache) + return vertex_scalars(bpc), edge_scalars(bpc) +end + +function environment( + bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... + ) + return environment(bpc, [partition_vertex]; kwargs...) +end + +function environment(bpc::AbstractBeliefPropagationCache, verts::Vector) + partition_verts = partitionvertices(bpc, verts) + messages = environment(bpc, partition_verts) + central_tensors = factors(bpc, setdiff(vertices(bpc, partition_verts), verts)) + return vcat(messages, central_tensors) +end + +function tensornetwork(bpc::AbstractBeliefPropagationCache) + return unpartitioned_graph(partitioned_tensornetwork(bpc)) +end + +#Forward from partitioned graph +for f in [ + :(PartitionedGraphs.partitioned_graph), + :(PartitionedGraphs.partitionedge), + :(PartitionedGraphs.partitionvertices), + :(PartitionedGraphs.vertices), + :(PartitionedGraphs.boundary_partitionedges), + :(ITensorMPS.linkinds), + ] + @eval begin + function $f(bpc::AbstractBeliefPropagationCache, args...; kwargs...) + return $f(partitioned_tensornetwork(bpc), args...; kwargs...) + end + end +end + +NDTensors.scalartype(bpc::AbstractBeliefPropagationCache) = scalartype(tensornetwork(bpc)) + +""" +Update the tensornetwork inside the cache +""" +function update_factors(bpc::AbstractBeliefPropagationCache, factors) + bpc = copy(bpc) + tn = tensornetwork(bpc) + for vertex in eachindex(factors) + # TODO: Add a check that this preserves the graph structure. + setindex_preserve_graph!(tn, factors[vertex], vertex) + end + return bpc +end + +function update_factor(bpc, vertex, factor) + return update_factors(bpc, Dictionary([vertex], [factor])) +end + +function message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) + mts = messages(bpc) + return get(() -> default_message(bpc, edge; kwargs...), mts, edge) +end +function messages(bpc::AbstractBeliefPropagationCache, edges; kwargs...) + return map(edge -> message(bpc, edge; kwargs...), edges) +end + +""" +Compute message tensor as product of incoming mts and local state +""" +function update_message( + bpc::AbstractBeliefPropagationCache, + edge::PartitionEdge; + message_update=default_message_update, + message_update_kwargs=(;), +) + vertex = src(edge) + messages = environment(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) + state = factor(bpc, vertex) + + return message_update(ITensor[messages; state]; message_update_kwargs...) +end + +""" +Do a sequential update of the message tensors on `edges` +""" +function update( + bpc::AbstractBeliefPropagationCache, + edges::Vector{<:PartitionEdge}; + (update_diff!)=nothing, + kwargs..., +) + bpc_updated = copy(bpc) + mts = messages(bpc_updated) + for e in edges + set!(mts, e, update_message(bpc, e; kwargs...)) + if !isnothing(update_diff!) + update_diff![] += message_diff(message(bpc, e), mts[e]) + end + end + return bpc_updated +end + + +""" +Update the message tensor on a single edge +""" +function update(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) + return update(bpc, [edge]; kwargs...) +end + +""" +Do parallel updates between groups of edges of all message tensors +Currently we send the full message tensor data struct to update for each edge_group. But really we only need the +mts relevant to that group. +""" +function update( + bpc::AbstractBeliefPropagationCache, + edge_groups::Vector{<:Vector{<:PartitionEdge}}; + kwargs..., +) + new_mts = copy(messages(bpc)) + for edges in edge_groups + bpc_t = update(bpc, edges; kwargs...) + for e in edges + new_mts[e] = message(bpc_t, e) + end + end + return set_messages(bpc, new_mts) +end + +""" +More generic interface for update, with default params +""" +function update( + bpc::AbstractBeliefPropagationCache; + edges=default_edge_sequence(bpc), + maxiter=default_bp_maxiter(bpc), + tol=nothing, + verbose=false, + kwargs..., +) + compute_error = !isnothing(tol) + if isnothing(maxiter) + error("You need to specify a number of iterations for BP!") + end + for i in 1:maxiter + diff = compute_error ? Ref(0.0) : nothing + bpc = update(bpc, edges; (update_diff!)=diff, kwargs...) + if compute_error && (diff.x / length(edges)) <= tol + if verbose + println("BP converged to desired precision after $i iterations.") + end + break + end + end + return bpc +end \ No newline at end of file diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index d5886ec7..22ca093f 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -14,57 +14,29 @@ using NamedGraphs.PartitionedGraphs: using SimpleTraits: SimpleTraits, Not, @traitfn using NDTensors: NDTensors -default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] -default_messages(ptn::PartitionedGraph) = Dictionary() -function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) - sequence = optimal_contraction_sequence(contract_list) - updated_messages = contract(contract_list; sequence, kwargs...) - message_norm = norm(updated_messages) - if normalize && !iszero(message_norm) - updated_messages /= message_norm - end - return ITensor[updated_messages] -end -@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing -@traitfn function default_bp_maxiter(g::::IsDirected) - return default_bp_maxiter(undirected_graph(underlying_graph(g))) -end -default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) -function default_partitioned_vertices(f::AbstractFormNetwork) - return group(v -> original_state_vertex(f, v), vertices(f)) -end -default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) function default_cache_construction_kwargs(alg::Algorithm"bp", ψ::AbstractITensorNetwork) return (; partitioned_vertices=default_partitioned_vertices(ψ)) end -#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages -function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) - lhs, rhs = contract(message_a), contract(message_b) - f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) - return 1 - f -end - -struct BeliefPropagationCache{PTN,MTS,DM} +struct BeliefPropagationCache{PTN,MTS} <: AbstractBeliefPropagationCache partitioned_tensornetwork::PTN messages::MTS - default_message::DM end #Constructors... function BeliefPropagationCache( - ptn::PartitionedGraph; messages=default_messages(ptn), default_message=default_message + ptn::PartitionedGraph; messages=default_messages(ptn) ) - return BeliefPropagationCache(ptn, messages, default_message) + return BeliefPropagationCache(ptn, messages) end -function BeliefPropagationCache(tn, partitioned_vertices; kwargs...) +function BeliefPropagationCache(tn::AbstractITensorNetwork, partitioned_vertices; kwargs...) ptn = PartitionedGraph(tn, partitioned_vertices) return BeliefPropagationCache(ptn; kwargs...) end function BeliefPropagationCache( - tn; partitioned_vertices=default_partitioned_vertices(tn), kwargs... + tn::AbstractITensorNetwork; partitioned_vertices=default_partitioned_vertices(tn), kwargs... ) return BeliefPropagationCache(tn, partitioned_vertices; kwargs...) end @@ -76,47 +48,20 @@ end function partitioned_tensornetwork(bp_cache::BeliefPropagationCache) return bp_cache.partitioned_tensornetwork end + messages(bp_cache::BeliefPropagationCache) = bp_cache.messages -default_message(bp_cache::BeliefPropagationCache) = bp_cache.default_message function tensornetwork(bp_cache::BeliefPropagationCache) return unpartitioned_graph(partitioned_tensornetwork(bp_cache)) end -#Forward from partitioned graph -for f in [ - :(PartitionedGraphs.partitioned_graph), - :(PartitionedGraphs.partitionedge), - :(PartitionedGraphs.partitionvertices), - :(PartitionedGraphs.vertices), - :(PartitionedGraphs.boundary_partitionedges), - :(ITensorMPS.linkinds), -] - @eval begin - function $f(bp_cache::BeliefPropagationCache, args...; kwargs...) - return $f(partitioned_tensornetwork(bp_cache), args...; kwargs...) - end - end -end - -NDTensors.scalartype(bp_cache) = scalartype(tensornetwork(bp_cache)) - function default_message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - return default_message(bp_cache)(scalartype(bp_cache), linkinds(bp_cache, edge)) -end - -function message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - mts = messages(bp_cache) - return get(() -> default_message(bp_cache, edge), mts, edge) -end -function messages(bp_cache::BeliefPropagationCache, edges; kwargs...) - return map(edge -> message(bp_cache, edge; kwargs...), edges) + return default_message(scalartype(bp_cache), linkinds(bp_cache, edge)) end function Base.copy(bp_cache::BeliefPropagationCache) return BeliefPropagationCache( copy(partitioned_tensornetwork(bp_cache)), copy(messages(bp_cache)), - default_message(bp_cache), ) end @@ -129,7 +74,7 @@ end function set_messages(cache::BeliefPropagationCache, messages) return BeliefPropagationCache( - partitioned_tensornetwork(cache), messages, default_message(cache) + partitioned_tensornetwork(cache), messages ) end @@ -143,135 +88,6 @@ function environment( return reduce(vcat, ms; init=ITensor[]) end -function environment( - bp_cache::BeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... -) - return environment(bp_cache, [partition_vertex]; kwargs...) -end - -function environment(bp_cache::BeliefPropagationCache, verts::Vector) - partition_verts = partitionvertices(bp_cache, verts) - messages = environment(bp_cache, partition_verts) - central_tensors = factors(bp_cache, setdiff(vertices(bp_cache, partition_verts), verts)) - return vcat(messages, central_tensors) -end - -function factors(bp_cache::BeliefPropagationCache, verts::Vector) - return ITensor[tensornetwork(bp_cache)[v] for v in verts] -end - -function factor(bp_cache::BeliefPropagationCache, vertex::PartitionVertex) - return factors(bp_cache, vertices(bp_cache, vertex)) -end - -""" -Compute message tensor as product of incoming mts and local state -""" -function update_message( - bp_cache::BeliefPropagationCache, - edge::PartitionEdge; - message_update=default_message_update, - message_update_kwargs=(;), -) - vertex = src(edge) - messages = environment(bp_cache, vertex; ignore_edges=PartitionEdge[reverse(edge)]) - state = factor(bp_cache, vertex) - - return message_update(ITensor[messages; state]; message_update_kwargs...) -end - -""" -Do a sequential update of the message tensors on `edges` -""" -function update( - bp_cache::BeliefPropagationCache, - edges::Vector{<:PartitionEdge}; - (update_diff!)=nothing, - kwargs..., -) - bp_cache_updated = copy(bp_cache) - mts = messages(bp_cache_updated) - for e in edges - set!(mts, e, update_message(bp_cache_updated, e; kwargs...)) - if !isnothing(update_diff!) - update_diff![] += message_diff(message(bp_cache, e), mts[e]) - end - end - return bp_cache_updated -end - -""" -Update the message tensor on a single edge -""" -function update(bp_cache::BeliefPropagationCache, edge::PartitionEdge; kwargs...) - return update(bp_cache, [edge]; kwargs...) -end - -""" -Do parallel updates between groups of edges of all message tensors -Currently we send the full message tensor data struct to update for each edge_group. But really we only need the -mts relevant to that group. -""" -function update( - bp_cache::BeliefPropagationCache, - edge_groups::Vector{<:Vector{<:PartitionEdge}}; - kwargs..., -) - new_mts = copy(messages(bp_cache)) - for edges in edge_groups - bp_cache_t = update(bp_cache, edges; kwargs...) - for e in edges - new_mts[e] = message(bp_cache_t, e) - end - end - return set_messages(bp_cache, new_mts) -end - -""" -More generic interface for update, with default params -""" -function update( - bp_cache::BeliefPropagationCache; - edges=default_edge_sequence(bp_cache), - maxiter=default_bp_maxiter(bp_cache), - tol=nothing, - verbose=false, - kwargs..., -) - compute_error = !isnothing(tol) - if isnothing(maxiter) - error("You need to specify a number of iterations for BP!") - end - for i in 1:maxiter - diff = compute_error ? Ref(0.0) : nothing - bp_cache = update(bp_cache, edges; (update_diff!)=diff, kwargs...) - if compute_error && (diff.x / length(edges)) <= tol - if verbose - println("BP converged to desired precision after $i iterations.") - end - break - end - end - return bp_cache -end - -""" -Update the tensornetwork inside the cache -""" -function update_factors(bp_cache::BeliefPropagationCache, factors) - bp_cache = copy(bp_cache) - tn = tensornetwork(bp_cache) - for vertex in eachindex(factors) - # TODO: Add a check that this preserves the graph structure. - setindex_preserve_graph!(tn, factors[vertex], vertex) - end - return bp_cache -end - -function update_factor(bp_cache, vertex, factor) - return update_factors(bp_cache, Dictionary([vertex], [factor])) -end - function region_scalar( bp_cache::BeliefPropagationCache, pv::PartitionVertex; @@ -291,23 +107,3 @@ function region_scalar( vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))); contract_kwargs... )[] end - -function vertex_scalars( - bp_cache::BeliefPropagationCache, - pvs=partitionvertices(partitioned_tensornetwork(bp_cache)); - kwargs..., -) - return map(pv -> region_scalar(bp_cache, pv; kwargs...), pvs) -end - -function edge_scalars( - bp_cache::BeliefPropagationCache, - pes=partitionedges(partitioned_tensornetwork(bp_cache)); - kwargs..., -) - return map(pe -> region_scalar(bp_cache, pe; kwargs...), pes) -end - -function scalar_factors_quotient(bp_cache::BeliefPropagationCache) - return vertex_scalars(bp_cache), edge_scalars(bp_cache) -end From 81dcea4185d4bf4c3803c589c592cc966237a2f1 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 18:26:22 +0000 Subject: [PATCH 32/47] Revert "AbstractCache" This reverts commit a02842248e5dc123e3c3353375476f9b658d58a7. --- src/ITensorNetworks.jl | 5 +- src/caches/abstractbeliefpropagationcache.jl | 236 ------------------- src/caches/beliefpropagationcache.jl | 220 ++++++++++++++++- 3 files changed, 214 insertions(+), 247 deletions(-) delete mode 100644 src/caches/abstractbeliefpropagationcache.jl diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 4da7f0d3..85126258 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -28,10 +28,9 @@ include("edge_sequences.jl") include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") -include("caches/abstractbeliefpropagationcache.jl") include("caches/beliefpropagationcache.jl") -#include("caches/boundarympscacheutils.jl") -#include("caches/boundarympscache.jl") +include("caches/boundarympscacheutils.jl") +include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl deleted file mode 100644 index 3646faa5..00000000 --- a/src/caches/abstractbeliefpropagationcache.jl +++ /dev/null @@ -1,236 +0,0 @@ -using Graphs: IsDirected -using SplitApplyCombine: group -using LinearAlgebra: diag, dot -using ITensors: dir -using ITensorMPS: ITensorMPS -using NamedGraphs.PartitionedGraphs: - PartitionedGraphs, - PartitionedGraph, - PartitionVertex, - boundary_partitionedges, - partitionvertices, - partitionedges, - unpartitioned_graph -using SimpleTraits: SimpleTraits, Not, @traitfn -using NDTensors: NDTensors - -abstract type AbstractBeliefPropagationCache end - -function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) - sequence = optimal_contraction_sequence(contract_list) - updated_messages = contract(contract_list; sequence, kwargs...) - message_norm = norm(updated_messages) - if normalize && !iszero(message_norm) - updated_messages /= message_norm - end - return ITensor[updated_messages] -end - -#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages -function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) - lhs, rhs = contract(message_a), contract(message_b) - f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) - return 1 - f -end - -default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] -default_messages(ptn::PartitionedGraph) = Dictionary() -@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing -@traitfn function default_bp_maxiter(g::::IsDirected) - return default_bp_maxiter(undirected_graph(underlying_graph(g))) -end -default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) -function default_partitioned_vertices(f::AbstractFormNetwork) - return group(v -> original_state_vertex(f, v), vertices(f)) -end -default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) - -partitioned_tensornetwork(bpc::AbstractBeliefPropagationCache) = not_implemented() -messages(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) = not_implemented() -Base.copy(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_bp_maxiter(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_edge_sequence(bpc::AbstractBeliefPropagationCache) = not_implemented() -environment(bpc::AbstractBeliefPropagationCache, partition_vertices::Vector{<:PartitionVertex}; kwargs...) = not_implemented() -region_scalar(bpc::AbstractBeliefPropagationCache, pv::PartitionVertex; kwargs...) = not_implemented() -region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) = not_implemented() - -function factors(bpc::AbstractBeliefPropagationCache, verts::Vector) - return ITensor[tensornetwork(bpc)[v] for v in verts] -end - -function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) - return factors(bpc, vertices(bpc, vertex)) -end - -function vertex_scalars( - bpc::AbstractBeliefPropagationCache, - pvs=partitionvertices(partitioned_tensornetwork(bpc)); - kwargs..., - ) - return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) -end - -function edge_scalars( - bpc::AbstractBeliefPropagationCache, - pes=partitionedges(partitioned_tensornetwork(bpc)); - kwargs..., - ) - return map(pe -> region_scalar(bpc, pe; kwargs...), pes) -end - -function scalar_factors_quotient(bpc::AbstractBeliefPropagationCache) - return vertex_scalars(bpc), edge_scalars(bpc) -end - -function environment( - bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... - ) - return environment(bpc, [partition_vertex]; kwargs...) -end - -function environment(bpc::AbstractBeliefPropagationCache, verts::Vector) - partition_verts = partitionvertices(bpc, verts) - messages = environment(bpc, partition_verts) - central_tensors = factors(bpc, setdiff(vertices(bpc, partition_verts), verts)) - return vcat(messages, central_tensors) -end - -function tensornetwork(bpc::AbstractBeliefPropagationCache) - return unpartitioned_graph(partitioned_tensornetwork(bpc)) -end - -#Forward from partitioned graph -for f in [ - :(PartitionedGraphs.partitioned_graph), - :(PartitionedGraphs.partitionedge), - :(PartitionedGraphs.partitionvertices), - :(PartitionedGraphs.vertices), - :(PartitionedGraphs.boundary_partitionedges), - :(ITensorMPS.linkinds), - ] - @eval begin - function $f(bpc::AbstractBeliefPropagationCache, args...; kwargs...) - return $f(partitioned_tensornetwork(bpc), args...; kwargs...) - end - end -end - -NDTensors.scalartype(bpc::AbstractBeliefPropagationCache) = scalartype(tensornetwork(bpc)) - -""" -Update the tensornetwork inside the cache -""" -function update_factors(bpc::AbstractBeliefPropagationCache, factors) - bpc = copy(bpc) - tn = tensornetwork(bpc) - for vertex in eachindex(factors) - # TODO: Add a check that this preserves the graph structure. - setindex_preserve_graph!(tn, factors[vertex], vertex) - end - return bpc -end - -function update_factor(bpc, vertex, factor) - return update_factors(bpc, Dictionary([vertex], [factor])) -end - -function message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) - mts = messages(bpc) - return get(() -> default_message(bpc, edge; kwargs...), mts, edge) -end -function messages(bpc::AbstractBeliefPropagationCache, edges; kwargs...) - return map(edge -> message(bpc, edge; kwargs...), edges) -end - -""" -Compute message tensor as product of incoming mts and local state -""" -function update_message( - bpc::AbstractBeliefPropagationCache, - edge::PartitionEdge; - message_update=default_message_update, - message_update_kwargs=(;), -) - vertex = src(edge) - messages = environment(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) - state = factor(bpc, vertex) - - return message_update(ITensor[messages; state]; message_update_kwargs...) -end - -""" -Do a sequential update of the message tensors on `edges` -""" -function update( - bpc::AbstractBeliefPropagationCache, - edges::Vector{<:PartitionEdge}; - (update_diff!)=nothing, - kwargs..., -) - bpc_updated = copy(bpc) - mts = messages(bpc_updated) - for e in edges - set!(mts, e, update_message(bpc, e; kwargs...)) - if !isnothing(update_diff!) - update_diff![] += message_diff(message(bpc, e), mts[e]) - end - end - return bpc_updated -end - - -""" -Update the message tensor on a single edge -""" -function update(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) - return update(bpc, [edge]; kwargs...) -end - -""" -Do parallel updates between groups of edges of all message tensors -Currently we send the full message tensor data struct to update for each edge_group. But really we only need the -mts relevant to that group. -""" -function update( - bpc::AbstractBeliefPropagationCache, - edge_groups::Vector{<:Vector{<:PartitionEdge}}; - kwargs..., -) - new_mts = copy(messages(bpc)) - for edges in edge_groups - bpc_t = update(bpc, edges; kwargs...) - for e in edges - new_mts[e] = message(bpc_t, e) - end - end - return set_messages(bpc, new_mts) -end - -""" -More generic interface for update, with default params -""" -function update( - bpc::AbstractBeliefPropagationCache; - edges=default_edge_sequence(bpc), - maxiter=default_bp_maxiter(bpc), - tol=nothing, - verbose=false, - kwargs..., -) - compute_error = !isnothing(tol) - if isnothing(maxiter) - error("You need to specify a number of iterations for BP!") - end - for i in 1:maxiter - diff = compute_error ? Ref(0.0) : nothing - bpc = update(bpc, edges; (update_diff!)=diff, kwargs...) - if compute_error && (diff.x / length(edges)) <= tol - if verbose - println("BP converged to desired precision after $i iterations.") - end - break - end - end - return bpc -end \ No newline at end of file diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index 22ca093f..d5886ec7 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -14,29 +14,57 @@ using NamedGraphs.PartitionedGraphs: using SimpleTraits: SimpleTraits, Not, @traitfn using NDTensors: NDTensors +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] +default_messages(ptn::PartitionedGraph) = Dictionary() +function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) + sequence = optimal_contraction_sequence(contract_list) + updated_messages = contract(contract_list; sequence, kwargs...) + message_norm = norm(updated_messages) + if normalize && !iszero(message_norm) + updated_messages /= message_norm + end + return ITensor[updated_messages] +end +@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing +@traitfn function default_bp_maxiter(g::::IsDirected) + return default_bp_maxiter(undirected_graph(underlying_graph(g))) +end +default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) +function default_partitioned_vertices(f::AbstractFormNetwork) + return group(v -> original_state_vertex(f, v), vertices(f)) +end +default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) function default_cache_construction_kwargs(alg::Algorithm"bp", ψ::AbstractITensorNetwork) return (; partitioned_vertices=default_partitioned_vertices(ψ)) end -struct BeliefPropagationCache{PTN,MTS} <: AbstractBeliefPropagationCache +#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages +function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) + lhs, rhs = contract(message_a), contract(message_b) + f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) + return 1 - f +end + +struct BeliefPropagationCache{PTN,MTS,DM} partitioned_tensornetwork::PTN messages::MTS + default_message::DM end #Constructors... function BeliefPropagationCache( - ptn::PartitionedGraph; messages=default_messages(ptn) + ptn::PartitionedGraph; messages=default_messages(ptn), default_message=default_message ) - return BeliefPropagationCache(ptn, messages) + return BeliefPropagationCache(ptn, messages, default_message) end -function BeliefPropagationCache(tn::AbstractITensorNetwork, partitioned_vertices; kwargs...) +function BeliefPropagationCache(tn, partitioned_vertices; kwargs...) ptn = PartitionedGraph(tn, partitioned_vertices) return BeliefPropagationCache(ptn; kwargs...) end function BeliefPropagationCache( - tn::AbstractITensorNetwork; partitioned_vertices=default_partitioned_vertices(tn), kwargs... + tn; partitioned_vertices=default_partitioned_vertices(tn), kwargs... ) return BeliefPropagationCache(tn, partitioned_vertices; kwargs...) end @@ -48,20 +76,47 @@ end function partitioned_tensornetwork(bp_cache::BeliefPropagationCache) return bp_cache.partitioned_tensornetwork end - messages(bp_cache::BeliefPropagationCache) = bp_cache.messages +default_message(bp_cache::BeliefPropagationCache) = bp_cache.default_message function tensornetwork(bp_cache::BeliefPropagationCache) return unpartitioned_graph(partitioned_tensornetwork(bp_cache)) end +#Forward from partitioned graph +for f in [ + :(PartitionedGraphs.partitioned_graph), + :(PartitionedGraphs.partitionedge), + :(PartitionedGraphs.partitionvertices), + :(PartitionedGraphs.vertices), + :(PartitionedGraphs.boundary_partitionedges), + :(ITensorMPS.linkinds), +] + @eval begin + function $f(bp_cache::BeliefPropagationCache, args...; kwargs...) + return $f(partitioned_tensornetwork(bp_cache), args...; kwargs...) + end + end +end + +NDTensors.scalartype(bp_cache) = scalartype(tensornetwork(bp_cache)) + function default_message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - return default_message(scalartype(bp_cache), linkinds(bp_cache, edge)) + return default_message(bp_cache)(scalartype(bp_cache), linkinds(bp_cache, edge)) +end + +function message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) + mts = messages(bp_cache) + return get(() -> default_message(bp_cache, edge), mts, edge) +end +function messages(bp_cache::BeliefPropagationCache, edges; kwargs...) + return map(edge -> message(bp_cache, edge; kwargs...), edges) end function Base.copy(bp_cache::BeliefPropagationCache) return BeliefPropagationCache( copy(partitioned_tensornetwork(bp_cache)), copy(messages(bp_cache)), + default_message(bp_cache), ) end @@ -74,7 +129,7 @@ end function set_messages(cache::BeliefPropagationCache, messages) return BeliefPropagationCache( - partitioned_tensornetwork(cache), messages + partitioned_tensornetwork(cache), messages, default_message(cache) ) end @@ -88,6 +143,135 @@ function environment( return reduce(vcat, ms; init=ITensor[]) end +function environment( + bp_cache::BeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... +) + return environment(bp_cache, [partition_vertex]; kwargs...) +end + +function environment(bp_cache::BeliefPropagationCache, verts::Vector) + partition_verts = partitionvertices(bp_cache, verts) + messages = environment(bp_cache, partition_verts) + central_tensors = factors(bp_cache, setdiff(vertices(bp_cache, partition_verts), verts)) + return vcat(messages, central_tensors) +end + +function factors(bp_cache::BeliefPropagationCache, verts::Vector) + return ITensor[tensornetwork(bp_cache)[v] for v in verts] +end + +function factor(bp_cache::BeliefPropagationCache, vertex::PartitionVertex) + return factors(bp_cache, vertices(bp_cache, vertex)) +end + +""" +Compute message tensor as product of incoming mts and local state +""" +function update_message( + bp_cache::BeliefPropagationCache, + edge::PartitionEdge; + message_update=default_message_update, + message_update_kwargs=(;), +) + vertex = src(edge) + messages = environment(bp_cache, vertex; ignore_edges=PartitionEdge[reverse(edge)]) + state = factor(bp_cache, vertex) + + return message_update(ITensor[messages; state]; message_update_kwargs...) +end + +""" +Do a sequential update of the message tensors on `edges` +""" +function update( + bp_cache::BeliefPropagationCache, + edges::Vector{<:PartitionEdge}; + (update_diff!)=nothing, + kwargs..., +) + bp_cache_updated = copy(bp_cache) + mts = messages(bp_cache_updated) + for e in edges + set!(mts, e, update_message(bp_cache_updated, e; kwargs...)) + if !isnothing(update_diff!) + update_diff![] += message_diff(message(bp_cache, e), mts[e]) + end + end + return bp_cache_updated +end + +""" +Update the message tensor on a single edge +""" +function update(bp_cache::BeliefPropagationCache, edge::PartitionEdge; kwargs...) + return update(bp_cache, [edge]; kwargs...) +end + +""" +Do parallel updates between groups of edges of all message tensors +Currently we send the full message tensor data struct to update for each edge_group. But really we only need the +mts relevant to that group. +""" +function update( + bp_cache::BeliefPropagationCache, + edge_groups::Vector{<:Vector{<:PartitionEdge}}; + kwargs..., +) + new_mts = copy(messages(bp_cache)) + for edges in edge_groups + bp_cache_t = update(bp_cache, edges; kwargs...) + for e in edges + new_mts[e] = message(bp_cache_t, e) + end + end + return set_messages(bp_cache, new_mts) +end + +""" +More generic interface for update, with default params +""" +function update( + bp_cache::BeliefPropagationCache; + edges=default_edge_sequence(bp_cache), + maxiter=default_bp_maxiter(bp_cache), + tol=nothing, + verbose=false, + kwargs..., +) + compute_error = !isnothing(tol) + if isnothing(maxiter) + error("You need to specify a number of iterations for BP!") + end + for i in 1:maxiter + diff = compute_error ? Ref(0.0) : nothing + bp_cache = update(bp_cache, edges; (update_diff!)=diff, kwargs...) + if compute_error && (diff.x / length(edges)) <= tol + if verbose + println("BP converged to desired precision after $i iterations.") + end + break + end + end + return bp_cache +end + +""" +Update the tensornetwork inside the cache +""" +function update_factors(bp_cache::BeliefPropagationCache, factors) + bp_cache = copy(bp_cache) + tn = tensornetwork(bp_cache) + for vertex in eachindex(factors) + # TODO: Add a check that this preserves the graph structure. + setindex_preserve_graph!(tn, factors[vertex], vertex) + end + return bp_cache +end + +function update_factor(bp_cache, vertex, factor) + return update_factors(bp_cache, Dictionary([vertex], [factor])) +end + function region_scalar( bp_cache::BeliefPropagationCache, pv::PartitionVertex; @@ -107,3 +291,23 @@ function region_scalar( vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))); contract_kwargs... )[] end + +function vertex_scalars( + bp_cache::BeliefPropagationCache, + pvs=partitionvertices(partitioned_tensornetwork(bp_cache)); + kwargs..., +) + return map(pv -> region_scalar(bp_cache, pv; kwargs...), pvs) +end + +function edge_scalars( + bp_cache::BeliefPropagationCache, + pes=partitionedges(partitioned_tensornetwork(bp_cache)); + kwargs..., +) + return map(pe -> region_scalar(bp_cache, pe; kwargs...), pes) +end + +function scalar_factors_quotient(bp_cache::BeliefPropagationCache) + return vertex_scalars(bp_cache), edge_scalars(bp_cache) +end From 5dc967782cb1a70245036e9b8498a7a62e476bdd Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 18:30:59 +0000 Subject: [PATCH 33/47] Abstract Cahce --- src/ITensorNetworks.jl | 5 +- src/caches/abstractbeliefpropagationcache.jl | 236 +++++++++++++++++++ src/caches/beliefpropagationcache.jl | 220 +---------------- 3 files changed, 247 insertions(+), 214 deletions(-) create mode 100644 src/caches/abstractbeliefpropagationcache.jl diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 85126258..4da7f0d3 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -28,9 +28,10 @@ include("edge_sequences.jl") include("formnetworks/abstractformnetwork.jl") include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") +include("caches/abstractbeliefpropagationcache.jl") include("caches/beliefpropagationcache.jl") -include("caches/boundarympscacheutils.jl") -include("caches/boundarympscache.jl") +#include("caches/boundarympscacheutils.jl") +#include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl new file mode 100644 index 00000000..3646faa5 --- /dev/null +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -0,0 +1,236 @@ +using Graphs: IsDirected +using SplitApplyCombine: group +using LinearAlgebra: diag, dot +using ITensors: dir +using ITensorMPS: ITensorMPS +using NamedGraphs.PartitionedGraphs: + PartitionedGraphs, + PartitionedGraph, + PartitionVertex, + boundary_partitionedges, + partitionvertices, + partitionedges, + unpartitioned_graph +using SimpleTraits: SimpleTraits, Not, @traitfn +using NDTensors: NDTensors + +abstract type AbstractBeliefPropagationCache end + +function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) + sequence = optimal_contraction_sequence(contract_list) + updated_messages = contract(contract_list; sequence, kwargs...) + message_norm = norm(updated_messages) + if normalize && !iszero(message_norm) + updated_messages /= message_norm + end + return ITensor[updated_messages] +end + +#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages +function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) + lhs, rhs = contract(message_a), contract(message_b) + f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) + return 1 - f +end + +default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] +default_messages(ptn::PartitionedGraph) = Dictionary() +@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing +@traitfn function default_bp_maxiter(g::::IsDirected) + return default_bp_maxiter(undirected_graph(underlying_graph(g))) +end +default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) +function default_partitioned_vertices(f::AbstractFormNetwork) + return group(v -> original_state_vertex(f, v), vertices(f)) +end +default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) + +partitioned_tensornetwork(bpc::AbstractBeliefPropagationCache) = not_implemented() +messages(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) = not_implemented() +Base.copy(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_bp_maxiter(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_edge_sequence(bpc::AbstractBeliefPropagationCache) = not_implemented() +environment(bpc::AbstractBeliefPropagationCache, partition_vertices::Vector{<:PartitionVertex}; kwargs...) = not_implemented() +region_scalar(bpc::AbstractBeliefPropagationCache, pv::PartitionVertex; kwargs...) = not_implemented() +region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) = not_implemented() + +function factors(bpc::AbstractBeliefPropagationCache, verts::Vector) + return ITensor[tensornetwork(bpc)[v] for v in verts] +end + +function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) + return factors(bpc, vertices(bpc, vertex)) +end + +function vertex_scalars( + bpc::AbstractBeliefPropagationCache, + pvs=partitionvertices(partitioned_tensornetwork(bpc)); + kwargs..., + ) + return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) +end + +function edge_scalars( + bpc::AbstractBeliefPropagationCache, + pes=partitionedges(partitioned_tensornetwork(bpc)); + kwargs..., + ) + return map(pe -> region_scalar(bpc, pe; kwargs...), pes) +end + +function scalar_factors_quotient(bpc::AbstractBeliefPropagationCache) + return vertex_scalars(bpc), edge_scalars(bpc) +end + +function environment( + bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... + ) + return environment(bpc, [partition_vertex]; kwargs...) +end + +function environment(bpc::AbstractBeliefPropagationCache, verts::Vector) + partition_verts = partitionvertices(bpc, verts) + messages = environment(bpc, partition_verts) + central_tensors = factors(bpc, setdiff(vertices(bpc, partition_verts), verts)) + return vcat(messages, central_tensors) +end + +function tensornetwork(bpc::AbstractBeliefPropagationCache) + return unpartitioned_graph(partitioned_tensornetwork(bpc)) +end + +#Forward from partitioned graph +for f in [ + :(PartitionedGraphs.partitioned_graph), + :(PartitionedGraphs.partitionedge), + :(PartitionedGraphs.partitionvertices), + :(PartitionedGraphs.vertices), + :(PartitionedGraphs.boundary_partitionedges), + :(ITensorMPS.linkinds), + ] + @eval begin + function $f(bpc::AbstractBeliefPropagationCache, args...; kwargs...) + return $f(partitioned_tensornetwork(bpc), args...; kwargs...) + end + end +end + +NDTensors.scalartype(bpc::AbstractBeliefPropagationCache) = scalartype(tensornetwork(bpc)) + +""" +Update the tensornetwork inside the cache +""" +function update_factors(bpc::AbstractBeliefPropagationCache, factors) + bpc = copy(bpc) + tn = tensornetwork(bpc) + for vertex in eachindex(factors) + # TODO: Add a check that this preserves the graph structure. + setindex_preserve_graph!(tn, factors[vertex], vertex) + end + return bpc +end + +function update_factor(bpc, vertex, factor) + return update_factors(bpc, Dictionary([vertex], [factor])) +end + +function message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) + mts = messages(bpc) + return get(() -> default_message(bpc, edge; kwargs...), mts, edge) +end +function messages(bpc::AbstractBeliefPropagationCache, edges; kwargs...) + return map(edge -> message(bpc, edge; kwargs...), edges) +end + +""" +Compute message tensor as product of incoming mts and local state +""" +function update_message( + bpc::AbstractBeliefPropagationCache, + edge::PartitionEdge; + message_update=default_message_update, + message_update_kwargs=(;), +) + vertex = src(edge) + messages = environment(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) + state = factor(bpc, vertex) + + return message_update(ITensor[messages; state]; message_update_kwargs...) +end + +""" +Do a sequential update of the message tensors on `edges` +""" +function update( + bpc::AbstractBeliefPropagationCache, + edges::Vector{<:PartitionEdge}; + (update_diff!)=nothing, + kwargs..., +) + bpc_updated = copy(bpc) + mts = messages(bpc_updated) + for e in edges + set!(mts, e, update_message(bpc, e; kwargs...)) + if !isnothing(update_diff!) + update_diff![] += message_diff(message(bpc, e), mts[e]) + end + end + return bpc_updated +end + + +""" +Update the message tensor on a single edge +""" +function update(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) + return update(bpc, [edge]; kwargs...) +end + +""" +Do parallel updates between groups of edges of all message tensors +Currently we send the full message tensor data struct to update for each edge_group. But really we only need the +mts relevant to that group. +""" +function update( + bpc::AbstractBeliefPropagationCache, + edge_groups::Vector{<:Vector{<:PartitionEdge}}; + kwargs..., +) + new_mts = copy(messages(bpc)) + for edges in edge_groups + bpc_t = update(bpc, edges; kwargs...) + for e in edges + new_mts[e] = message(bpc_t, e) + end + end + return set_messages(bpc, new_mts) +end + +""" +More generic interface for update, with default params +""" +function update( + bpc::AbstractBeliefPropagationCache; + edges=default_edge_sequence(bpc), + maxiter=default_bp_maxiter(bpc), + tol=nothing, + verbose=false, + kwargs..., +) + compute_error = !isnothing(tol) + if isnothing(maxiter) + error("You need to specify a number of iterations for BP!") + end + for i in 1:maxiter + diff = compute_error ? Ref(0.0) : nothing + bpc = update(bpc, edges; (update_diff!)=diff, kwargs...) + if compute_error && (diff.x / length(edges)) <= tol + if verbose + println("BP converged to desired precision after $i iterations.") + end + break + end + end + return bpc +end \ No newline at end of file diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index d5886ec7..22ca093f 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -14,57 +14,29 @@ using NamedGraphs.PartitionedGraphs: using SimpleTraits: SimpleTraits, Not, @traitfn using NDTensors: NDTensors -default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] -default_messages(ptn::PartitionedGraph) = Dictionary() -function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) - sequence = optimal_contraction_sequence(contract_list) - updated_messages = contract(contract_list; sequence, kwargs...) - message_norm = norm(updated_messages) - if normalize && !iszero(message_norm) - updated_messages /= message_norm - end - return ITensor[updated_messages] -end -@traitfn default_bp_maxiter(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing -@traitfn function default_bp_maxiter(g::::IsDirected) - return default_bp_maxiter(undirected_graph(underlying_graph(g))) -end -default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertices(ψ)) -function default_partitioned_vertices(f::AbstractFormNetwork) - return group(v -> original_state_vertex(f, v), vertices(f)) -end -default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) function default_cache_construction_kwargs(alg::Algorithm"bp", ψ::AbstractITensorNetwork) return (; partitioned_vertices=default_partitioned_vertices(ψ)) end -#TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages -function message_diff(message_a::Vector{ITensor}, message_b::Vector{ITensor}) - lhs, rhs = contract(message_a), contract(message_b) - f = abs2(dot(lhs / norm(lhs), rhs / norm(rhs))) - return 1 - f -end - -struct BeliefPropagationCache{PTN,MTS,DM} +struct BeliefPropagationCache{PTN,MTS} <: AbstractBeliefPropagationCache partitioned_tensornetwork::PTN messages::MTS - default_message::DM end #Constructors... function BeliefPropagationCache( - ptn::PartitionedGraph; messages=default_messages(ptn), default_message=default_message + ptn::PartitionedGraph; messages=default_messages(ptn) ) - return BeliefPropagationCache(ptn, messages, default_message) + return BeliefPropagationCache(ptn, messages) end -function BeliefPropagationCache(tn, partitioned_vertices; kwargs...) +function BeliefPropagationCache(tn::AbstractITensorNetwork, partitioned_vertices; kwargs...) ptn = PartitionedGraph(tn, partitioned_vertices) return BeliefPropagationCache(ptn; kwargs...) end function BeliefPropagationCache( - tn; partitioned_vertices=default_partitioned_vertices(tn), kwargs... + tn::AbstractITensorNetwork; partitioned_vertices=default_partitioned_vertices(tn), kwargs... ) return BeliefPropagationCache(tn, partitioned_vertices; kwargs...) end @@ -76,47 +48,20 @@ end function partitioned_tensornetwork(bp_cache::BeliefPropagationCache) return bp_cache.partitioned_tensornetwork end + messages(bp_cache::BeliefPropagationCache) = bp_cache.messages -default_message(bp_cache::BeliefPropagationCache) = bp_cache.default_message function tensornetwork(bp_cache::BeliefPropagationCache) return unpartitioned_graph(partitioned_tensornetwork(bp_cache)) end -#Forward from partitioned graph -for f in [ - :(PartitionedGraphs.partitioned_graph), - :(PartitionedGraphs.partitionedge), - :(PartitionedGraphs.partitionvertices), - :(PartitionedGraphs.vertices), - :(PartitionedGraphs.boundary_partitionedges), - :(ITensorMPS.linkinds), -] - @eval begin - function $f(bp_cache::BeliefPropagationCache, args...; kwargs...) - return $f(partitioned_tensornetwork(bp_cache), args...; kwargs...) - end - end -end - -NDTensors.scalartype(bp_cache) = scalartype(tensornetwork(bp_cache)) - function default_message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - return default_message(bp_cache)(scalartype(bp_cache), linkinds(bp_cache, edge)) -end - -function message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) - mts = messages(bp_cache) - return get(() -> default_message(bp_cache, edge), mts, edge) -end -function messages(bp_cache::BeliefPropagationCache, edges; kwargs...) - return map(edge -> message(bp_cache, edge; kwargs...), edges) + return default_message(scalartype(bp_cache), linkinds(bp_cache, edge)) end function Base.copy(bp_cache::BeliefPropagationCache) return BeliefPropagationCache( copy(partitioned_tensornetwork(bp_cache)), copy(messages(bp_cache)), - default_message(bp_cache), ) end @@ -129,7 +74,7 @@ end function set_messages(cache::BeliefPropagationCache, messages) return BeliefPropagationCache( - partitioned_tensornetwork(cache), messages, default_message(cache) + partitioned_tensornetwork(cache), messages ) end @@ -143,135 +88,6 @@ function environment( return reduce(vcat, ms; init=ITensor[]) end -function environment( - bp_cache::BeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... -) - return environment(bp_cache, [partition_vertex]; kwargs...) -end - -function environment(bp_cache::BeliefPropagationCache, verts::Vector) - partition_verts = partitionvertices(bp_cache, verts) - messages = environment(bp_cache, partition_verts) - central_tensors = factors(bp_cache, setdiff(vertices(bp_cache, partition_verts), verts)) - return vcat(messages, central_tensors) -end - -function factors(bp_cache::BeliefPropagationCache, verts::Vector) - return ITensor[tensornetwork(bp_cache)[v] for v in verts] -end - -function factor(bp_cache::BeliefPropagationCache, vertex::PartitionVertex) - return factors(bp_cache, vertices(bp_cache, vertex)) -end - -""" -Compute message tensor as product of incoming mts and local state -""" -function update_message( - bp_cache::BeliefPropagationCache, - edge::PartitionEdge; - message_update=default_message_update, - message_update_kwargs=(;), -) - vertex = src(edge) - messages = environment(bp_cache, vertex; ignore_edges=PartitionEdge[reverse(edge)]) - state = factor(bp_cache, vertex) - - return message_update(ITensor[messages; state]; message_update_kwargs...) -end - -""" -Do a sequential update of the message tensors on `edges` -""" -function update( - bp_cache::BeliefPropagationCache, - edges::Vector{<:PartitionEdge}; - (update_diff!)=nothing, - kwargs..., -) - bp_cache_updated = copy(bp_cache) - mts = messages(bp_cache_updated) - for e in edges - set!(mts, e, update_message(bp_cache_updated, e; kwargs...)) - if !isnothing(update_diff!) - update_diff![] += message_diff(message(bp_cache, e), mts[e]) - end - end - return bp_cache_updated -end - -""" -Update the message tensor on a single edge -""" -function update(bp_cache::BeliefPropagationCache, edge::PartitionEdge; kwargs...) - return update(bp_cache, [edge]; kwargs...) -end - -""" -Do parallel updates between groups of edges of all message tensors -Currently we send the full message tensor data struct to update for each edge_group. But really we only need the -mts relevant to that group. -""" -function update( - bp_cache::BeliefPropagationCache, - edge_groups::Vector{<:Vector{<:PartitionEdge}}; - kwargs..., -) - new_mts = copy(messages(bp_cache)) - for edges in edge_groups - bp_cache_t = update(bp_cache, edges; kwargs...) - for e in edges - new_mts[e] = message(bp_cache_t, e) - end - end - return set_messages(bp_cache, new_mts) -end - -""" -More generic interface for update, with default params -""" -function update( - bp_cache::BeliefPropagationCache; - edges=default_edge_sequence(bp_cache), - maxiter=default_bp_maxiter(bp_cache), - tol=nothing, - verbose=false, - kwargs..., -) - compute_error = !isnothing(tol) - if isnothing(maxiter) - error("You need to specify a number of iterations for BP!") - end - for i in 1:maxiter - diff = compute_error ? Ref(0.0) : nothing - bp_cache = update(bp_cache, edges; (update_diff!)=diff, kwargs...) - if compute_error && (diff.x / length(edges)) <= tol - if verbose - println("BP converged to desired precision after $i iterations.") - end - break - end - end - return bp_cache -end - -""" -Update the tensornetwork inside the cache -""" -function update_factors(bp_cache::BeliefPropagationCache, factors) - bp_cache = copy(bp_cache) - tn = tensornetwork(bp_cache) - for vertex in eachindex(factors) - # TODO: Add a check that this preserves the graph structure. - setindex_preserve_graph!(tn, factors[vertex], vertex) - end - return bp_cache -end - -function update_factor(bp_cache, vertex, factor) - return update_factors(bp_cache, Dictionary([vertex], [factor])) -end - function region_scalar( bp_cache::BeliefPropagationCache, pv::PartitionVertex; @@ -291,23 +107,3 @@ function region_scalar( vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))); contract_kwargs... )[] end - -function vertex_scalars( - bp_cache::BeliefPropagationCache, - pvs=partitionvertices(partitioned_tensornetwork(bp_cache)); - kwargs..., -) - return map(pv -> region_scalar(bp_cache, pv; kwargs...), pvs) -end - -function edge_scalars( - bp_cache::BeliefPropagationCache, - pes=partitionedges(partitioned_tensornetwork(bp_cache)); - kwargs..., -) - return map(pe -> region_scalar(bp_cache, pe; kwargs...), pes) -end - -function scalar_factors_quotient(bp_cache::BeliefPropagationCache) - return vertex_scalars(bp_cache), edge_scalars(bp_cache) -end From 611bf18cca59cf062743979b4f9eaa8cc35ab8fd Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 2 Jan 2025 19:19:33 +0000 Subject: [PATCH 34/47] Working Abstract Cache --- examples/test_boundarymps.jl | 148 +++++++++++++++++++ src/caches/abstractbeliefpropagationcache.jl | 128 +++++++++------- src/caches/beliefpropagationcache.jl | 33 ++--- src/caches/boundarympscache.jl | 49 +----- src/caches/boundarympscacheutils.jl | 2 +- 5 files changed, 239 insertions(+), 121 deletions(-) create mode 100644 examples/test_boundarymps.jl diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl new file mode 100644 index 00000000..2fd64f32 --- /dev/null +++ b/examples/test_boundarymps.jl @@ -0,0 +1,148 @@ +using ITensorNetworks: + BoundaryMPSCache, + BeliefPropagationCache, + QuadraticFormNetwork, + IndsNetwork, + siteinds, + ttn, + random_tensornetwork, + partitionedges, + messages, + update, + partition_update, + set_messages, + message, + planargraph_partitionedges, + switch_messages, + environment, + VidalITensorNetwork, + ITensorNetwork, + expect, + default_message_update, + contraction_sequence, + insert_linkinds, + partitioned_tensornetwork, + default_message, + biorthogonalize, + pe_above +using OMEinsumContractionOrders +using ITensorNetworks.ITensorsExtensions: map_eigvals +using ITensorNetworks.ModelHamiltonians: ising +using ITensors: + ITensor, + ITensors, + Index, + OpSum, + terms, + sites, + contract, + commonind, + replaceind, + replaceinds, + prime, + dag, + noncommonind, + noncommoninds, + inds +using NamedGraphs: NamedGraphs, AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors +using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph +using NamedGraphs.GraphsExtensions: rem_vertex, add_edges, add_edge +using NamedGraphs.PartitionedGraphs: + PartitionedGraph, + partitioned_graph, + PartitionVertex, + PartitionEdge, + unpartitioned_graph, + partitioned_vertices, + which_partition +using LinearAlgebra: normalize +using Graphs: center + +using Random + +function lieb_lattice_grid(nx::Int64, ny::Int64; periodic=false) + @assert (!periodic && isodd(nx) && isodd(ny)) || (periodic && iseven(nx) && iseven(ny)) + g = named_grid((nx, ny); periodic) + for v in vertices(g) + if iseven(first(v)) && iseven(last(v)) + g = rem_vertex(g, v) + end + end + return g +end + +function named_grid_periodic_x(nxny::Tuple) + nx, ny = nxny + g = named_grid((nx, ny)) + for i in 1:ny + g = add_edge(g, NamedEdge((nx, i) => (1, i))) + end + return g +end + +function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) + s = siteinds(ψ) + ψIψ = QuadraticFormNetwork(ψ) + ψOψ = QuadraticFormNetwork(ψ) + for (op_string, v) in zip(ops, vs) + ψOψ[(v, "operator")] = ITensors.op(op_string, s[v]) + end + numer_seq = contraction_sequence(ψOψ; alg="sa_bipartite") + denom_seq = contraction_sequence(ψIψ; alg="sa_bipartite") + numer, denom = contract(ψOψ; sequence=numer_seq)[], contract(ψIψ; sequence=denom_seq)[] + return numer / denom +end + +function exact_expect(ψ::ITensorNetwork, op_string::String, v) + return exact_expect(ψ, [op_string], [v]) +end + +function make_eigs_real(A::ITensor) + return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) +end + +Random.seed!(1834) +ITensors.disable_warn_order() + +function main() + L = 4 + #g = lieb_lattice_grid(L, L) + g = named_hexagonal_lattice_graph(L, L) + #g = named_grid_periodic_x((L,2)) + #g = named_grid((L, 3)) + vc = first(center(g)) + s = siteinds("S=1/2", g) + ψ = random_tensornetwork(s; link_space=3) + bp_update_kwargs = (; + maxiter=50, tol=1e-14, message_update=ms -> make_eigs_real.(default_message_update(ms)) + ) + + #Run BP first to normalize and put in a stable gauge + #bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + #bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) + ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + ψIψ = update(ψIψ; bp_update_kwargs...) + ψ = VidalITensorNetwork( + ψ; (cache!)=Ref(ψIψ), update_cache=false, cache_update_kwargs=(; maxiter=0) + ) + ψ = ITensorNetwork(ψ) + + fit_kwargs = (; niters=50, tolerance=1e-12) + + ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) + #ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = 4) + rs = [1, 2, 4, 8, 16, 32] + + @show exact_expect(ψ, "Z", vc) + + for r in rs + ψIψ = BoundaryMPSCache(ψIψ_bp; grouping_function=v -> first(v), message_rank=r) + ψIψ = update(ψIψ; mps_fit_kwargs=fit_kwargs) + #ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) + ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence="automatic") + sz = contract([ρ, ITensors.op("Z", s[vc])])[] / contract([ρ, ITensors.op("I", s[vc])])[] + @show sz + end +end + +main() diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index 3646faa5..327db5d6 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -17,13 +17,13 @@ using NDTensors: NDTensors abstract type AbstractBeliefPropagationCache end function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) - sequence = optimal_contraction_sequence(contract_list) - updated_messages = contract(contract_list; sequence, kwargs...) - message_norm = norm(updated_messages) - if normalize && !iszero(message_norm) - updated_messages /= message_norm - end - return ITensor[updated_messages] + sequence = optimal_contraction_sequence(contract_list) + updated_messages = contract(contract_list; sequence, kwargs...) + message_norm = norm(updated_messages) + if normalize && !iszero(message_norm) + updated_messages /= message_norm + end + return ITensor[updated_messages] end #TODO: Take `dot` without precontracting the messages to allow scaling to more complex messages @@ -47,73 +47,90 @@ default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) partitioned_tensornetwork(bpc::AbstractBeliefPropagationCache) = not_implemented() messages(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) = not_implemented() +function default_message( + bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs... +) + return not_implemented() +end Base.copy(bpc::AbstractBeliefPropagationCache) = not_implemented() default_bp_maxiter(bpc::AbstractBeliefPropagationCache) = not_implemented() default_edge_sequence(bpc::AbstractBeliefPropagationCache) = not_implemented() -environment(bpc::AbstractBeliefPropagationCache, partition_vertices::Vector{<:PartitionVertex}; kwargs...) = not_implemented() -region_scalar(bpc::AbstractBeliefPropagationCache, pv::PartitionVertex; kwargs...) = not_implemented() -region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) = not_implemented() +function environment(bpc::AbstractBeliefPropagationCache, verts::Vector; kwargs...) + return not_implemented() +end +function region_scalar(bpc::AbstractBeliefPropagationCache, pv::PartitionVertex; kwargs...) + return not_implemented() +end +function region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) + return not_implemented() +end + +function tensornetwork(bpc::AbstractBeliefPropagationCache) + return unpartitioned_graph(partitioned_tensornetwork(bpc)) +end function factors(bpc::AbstractBeliefPropagationCache, verts::Vector) - return ITensor[tensornetwork(bpc)[v] for v in verts] + return ITensor[tensornetwork(bpc)[v] for v in verts] end - + function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) - return factors(bpc, vertices(bpc, vertex)) + return factors(bpc, vertices(bpc, vertex)) end function vertex_scalars( - bpc::AbstractBeliefPropagationCache, - pvs=partitionvertices(partitioned_tensornetwork(bpc)); - kwargs..., - ) - return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) + bpc::AbstractBeliefPropagationCache, + pvs=partitionvertices(partitioned_tensornetwork(bpc)); + kwargs..., +) + return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) end - + function edge_scalars( - bpc::AbstractBeliefPropagationCache, - pes=partitionedges(partitioned_tensornetwork(bpc)); - kwargs..., - ) - return map(pe -> region_scalar(bpc, pe; kwargs...), pes) + bpc::AbstractBeliefPropagationCache, + pes=partitionedges(partitioned_tensornetwork(bpc)); + kwargs..., +) + return map(pe -> region_scalar(bpc, pe; kwargs...), pes) end - + function scalar_factors_quotient(bpc::AbstractBeliefPropagationCache) - return vertex_scalars(bpc), edge_scalars(bpc) + return vertex_scalars(bpc), edge_scalars(bpc) end -function environment( - bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... - ) - return environment(bpc, [partition_vertex]; kwargs...) +function incoming_messages( + bpc::AbstractBeliefPropagationCache, + partition_vertices::Vector{<:PartitionVertex}; + ignore_edges=(), +) + bpes = boundary_partitionedges(bpc, partition_vertices; dir=:in) + ms = messages(bpc, setdiff(bpes, ignore_edges)) + return reduce(vcat, ms; init=ITensor[]) end - -function environment(bpc::AbstractBeliefPropagationCache, verts::Vector) - partition_verts = partitionvertices(bpc, verts) - messages = environment(bpc, partition_verts) - central_tensors = factors(bpc, setdiff(vertices(bpc, partition_verts), verts)) - return vcat(messages, central_tensors) + +function incoming_messages( + bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex; kwargs... +) + return incoming_messages(bpc, [partition_vertex]; kwargs...) end function tensornetwork(bpc::AbstractBeliefPropagationCache) - return unpartitioned_graph(partitioned_tensornetwork(bpc)) + return unpartitioned_graph(partitioned_tensornetwork(bpc)) end #Forward from partitioned graph for f in [ - :(PartitionedGraphs.partitioned_graph), - :(PartitionedGraphs.partitionedge), - :(PartitionedGraphs.partitionvertices), - :(PartitionedGraphs.vertices), - :(PartitionedGraphs.boundary_partitionedges), - :(ITensorMPS.linkinds), - ] - @eval begin - function $f(bpc::AbstractBeliefPropagationCache, args...; kwargs...) - return $f(partitioned_tensornetwork(bpc), args...; kwargs...) - end + :(PartitionedGraphs.partitioned_graph), + :(PartitionedGraphs.partitionedge), + :(PartitionedGraphs.partitionvertices), + :(PartitionedGraphs.vertices), + :(PartitionedGraphs.boundary_partitionedges), + :(ITensorMPS.linkinds), +] + @eval begin + function $f(bpc::AbstractBeliefPropagationCache, args...; kwargs...) + return $f(partitioned_tensornetwork(bpc), args...; kwargs...) end + end end NDTensors.scalartype(bpc::AbstractBeliefPropagationCache) = scalartype(tensornetwork(bpc)) @@ -136,11 +153,11 @@ function update_factor(bpc, vertex, factor) end function message(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) - mts = messages(bpc) - return get(() -> default_message(bpc, edge; kwargs...), mts, edge) + mts = messages(bpc) + return get(() -> default_message(bpc, edge; kwargs...), mts, edge) end function messages(bpc::AbstractBeliefPropagationCache, edges; kwargs...) - return map(edge -> message(bpc, edge; kwargs...), edges) + return map(edge -> message(bpc, edge; kwargs...), edges) end """ @@ -153,7 +170,7 @@ function update_message( message_update_kwargs=(;), ) vertex = src(edge) - messages = environment(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) + messages = incoming_messages(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) state = factor(bpc, vertex) return message_update(ITensor[messages; state]; message_update_kwargs...) @@ -171,7 +188,7 @@ function update( bpc_updated = copy(bpc) mts = messages(bpc_updated) for e in edges - set!(mts, e, update_message(bpc, e; kwargs...)) + set!(mts, e, update_message(bpc_updated, e; kwargs...)) if !isnothing(update_diff!) update_diff![] += message_diff(message(bpc, e), mts[e]) end @@ -179,7 +196,6 @@ function update( return bpc_updated end - """ Update the message tensor on a single edge """ @@ -233,4 +249,4 @@ function update( end end return bpc -end \ No newline at end of file +end diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index 22ca093f..0ba084d2 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -24,9 +24,7 @@ struct BeliefPropagationCache{PTN,MTS} <: AbstractBeliefPropagationCache end #Constructors... -function BeliefPropagationCache( - ptn::PartitionedGraph; messages=default_messages(ptn) -) +function BeliefPropagationCache(ptn::PartitionedGraph; messages=default_messages(ptn)) return BeliefPropagationCache(ptn, messages) end @@ -36,7 +34,9 @@ function BeliefPropagationCache(tn::AbstractITensorNetwork, partitioned_vertices end function BeliefPropagationCache( - tn::AbstractITensorNetwork; partitioned_vertices=default_partitioned_vertices(tn), kwargs... + tn::AbstractITensorNetwork; + partitioned_vertices=default_partitioned_vertices(tn), + kwargs..., ) return BeliefPropagationCache(tn, partitioned_vertices; kwargs...) end @@ -50,9 +50,6 @@ function partitioned_tensornetwork(bp_cache::BeliefPropagationCache) end messages(bp_cache::BeliefPropagationCache) = bp_cache.messages -function tensornetwork(bp_cache::BeliefPropagationCache) - return unpartitioned_graph(partitioned_tensornetwork(bp_cache)) -end function default_message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) return default_message(scalartype(bp_cache), linkinds(bp_cache, edge)) @@ -60,8 +57,7 @@ end function Base.copy(bp_cache::BeliefPropagationCache) return BeliefPropagationCache( - copy(partitioned_tensornetwork(bp_cache)), - copy(messages(bp_cache)), + copy(partitioned_tensornetwork(bp_cache)), copy(messages(bp_cache)) ) end @@ -73,19 +69,14 @@ function default_edge_sequence(bp_cache::BeliefPropagationCache) end function set_messages(cache::BeliefPropagationCache, messages) - return BeliefPropagationCache( - partitioned_tensornetwork(cache), messages - ) + return BeliefPropagationCache(partitioned_tensornetwork(cache), messages) end -function environment( - bp_cache::BeliefPropagationCache, - partition_vertices::Vector{<:PartitionVertex}; - ignore_edges=(), -) - bpes = boundary_partitionedges(bp_cache, partition_vertices; dir=:in) - ms = messages(bp_cache, setdiff(bpes, ignore_edges)) - return reduce(vcat, ms; init=ITensor[]) +function environment(bpc::BeliefPropagationCache, verts::Vector; kwargs...) + partition_verts = partitionvertices(bpc, verts) + messages = incoming_messages(bpc, partition_verts; kwargs...) + central_tensors = factors(bpc, setdiff(vertices(bpc, partition_verts), verts)) + return vcat(messages, central_tensors) end function region_scalar( @@ -93,7 +84,7 @@ function region_scalar( pv::PartitionVertex; contract_kwargs=(; sequence="automatic"), ) - incoming_mts = environment(bp_cache, [pv]) + incoming_mts = incoming_messages(bp_cache, [pv]) local_state = factor(bp_cache, pv) return contract(vcat(incoming_mts, local_state); contract_kwargs...)[] end diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index f0cee27c..f21165bd 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -9,7 +9,7 @@ using SplitApplyCombine: group using ITensors: commoninds, random_itensor using LinearAlgebra: pinv -struct BoundaryMPSCache{BPC,PG} +struct BoundaryMPSCache{BPC,PG} <: AbstractBeliefPropagationCache bp_cache::BPC partitionedplanargraph::PG end @@ -18,10 +18,14 @@ bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph ppg(bmpsc) = partitionedplanargraph(bmpsc) planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) -tensornetwork(bmpsc::BoundaryMPSCache) = tensornetwork(bp_cache(bmpsc)) + function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) return partitioned_tensornetwork(bp_cache(bmpsc)) end +messages(bmpsc::BoundaryMPSCache) = messages(bp_cache(bmpsc)) +function default_message(bpc::AbstractBeliefPropagationCache, args...; kwargs...) + return default_message(bp_cache(bmpsc), args...; kwargs...) +end default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) function default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) @@ -457,44 +461,3 @@ function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwa bmpsc = partition_update(bmpsc, pv) return environment(bp_cache(bmpsc), verts; kwargs...) end - -function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, vertex; kwargs...) - return environment(bmpsc, [vertex]; kwargs...) -end - -#Forward onto beliefpropagationcache -for f in [ - :messages, - :message, - :update_message, - :(ITensorNetworks.linkinds), - :default_edge_sequence, - :factor, - :factors, -] - @eval begin - function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) - return $f(bp_cache(bmpsc), args...; kwargs...) - end - end -end - -#Wrap around beliefpropagationcache -for f in [:update_factors, :update_factor] - @eval begin - function $f(bmpsc::BoundaryMPSCache, args...; kwargs...) - bmpsc = copy(bmpsc) - bpc = bp_cache(bmpsc) - bpc = $f(bpc, args...; kwargs...) - return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) - end - end -end - -#Wrap around beliefpropagationcache but only for specific argument -function update(bmpsc::BoundaryMPSCache, pes::Vector{<:PartitionEdge}; kwargs...) - bmpsc = copy(bmpsc) - bpc = bp_cache(bmpsc) - bpc = update(bpc, pes; kwargs...) - return BoundaryMPSCache(bpc, partitionedplanargraph(bmpsc)) -end diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index f64aa9cd..2c6758f5 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -13,7 +13,7 @@ end #Add partition edges that may not have meaning in the underlying graph function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) - return BeliefPropagationCache(pg, messages(bpc), default_message(bpc)) + return BeliefPropagationCache(pg, messages(bpc)) end #Add partition edges necessary to connect up all vertices in a partition in the planar graph created by the sort function From 6c7a2a816ff2d9ad3309cf99ff048e500d33e222 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 3 Jan 2025 12:57:15 +0000 Subject: [PATCH 35/47] Simplify initialization of messges --- src/ITensorNetworks.jl | 4 +- src/caches/abstractbeliefpropagationcache.jl | 10 +- src/caches/boundarympscache.jl | 101 ++++++++++--------- 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 4da7f0d3..173f8cc4 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -30,8 +30,8 @@ include("formnetworks/bilinearformnetwork.jl") include("formnetworks/quadraticformnetwork.jl") include("caches/abstractbeliefpropagationcache.jl") include("caches/beliefpropagationcache.jl") -#include("caches/boundarympscacheutils.jl") -#include("caches/boundarympscache.jl") +include("caches/boundarympscacheutils.jl") +include("caches/boundarympscache.jl") include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index 327db5d6..51abb32d 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -113,10 +113,6 @@ function incoming_messages( return incoming_messages(bpc, [partition_vertex]; kwargs...) end -function tensornetwork(bpc::AbstractBeliefPropagationCache) - return unpartitioned_graph(partitioned_tensornetwork(bpc)) -end - #Forward from partitioned graph for f in [ :(PartitionedGraphs.partitioned_graph), @@ -159,6 +155,12 @@ end function messages(bpc::AbstractBeliefPropagationCache, edges; kwargs...) return map(edge -> message(bpc, edge; kwargs...), edges) end +function set_message(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge, message) + bpc = copy(bpc) + ms = messages(bpc) + set!(ms, pe, message) + return bpc +end """ Compute message tensor as product of incoming mts and local state diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index f21165bd..8b30474f 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -12,10 +12,12 @@ using LinearAlgebra: pinv struct BoundaryMPSCache{BPC,PG} <: AbstractBeliefPropagationCache bp_cache::BPC partitionedplanargraph::PG + maximum_virtual_dimension::Int64 end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph +maximum_virtual_dimension(bmpsc::BoundaryMPSCache) = bmpsc.maximum_virtual_dimension ppg(bmpsc) = partitionedplanargraph(bmpsc) planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) @@ -23,9 +25,6 @@ function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) return partitioned_tensornetwork(bp_cache(bmpsc)) end messages(bmpsc::BoundaryMPSCache) = messages(bp_cache(bmpsc)) -function default_message(bpc::AbstractBeliefPropagationCache, args...; kwargs...) - return default_message(bp_cache(bmpsc), args...; kwargs...) -end default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) function default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) @@ -38,7 +37,39 @@ end default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) function Base.copy(bmpsc::BoundaryMPSCache) - return BoundaryMPSCache(copy(bp_cache(bmpsc)), copy(ppg(bmpsc))) + return BoundaryMPSCache( + copy(bp_cache(bmpsc)), copy(ppg(bmpsc)), maximum_virtual_dimension(bmpsc) + ) +end + +function default_message(bmpsc::BoundaryMPSCache, args...; kwargs...) + return default_message(bp_cache(bmpsc), args...; kwargs...) +end + +function virtual_index_dimension( + bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge +) + pes = planargraph_partitionpair_partitionedges( + bmpsc, planargraph_partitionpair(bmpsc, pe1) + ) + if findfirst(x -> x == pe1, pes) > findfirst(x -> x == pe2, pes) + inds_above = reduce( + vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pe2)] + ) + inds_below = reduce( + vcat, [linkinds(bmpsc, pe) for pe in partitionedges_below(bmpsc, pe1)] + ) + else + inds_above = reduce( + vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pe1)] + ) + inds_below = reduce( + vcat, [linkinds(bmpsc, pe) for pe in partitionedges_below(bmpsc, pe2)] + ) + end + return minimum(( + prod(dim.(inds_above)), prod(dim.(inds_below)), maximum_virtual_dimension(bmpsc) + )) end function planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) @@ -63,8 +94,8 @@ function BoundaryMPSCache( planar_graph = partitioned_graph(bpc) vertex_groups = group(grouping_function, collect(vertices(planar_graph))) ppg = PartitionedGraph(planar_graph, vertex_groups) - bmpsc = BoundaryMPSCache(bpc, ppg) - return set_interpartition_messages(bmpsc, message_rank) + bmpsc = BoundaryMPSCache(bpc, ppg, message_rank) + return set_interpartition_messages(bmpsc) end function BoundaryMPSCache(tn::AbstractITensorNetwork, args...; kwargs...) @@ -143,60 +174,32 @@ function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) ) end -function set_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge, m::Vector{ITensor}) - bmpsc = copy(bmpsc) - ms = messages(bmpsc) - set!(ms, pe, m) - return bmpsc -end - -#Initialise all the message tensors for the pair of neighboring partitions, with virtual rank given by message rank +#Initialise all the message tensors for the pairs of neighboring partitions, with virtual rank given by message rank function set_interpartition_messages( - bmpsc::BoundaryMPSCache, partitionpair::Pair, message_rank::Int64 + bmpsc::BoundaryMPSCache, partitionpairs::Vector{<:Pair} ) bmpsc = copy(bmpsc) ms = messages(bmpsc) - pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) - prev_virtual_ind = nothing - maximum_virtual_dim_from_below = 1 - for (i, pg_pe) in enumerate(pes) - siteinds = linkinds(bmpsc, pg_pe) - maximum_virtual_dim_from_below *= prod(dim.(siteinds)) - maximum_virtual_dim_from_above = if i != length(pes) - prod( - dim.( - reduce(vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pg_pe)]) - ), - ) - else - 1 + for partitionpair in partitionpairs + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + for pe in pes + set!(ms, pe, ITensor[dense(delta(linkinds(bmpsc, pe)))]) end - virtualind_dim = minimum([ - message_rank, maximum_virtual_dim_from_above, maximum_virtual_dim_from_below - ]) - next_virtual_index = i != length(pes) ? Index(virtualind_dim, "m$(i)$(i+1)") : nothing - me = denseblocks(delta(siteinds)) - virt_inds = filter(x -> !isnothing(x), [prev_virtual_ind, next_virtual_index]) - if !isempty(virt_inds) - me *= delta(virt_inds) + for i in 1:(length(pes) - 1) + virt_dim = virtual_index_dimension(bmpsc, pes[i], pes[i + 1]) + ind = Index(virt_dim, "m$(i)$(i+1)") + m1, m2 = only(ms[pes[i]]), only(ms[pes[i + 1]]) + set!(ms, pes[i], ITensor[m1 * delta(ind)]) + set!(ms, pes[i + 1], ITensor[m2 * delta(ind)]) end - set!(ms, pg_pe, ITensor[me]) - prev_virtual_ind = next_virtual_index end - return bmpsc end #Initialise all the interpartition message tensors with virtual rank given by message rank -function set_interpartition_messages(bmpsc::BoundaryMPSCache, message_rank::Int64=1) - bmpsc = copy(bmpsc) - pes = partitionedges(ppg(bmpsc)) - for pe in vcat(pes, reverse(reverse.(pes))) - bmpsc = set_interpartition_messages( - bmpsc, parent(src(pe)) => parent(dst(pe)), message_rank - ) - end - return bmpsc +function set_interpartition_messages(bmpsc::BoundaryMPSCache) + partitionpairs = pair.(partitionedges(ppg(bmpsc))) + return set_interpartition_messages(bmpsc, vcat(partitionpairs, reverse.(partitionpairs))) end #Switch the message on partition edge pe with its reverse (and dagger them) From 531c5af8a1708b637eacf4fcb28632736fcba12a Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 3 Jan 2025 12:58:06 +0000 Subject: [PATCH 36/47] Rm redundant file --- examples/test_boundarymps.jl | 148 ----------------------------------- 1 file changed, 148 deletions(-) delete mode 100644 examples/test_boundarymps.jl diff --git a/examples/test_boundarymps.jl b/examples/test_boundarymps.jl deleted file mode 100644 index 2fd64f32..00000000 --- a/examples/test_boundarymps.jl +++ /dev/null @@ -1,148 +0,0 @@ -using ITensorNetworks: - BoundaryMPSCache, - BeliefPropagationCache, - QuadraticFormNetwork, - IndsNetwork, - siteinds, - ttn, - random_tensornetwork, - partitionedges, - messages, - update, - partition_update, - set_messages, - message, - planargraph_partitionedges, - switch_messages, - environment, - VidalITensorNetwork, - ITensorNetwork, - expect, - default_message_update, - contraction_sequence, - insert_linkinds, - partitioned_tensornetwork, - default_message, - biorthogonalize, - pe_above -using OMEinsumContractionOrders -using ITensorNetworks.ITensorsExtensions: map_eigvals -using ITensorNetworks.ModelHamiltonians: ising -using ITensors: - ITensor, - ITensors, - Index, - OpSum, - terms, - sites, - contract, - commonind, - replaceind, - replaceinds, - prime, - dag, - noncommonind, - noncommoninds, - inds -using NamedGraphs: NamedGraphs, AbstractGraph, NamedEdge, NamedGraph, vertices, neighbors -using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph -using NamedGraphs.GraphsExtensions: rem_vertex, add_edges, add_edge -using NamedGraphs.PartitionedGraphs: - PartitionedGraph, - partitioned_graph, - PartitionVertex, - PartitionEdge, - unpartitioned_graph, - partitioned_vertices, - which_partition -using LinearAlgebra: normalize -using Graphs: center - -using Random - -function lieb_lattice_grid(nx::Int64, ny::Int64; periodic=false) - @assert (!periodic && isodd(nx) && isodd(ny)) || (periodic && iseven(nx) && iseven(ny)) - g = named_grid((nx, ny); periodic) - for v in vertices(g) - if iseven(first(v)) && iseven(last(v)) - g = rem_vertex(g, v) - end - end - return g -end - -function named_grid_periodic_x(nxny::Tuple) - nx, ny = nxny - g = named_grid((nx, ny)) - for i in 1:ny - g = add_edge(g, NamedEdge((nx, i) => (1, i))) - end - return g -end - -function exact_expect(ψ::ITensorNetwork, ops::Vector{<:String}, vs::Vector) - s = siteinds(ψ) - ψIψ = QuadraticFormNetwork(ψ) - ψOψ = QuadraticFormNetwork(ψ) - for (op_string, v) in zip(ops, vs) - ψOψ[(v, "operator")] = ITensors.op(op_string, s[v]) - end - numer_seq = contraction_sequence(ψOψ; alg="sa_bipartite") - denom_seq = contraction_sequence(ψIψ; alg="sa_bipartite") - numer, denom = contract(ψOψ; sequence=numer_seq)[], contract(ψIψ; sequence=denom_seq)[] - return numer / denom -end - -function exact_expect(ψ::ITensorNetwork, op_string::String, v) - return exact_expect(ψ, [op_string], [v]) -end - -function make_eigs_real(A::ITensor) - return map_eigvals(x -> real(x), A, first(inds(A)), last(inds(A)); ishermitian=true) -end - -Random.seed!(1834) -ITensors.disable_warn_order() - -function main() - L = 4 - #g = lieb_lattice_grid(L, L) - g = named_hexagonal_lattice_graph(L, L) - #g = named_grid_periodic_x((L,2)) - #g = named_grid((L, 3)) - vc = first(center(g)) - s = siteinds("S=1/2", g) - ψ = random_tensornetwork(s; link_space=3) - bp_update_kwargs = (; - maxiter=50, tol=1e-14, message_update=ms -> make_eigs_real.(default_message_update(ms)) - ) - - #Run BP first to normalize and put in a stable gauge - #bpc = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - #bpc = add_edges(bpc, PartitionEdge.(missing_edges(ψ))) - ψIψ = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - ψIψ = update(ψIψ; bp_update_kwargs...) - ψ = VidalITensorNetwork( - ψ; (cache!)=Ref(ψIψ), update_cache=false, cache_update_kwargs=(; maxiter=0) - ) - ψ = ITensorNetwork(ψ) - - fit_kwargs = (; niters=50, tolerance=1e-12) - - ψIψ_bp = BeliefPropagationCache(QuadraticFormNetwork(ψ)) - #ψIψ = BoundaryMPSCache(ψIψ_bp; sort_f = v -> first(v), message_rank = 4) - rs = [1, 2, 4, 8, 16, 32] - - @show exact_expect(ψ, "Z", vc) - - for r in rs - ψIψ = BoundaryMPSCache(ψIψ_bp; grouping_function=v -> first(v), message_rank=r) - ψIψ = update(ψIψ; mps_fit_kwargs=fit_kwargs) - #ψIψ = update(ψIψ; alg = "biorthogonal", maxiter =50) - ρ = contract(environment(ψIψ, [(vc, "operator")]); sequence="automatic") - sz = contract([ρ, ITensors.op("Z", s[vc])])[] / contract([ρ, ITensors.op("I", s[vc])])[] - @show sz - end -end - -main() From b11c2f8c4b1f112b783fbdab0808fc7f941e166e Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 3 Jan 2025 17:00:53 +0000 Subject: [PATCH 37/47] Bug fix in test_apply --- test/test_apply.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_apply.jl b/test/test_apply.jl index 18d4f590..0bbc580a 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -30,7 +30,7 @@ using Test: @test, @testset #Simple Belief Propagation Grouping bp_cache = BeliefPropagationCache(ψψ, group(v -> v[1], vertices(ψψ))) bp_cache = update(bp_cache; maxiter=20) - envsSBP = environment(bp_cache, PartitionVertex.([v1, v2])) + envsSBP = environment(bp_cache, [(v1, "bra"), (v1, "ket"), (v2, "bra"), (v2, "ket")]) ψv = VidalITensorNetwork(ψ) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) bp_cache = BeliefPropagationCache(ψψ, group(v -> v[1][1], vertices(ψψ))) From 99b49299b6559e9f4f4416bd1f22b2f6157a9eaa Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 8 Jan 2025 15:56:56 -0500 Subject: [PATCH 38/47] Generic update interface for boundary mps and simple bp --- src/caches/abstractbeliefpropagationcache.jl | 83 +++++++++++++----- src/caches/beliefpropagationcache.jl | 11 ++- src/caches/boundarympscache.jl | 90 +++++++------------- test/test_belief_propagation.jl | 4 +- test/test_boundarymps.jl | 12 +-- 5 files changed, 111 insertions(+), 89 deletions(-) diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index 51abb32d..36700fcf 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -52,9 +52,15 @@ function default_message( ) return not_implemented() end +default_message_update_alg(bpc::AbstractBeliefPropagationCache) = not_implemented() Base.copy(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_bp_maxiter(bpc::AbstractBeliefPropagationCache) = not_implemented() -default_edge_sequence(bpc::AbstractBeliefPropagationCache) = not_implemented() +default_bp_maxiter(alg::Algorithm, bpc::AbstractBeliefPropagationCache) = not_implemented() +function default_edge_sequence(alg::Algorithm, bpc::AbstractBeliefPropagationCache) + return not_implemented() +end +function default_message_update_kwargs(alg::Algorithm, bpc::AbstractBeliefPropagationCache) + return not_implemented() +end function environment(bpc::AbstractBeliefPropagationCache, verts::Vector; kwargs...) return not_implemented() end @@ -65,6 +71,22 @@ function region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; k return not_implemented() end +function default_edge_sequence( + bpc::AbstractBeliefPropagationCache; alg=default_message_update_alg(bpc) +) + return default_edge_sequence(Algorithm(alg), bpc) +end +function default_bp_maxiter( + bpc::AbstractBeliefPropagationCache; alg=default_message_update_alg(bpc) +) + return default_bp_maxiter(Algorithm(alg), bpc) +end +function default_message_update_kwargs( + bpc::AbstractBeliefPropagationCache; alg=default_message_update_alg(bpc) +) + return default_message_update_kwargs(Algorithm(alg), bpc) +end + function tensornetwork(bpc::AbstractBeliefPropagationCache) return unpartitioned_graph(partitioned_tensornetwork(bpc)) end @@ -165,44 +187,49 @@ end """ Compute message tensor as product of incoming mts and local state """ -function update_message( +function updated_message( bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; message_update=default_message_update, message_update_kwargs=(;), ) vertex = src(edge) - messages = incoming_messages(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) + incoming_ms = incoming_messages(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) state = factor(bpc, vertex) - return message_update(ITensor[messages; state]; message_update_kwargs...) + return message_update(ITensor[incoming_ms; state]; message_update_kwargs...) +end + +function update( + alg::Algorithm"SimpleBP", + bpc::AbstractBeliefPropagationCache, + edge::PartitionEdge; + kwargs..., +) + new_m = updated_message(bpc, edge; kwargs...) + bpc = set_message(bpc, edge, new_m) + return bpc end """ Do a sequential update of the message tensors on `edges` """ function update( + alg::Algorithm, bpc::AbstractBeliefPropagationCache, - edges::Vector{<:PartitionEdge}; + edges::Vector; (update_diff!)=nothing, kwargs..., ) - bpc_updated = copy(bpc) - mts = messages(bpc_updated) + bpc = copy(bpc) for e in edges - set!(mts, e, update_message(bpc_updated, e; kwargs...)) + prev_message = !isnothing(update_diff!) ? message(bpc, e) : nothing + bpc = update(alg, bpc, e; kwargs...) if !isnothing(update_diff!) - update_diff![] += message_diff(message(bpc, e), mts[e]) + update_diff![] += message_diff(message(bpc, e), prev_message) end end - return bpc_updated -end - -""" -Update the message tensor on a single edge -""" -function update(bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs...) - return update(bpc, [edge]; kwargs...) + return bpc end """ @@ -211,13 +238,14 @@ Currently we send the full message tensor data struct to update for each edge_gr mts relevant to that group. """ function update( + alg::Algorithm, bpc::AbstractBeliefPropagationCache, edge_groups::Vector{<:Vector{<:PartitionEdge}}; kwargs..., ) new_mts = copy(messages(bpc)) for edges in edge_groups - bpc_t = update(bpc, edges; kwargs...) + bpc_t = update(alg, bpc, edges; kwargs...) for e in edges new_mts[e] = message(bpc_t, e) end @@ -229,12 +257,13 @@ end More generic interface for update, with default params """ function update( + alg::Algorithm, bpc::AbstractBeliefPropagationCache; - edges=default_edge_sequence(bpc), - maxiter=default_bp_maxiter(bpc), + edges=default_edge_sequence(alg, bpc), + maxiter=default_bp_maxiter(alg, bpc), + message_update_kwargs=default_message_update_kwargs(alg, bpc), tol=nothing, verbose=false, - kwargs..., ) compute_error = !isnothing(tol) if isnothing(maxiter) @@ -242,7 +271,7 @@ function update( end for i in 1:maxiter diff = compute_error ? Ref(0.0) : nothing - bpc = update(bpc, edges; (update_diff!)=diff, kwargs...) + bpc = update(alg, bpc, edges; (update_diff!)=diff, message_update_kwargs...) if compute_error && (diff.x / length(edges)) <= tol if verbose println("BP converged to desired precision after $i iterations.") @@ -252,3 +281,11 @@ function update( end return bpc end + +function update( + bpc::AbstractBeliefPropagationCache; + alg::String=default_message_update_alg(bpc), + kwargs..., +) + return update(Algorithm(alg), bpc; kwargs...) +end diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index 0ba084d2..97dd6471 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -61,12 +61,19 @@ function Base.copy(bp_cache::BeliefPropagationCache) ) end -function default_bp_maxiter(bp_cache::BeliefPropagationCache) +default_message_update_alg(bp_cache::BeliefPropagationCache) = "SimpleBP" + +function default_bp_maxiter(alg::Algorithm"SimpleBP", bp_cache::BeliefPropagationCache) return default_bp_maxiter(partitioned_graph(bp_cache)) end -function default_edge_sequence(bp_cache::BeliefPropagationCache) +function default_edge_sequence(alg::Algorithm"SimpleBP", bp_cache::BeliefPropagationCache) return default_edge_sequence(partitioned_tensornetwork(bp_cache)) end +function default_message_update_kwargs( + alg::Algorithm"SimpleBP", bpc::AbstractBeliefPropagationCache +) + return (;) +end function set_messages(cache::BeliefPropagationCache, messages) return BeliefPropagationCache(partitioned_tensornetwork(cache), messages) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 8b30474f..0e7dfbf3 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -26,15 +26,19 @@ function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) end messages(bmpsc::BoundaryMPSCache) = messages(bp_cache(bmpsc)) -default_edge_sequence(bmpsc::BoundaryMPSCache) = pair.(default_edge_sequence(ppg(bmpsc))) +default_message_update_alg(bmpsc::BoundaryMPSCache) = "orthogonal" + function default_bp_maxiter(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) return default_bp_maxiter(partitioned_graph(ppg(bmpsc))) end default_bp_maxiter(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = 50 -function default_mps_fit_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) +function default_edge_sequence(alg::Algorithm, bmpsc::BoundaryMPSCache) + return pair.(default_edge_sequence(ppg(bmpsc))) +end +function default_message_update_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) return (; niters=50, tolerance=1e-10) end -default_mps_fit_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) +default_message_update_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache( @@ -221,23 +225,33 @@ function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) end #Update all messages tensors within a partition -function partition_update(bmpsc::BoundaryMPSCache, partition::Int64; kwargs...) +function partition_update(bmpsc::BoundaryMPSCache, partition::Int64) vs = sort(planargraph_vertices(bmpsc, partition)) - bmpsc = partition_update(bmpsc, first(vs); kwargs...) - bmpsc = partition_update(bmpsc, last(vs); kwargs...) + bmpsc = partition_update(bmpsc, first(vs)) + bmpsc = partition_update(bmpsc, last(vs)) return bmpsc end #Update all messages within a partition along the path from from v1 to v2 -function partition_update(bmpsc::BoundaryMPSCache, v1, v2; kwargs...) - return update(bmpsc, PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); kwargs...) +function partition_update(bmpsc::BoundaryMPSCache, v1, v2) + return update( + Algorithm("SimpleBP"), + bmpsc, + PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); + message_update_kwargs=(; normalize=false), + ) end #Update all message tensors within a partition pointing towards v -function partition_update(bmpsc::BoundaryMPSCache, v; kwargs...) +function partition_update(bmpsc::BoundaryMPSCache, v) pv = planargraph_partition(bmpsc, v) g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) - return update(bmpsc, PartitionEdge.(post_order_dfs_edges(g, v)); kwargs...) + return update( + Algorithm("SimpleBP"), + bmpsc, + PartitionEdge.(post_order_dfs_edges(g, v)); + message_update_kwargs=(; normalize=false), + ) end #Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 @@ -327,7 +341,8 @@ function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) end #Update all the message tensors on an interpartition via an orthogonal fitting procedure -function mps_update( +#TODO: Unify this to one function and make two-site possible +function update( alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; @@ -350,18 +365,11 @@ function mps_update( orthogonalize(bmpsc, reverse(update_pe)) end bmpsc = if !isnothing(prev_v) - partition_update( - bmpsc, - prev_v, - cur_v; - message_update=ms -> default_message_update(ms; normalize=false), - ) + partition_update(bmpsc, prev_v, cur_v) else - partition_update( - bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) - ) + partition_update(bmpsc, cur_v) end - me = update_message( + me = updated_message( bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) ) costfunction += region_scalar(bp_cache(bmpsc), src(update_pe)) / norm(me) @@ -379,7 +387,7 @@ function mps_update( end #Update all the message tensors on an interpartition via a biorthogonal fitting procedure -function mps_update( +function update( alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize=true ) prev_v, prev_pe = nothing, nothing @@ -391,21 +399,14 @@ function mps_update( biorthogonalize(bmpsc, update_pe) end bmpsc = if !isnothing(prev_v) - partition_update( - bmpsc, - prev_v, - cur_v; - message_update=ms -> default_message_update(ms; normalize=false), - ) + partition_update(bmpsc, prev_v, cur_v) else - partition_update( - bmpsc, cur_v; message_update=ms -> default_message_update(ms; normalize=false) - ) + partition_update(bmpsc, cur_v) end me_prev = only(message(bmpsc, update_pe)) me = only( - update_message( + updated_message( bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) ), ) @@ -432,31 +433,6 @@ function mps_update( return bmpsc end -""" -More generic interface for update, with default params -""" -function update( - alg::Algorithm, - bmpsc::BoundaryMPSCache; - partitionpairs=default_edge_sequence(bmpsc), - maxiter=default_bp_maxiter(alg, bmpsc), - mps_fit_kwargs=default_mps_fit_kwargs(alg, bmpsc), -) - if isnothing(maxiter) - error("You need to specify a number of iterations for Boundary MPS!") - end - for i in 1:maxiter - for partitionpair in partitionpairs - bmpsc = mps_update(alg, bmpsc, partitionpair; mps_fit_kwargs...) - end - end - return bmpsc -end - -function update(bmpsc::BoundaryMPSCache; alg::String="orthogonal", kwargs...) - return update(Algorithm(alg), bmpsc; kwargs...) -end - #Assume all vertices live in the same partition for now function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index a151e4d1..040d701f 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -23,7 +23,7 @@ using ITensorNetworks: tensornetwork, update, update_factor, - update_message, + updated_message, message_diff using ITensors: ITensors, ITensor, combiner, dag, inds, inner, op, prime, random_itensor using ITensorNetworks.ModelNetworks: ModelNetworks @@ -51,7 +51,7 @@ using Test: @test, @testset bpc = update(bpc; maxiter=25, tol=eps(real(elt))) #Test messages are converged for pe in partitionedges(partitioned_tensornetwork(bpc)) - @test message_diff(update_message(bpc, pe), message(bpc, pe)) < 10 * eps(real(elt)) + @test message_diff(updated_message(bpc, pe), message(bpc, pe)) < 10 * eps(real(elt)) @test eltype(only(message(bpc, pe))) == elt end #Test updating the underlying tensornetwork in the cache diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 38edc142..430ba1e6 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -29,7 +29,6 @@ using ITensorNetworks: tensornetwork, update, update_factor, - update_message, message_diff using ITensors: ITensors, ITensor, combiner, dag, dim, inds, inner, normalize, op, prime, random_itensor @@ -53,7 +52,6 @@ using LinearAlgebra: norm ) begin ITensors.disable_warn_order() - mps_fit_kwargs = (; niters=50, tolerance=1e-10) rng = StableRNG(1234) #First a comb tree (which is still a planar graph) and a flat tensor network @@ -71,7 +69,7 @@ using LinearAlgebra: norm #Orthogonal Boundary MPS, group by row tn_boundaryMPS = BoundaryMPSCache(tn; grouping_function=v -> last(v), message_rank=1) - tn_boundaryMPS = update(tn_boundaryMPS; mps_fit_kwargs) + tn_boundaryMPS = update(tn_boundaryMPS) ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) @@ -121,15 +119,19 @@ using LinearAlgebra: norm end message_update_f = ms -> make_posdef.(default_message_update(ms)) ψ_vidal = VidalITensorNetwork( - ψ; cache_update_kwargs=(; message_update=message_update_f, maxiter=50, tol=1e-14) + ψ; + cache_update_kwargs=(; + maxiter=50, tol=1e-14, message_update_kwargs=(; message_update=message_update_f) + ), ) cache_ref = Ref{BeliefPropagationCache}() ψ_symm = ITensorNetwork(ψ_vidal; (cache!)=cache_ref) bp_cache = cache_ref[] #Do Orthogonal Boundary MPS + message_update_kwargs = (; niters=25, tolerance=1e-10) ψIψ_boundaryMPS = BoundaryMPSCache(QuadraticFormNetwork(ψ_symm); message_rank=1) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; mps_fit_kwargs) + ψIψ_boundaryMPS = update(ψIψ_boundaryMPS; message_update_kwargs) for pe in keys(messages(bp_cache)) m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) From 19630aa6109f1fad983299b6feac0027d093892b Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 9 Jan 2025 16:44:30 -0500 Subject: [PATCH 39/47] Unify update function --- src/caches/boundarympscache.jl | 232 ++++++++++++++++++++------------- 1 file changed, 138 insertions(+), 94 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 0e7dfbf3..c4ab049e 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -232,31 +232,28 @@ function partition_update(bmpsc::BoundaryMPSCache, partition::Int64) return bmpsc end -#Update all messages within a partition along the path from from v1 to v2 -function partition_update(bmpsc::BoundaryMPSCache, v1, v2) - return update( - Algorithm("SimpleBP"), - bmpsc, - PartitionEdge.(a_star(ppg(bmpsc), v1, v2)); - message_update_kwargs=(; normalize=false), - ) +function partition_update_sequence(bmpsc::BoundaryMPSCache, v1, v2) + return PartitionEdge.(a_star(ppg(bmpsc), v1, v2)) end - -#Update all message tensors within a partition pointing towards v -function partition_update(bmpsc::BoundaryMPSCache, v) +function partition_update_sequence(bmpsc::BoundaryMPSCache, v) pv = planargraph_partition(bmpsc, v) g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) + return PartitionEdge.(post_order_dfs_edges(g, v)) +end + +#Update all messages within a partition along the path from from v1 to v2 +function partition_update(bmpsc::BoundaryMPSCache, args...) return update( Algorithm("SimpleBP"), bmpsc, - PartitionEdge.(post_order_dfs_edges(g, v)); + partition_update_sequence(bmpsc, args...); message_update_kwargs=(; normalize=false), ) end #Move the orthogonality centre one step on an interpartition from the message tensor on pe1 to that on pe2 function gauge_step( - alg::Algorithm"orthogonalize", + alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge; @@ -276,7 +273,7 @@ end #Move the biorthogonality centre one step on an interpartition from the partition edge pe1 (and its reverse) to that on pe2 function gauge_step( - alg::Algorithm"biorthogonalize", + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge, @@ -323,114 +320,161 @@ function gauge_walk(alg::Algorithm, bmpsc::BoundaryMPSCache, seq::Vector; kwargs return bmpsc end +function gauge(alg::Algorithm, bmpsc::BoundaryMPSCache, args...; kwargs...) + return gauge_walk(alg, bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs...) +end + #Move the orthogonality centre on an interpartition to the message tensor on pe or between two pes function ITensorMPS.orthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) - return gauge_walk( - Algorithm("orthogonalize"), bmpsc, mps_gauge_update_sequence(bmpsc, args...); kwargs... - ) + return gauge(Algorithm("orthogonal"), bmpsc, args...; kwargs...) end #Move the biorthogonality centre on an interpartition to the message tensor or between two pes function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) - return gauge_walk( - Algorithm("biorthogonalize"), - bmpsc, - mps_gauge_update_sequence(bmpsc, args...); - kwargs..., - ) + return gauge(Algorithm("biorthogonal"), bmpsc, args...; kwargs...) end -#Update all the message tensors on an interpartition via an orthogonal fitting procedure -#TODO: Unify this to one function and make two-site possible -function update( +function default_inserter( alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, + pe::PartitionEdge, + me::Vector{ITensor}, +) + return set_message(bmpsc, reverse(pe), dag.(me)) +end + +function default_inserter( + alg::Algorithm"biorthogonal", + bmpsc::BoundaryMPSCache, + pe::PartitionEdge, + me::Vector{ITensor}, +) + p_above, p_below = partitionedge_above(bmpsc, pe), partitionedge_below(bmpsc, pe) + me = only(me) + me_prev = only(message(bmpsc, pe)) + if !isnothing(p_above) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_above)))), + commonind(me_prev, only(message(bmpsc, p_above))), + ) + end + if !isnothing(p_below) + me = replaceind( + me, + commonind(me, only(message(bmpsc, reverse(p_below)))), + commonind(me_prev, only(message(bmpsc, p_below))), + ) + end + return set_message(bmpsc, pe, ITensor[me]) +end + +function default_updater( + alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v +) + bmpsc = if !isnothing(prev_pe) + gauge(alg, bmpsc, reverse(prev_pe), reverse(update_pe)) + else + gauge(alg, bmpsc, reverse(update_pe)) + end + bmpsc = if !isnothing(prev_v) + partition_update(bmpsc, prev_v, cur_v) + else + partition_update(bmpsc, cur_v) + end + return bmpsc +end + +function default_updater( + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v +) + bmpsc = if !isnothing(prev_pe) + gauge(alg, bmpsc, prev_pe, update_pe) + else + gauge(alg, bmpsc, update_pe) + end + bmpsc = if !isnothing(prev_v) + partition_update(bmpsc, prev_v, cur_v) + else + partition_update(bmpsc, cur_v) + end + return bmpsc +end + +function default_cache_prep_function( + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair +) + return bmpsc +end +function default_cache_prep_function( + alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, partitionpair +) + return switch_messages(bmpsc, partitionpair) +end + +default_niters(alg::Algorithm"orthogonal") = 25 +default_niters(alg::Algorithm"biorthogonal") = 3 +default_tolerance(alg::Algorithm"orthogonal") = 1e-10 +default_tolerance(alg::Algorithm"biorthogonal") = nothing + +function default_costfunction( + alg::Algorithm"orthogonal", + bmpsc::BoundaryMPSCache, + pe::PartitionEdge, + me::Vector{ITensor}, +) + return region_scalar(bp_cache(bmpsc), src(pe)) / norm(only(me)) +end + +function default_costfunction( + alg::Algorithm"biorthogonal", + bmpsc::BoundaryMPSCache, + pe::PartitionEdge, + me::Vector{ITensor}, +) + return region_scalar(bp_cache(bmpsc), src(pe)) / + dot(only(me), only(message(bmpsc, reverse(pe)))) +end + +#Update all the message tensors on an interpartition via a specified fitting procedure +#TODO: Make two-site possible +function update( + alg::Algorithm, + bmpsc::BoundaryMPSCache, partitionpair::Pair; - niters::Int64=25, - tolerance=1e-10, + inserter=default_inserter, + costfunction=default_costfunction, + updater=default_updater, + cache_prep_function=default_cache_prep_function, + niters::Int64=default_niters(alg), + tolerance=default_tolerance(alg), normalize=true, ) - bmpsc = switch_messages(bmpsc, partitionpair) + bmpsc = cache_prep_function(alg, bmpsc, partitionpair) pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) update_seq = vcat(pes, reverse(pes)[2:length(pes)]) prev_v, prev_pe = nothing, nothing - prev_costfunction = 0 + prev_cf = 0 for i in 1:niters - costfunction = 0 + cf = 0 for update_pe in update_seq cur_v = parent(src(update_pe)) - bmpsc = if !isnothing(prev_pe) - orthogonalize(bmpsc, reverse(prev_pe), reverse(update_pe)) - else - orthogonalize(bmpsc, reverse(update_pe)) - end - bmpsc = if !isnothing(prev_v) - partition_update(bmpsc, prev_v, cur_v) - else - partition_update(bmpsc, cur_v) - end + bmpsc = updater(alg, bmpsc, prev_pe, update_pe, prev_v, cur_v) me = updated_message( bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) ) - costfunction += region_scalar(bp_cache(bmpsc), src(update_pe)) / norm(me) - bmpsc = set_message(bmpsc, reverse(update_pe), dag.(me)) + cf += costfunction(alg, bmpsc, update_pe, me) + bmpsc = inserter(alg, bmpsc, update_pe, me) prev_v, prev_pe = cur_v, update_pe end - epsilon = abs(costfunction - prev_costfunction) / length(update_seq) + epsilon = abs(cf - prev_cf) / length(update_seq) if !isnothing(tolerance) && epsilon < tolerance - return switch_messages(bmpsc, partitionpair) + return cache_prep_function(alg, bmpsc, partitionpair) else - prev_costfunction = costfunction + prev_cf = cf end end - return switch_messages(bmpsc, partitionpair) -end - -#Update all the message tensors on an interpartition via a biorthogonal fitting procedure -function update( - alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, partitionpair::Pair; normalize=true -) - prev_v, prev_pe = nothing, nothing - for update_pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) - cur_v = parent(src(update_pe)) - bmpsc = if !isnothing(prev_pe) - biorthogonalize(bmpsc, prev_pe, update_pe) - else - biorthogonalize(bmpsc, update_pe) - end - bmpsc = if !isnothing(prev_v) - partition_update(bmpsc, prev_v, cur_v) - else - partition_update(bmpsc, cur_v) - end - - me_prev = only(message(bmpsc, update_pe)) - me = only( - updated_message( - bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) - ), - ) - p_above, p_below = partitionedge_above(bmpsc, update_pe), - partitionedge_below(bmpsc, update_pe) - if !isnothing(p_above) - me = replaceind( - me, - commonind(me, only(message(bmpsc, reverse(p_above)))), - commonind(me_prev, only(message(bmpsc, p_above))), - ) - end - if !isnothing(p_below) - me = replaceind( - me, - commonind(me, only(message(bmpsc, reverse(p_below)))), - commonind(me_prev, only(message(bmpsc, p_below))), - ) - end - bmpsc = set_message(bmpsc, update_pe, ITensor[me]) - prev_v, prev_pe = cur_v, update_pe - end - - return bmpsc + return cache_prep_function(alg, bmpsc, partitionpair) end #Assume all vertices live in the same partition for now From 35fe94f406b232475cb1930860c83c3b40e25bcd Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 10 Jan 2025 10:42:58 -0500 Subject: [PATCH 40/47] Generic naming support --- src/caches/abstractbeliefpropagationcache.jl | 8 +- src/caches/boundarympscache.jl | 169 ++++++++----------- src/caches/boundarympscacheutils.jl | 4 +- test/test_boundarymps.jl | 4 +- 4 files changed, 84 insertions(+), 101 deletions(-) diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index 36700fcf..d010043d 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -190,14 +190,16 @@ Compute message tensor as product of incoming mts and local state function updated_message( bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; - message_update=default_message_update, - message_update_kwargs=(;), + message_update_function=default_message_update, + message_update_function_kwargs=(;), ) vertex = src(edge) incoming_ms = incoming_messages(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) state = factor(bpc, vertex) - return message_update(ITensor[incoming_ms; state]; message_update_kwargs...) + return message_update_function( + ITensor[incoming_ms; state]; message_update_function_kwargs... + ) end function update( diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index c4ab049e..fd69c5f9 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -9,16 +9,18 @@ using SplitApplyCombine: group using ITensors: commoninds, random_itensor using LinearAlgebra: pinv -struct BoundaryMPSCache{BPC,PG} <: AbstractBeliefPropagationCache +struct BoundaryMPSCache{BPC,PG,PP} <: AbstractBeliefPropagationCache bp_cache::BPC partitionedplanargraph::PG + partitionpair_partitionedges::PP maximum_virtual_dimension::Int64 end bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph -maximum_virtual_dimension(bmpsc::BoundaryMPSCache) = bmpsc.maximum_virtual_dimension ppg(bmpsc) = partitionedplanargraph(bmpsc) +maximum_virtual_dimension(bmpsc::BoundaryMPSCache) = bmpsc.maximum_virtual_dimension +partitionpair_partitionedges(bmpsc::BoundaryMPSCache) = bmpsc.partitionpair_partitionedges planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) @@ -38,16 +40,23 @@ end function default_message_update_kwargs(alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache) return (; niters=50, tolerance=1e-10) end -default_message_update_kwargs(alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache) = (;) +function default_message_update_kwargs( + alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache +) + return (; niters=3, tolerance=nothing) +end function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache( - copy(bp_cache(bmpsc)), copy(ppg(bmpsc)), maximum_virtual_dimension(bmpsc) + copy(bp_cache(bmpsc)), + copy(ppg(bmpsc)), + copy(partitionpair_partitionedges(bmpsc)), + maximum_virtual_dimension(bmpsc), ) end -function default_message(bmpsc::BoundaryMPSCache, args...; kwargs...) - return default_message(bp_cache(bmpsc), args...; kwargs...) +function default_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) + return default_message(bp_cache(bmpsc), pe::PartitionEdge; kwargs...) end function virtual_index_dimension( @@ -56,27 +65,20 @@ function virtual_index_dimension( pes = planargraph_partitionpair_partitionedges( bmpsc, planargraph_partitionpair(bmpsc, pe1) ) + if findfirst(x -> x == pe1, pes) > findfirst(x -> x == pe2, pes) - inds_above = reduce( - vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pe2)] - ) - inds_below = reduce( - vcat, [linkinds(bmpsc, pe) for pe in partitionedges_below(bmpsc, pe1)] - ) + lower_pe, upper_pe = pe2, pe1 else - inds_above = reduce( - vcat, [linkinds(bmpsc, pe) for pe in partitionedges_above(bmpsc, pe1)] - ) - inds_below = reduce( - vcat, [linkinds(bmpsc, pe) for pe in partitionedges_below(bmpsc, pe2)] - ) + lower_pe, upper_pe = pe1, pe2 end + inds_above = reduce(vcat, linkinds.((bmpsc,), partitionedges_above(bmpsc, lower_pe))) + inds_below = reduce(vcat, linkinds.((bmpsc,), partitionedges_below(bmpsc, upper_pe))) return minimum(( prod(dim.(inds_above)), prod(dim.(inds_below)), maximum_virtual_dimension(bmpsc) )) end -function planargraph_vertices(bmpsc::BoundaryMPSCache, partition::Int64) +function planargraph_vertices(bmpsc::BoundaryMPSCache, partition) return vertices(ppg(bmpsc), PartitionVertex(partition)) end function planargraph_partition(bmpsc::BoundaryMPSCache, vertex) @@ -88,17 +90,26 @@ end function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return pair(partitionedge(ppg(bmpsc), parent(pe))) end +function planargraph_partitionpair_partitionedges( + bmpsc::BoundaryMPSCache, partition_pair::Pair +) + return partitionpair_partitionedges(bmpsc)[partition_pair] +end function BoundaryMPSCache( bpc::BeliefPropagationCache; grouping_function::Function=v -> first(v), + group_sorting_function::Function=v -> last(v), message_rank::Int64=1, ) bpc = insert_pseudo_planar_edges(bpc; grouping_function) planar_graph = partitioned_graph(bpc) vertex_groups = group(grouping_function, collect(vertices(planar_graph))) + vertex_groups = map(x -> sort(x; by=group_sorting_function), vertex_groups) ppg = PartitionedGraph(planar_graph, vertex_groups) - bmpsc = BoundaryMPSCache(bpc, ppg, message_rank) + partitionpairs = vcat(pair.(partitionedges(ppg)), reverse.(pair.(partitionedges(ppg)))) + pp_pe = Dictionary(partitionpairs, sorted_partitionedges.((ppg,), partitionpairs)) + bmpsc = BoundaryMPSCache(bpc, ppg, pp_pe, message_rank) return set_interpartition_messages(bmpsc) end @@ -106,26 +117,20 @@ function BoundaryMPSCache(tn::AbstractITensorNetwork, args...; kwargs...) return BoundaryMPSCache(BeliefPropagationCache(tn, args...); kwargs...) end -#Get all partitionedges within a column/row, ordered top to bottom -function planargraph_partitionedges(bmpsc::BoundaryMPSCache, partition::Int64) - vs = sort(planargraph_vertices(bmpsc, partition)) - return PartitionEdge.([vs[i] => vs[i + 1] for i in 1:(length(vs) - 1)]) -end - -#Get all partitionedges between the pair of neighboring partitions, sorted top to bottom -#TODO: Bring in line with NamedGraphs change -function planargraph_partitionpair_partitionedges( - bmpsc::BoundaryMPSCache, partitionpair::Pair -) - pg = planargraph(bmpsc) - src_vs, dst_vs = planargraph_vertices(bmpsc, first(partitionpair)), - planargraph_vertices(bmpsc, last(partitionpair)) - es = filter( - x -> !isempty(last(x)), - [src_v => intersect(neighbors(pg, src_v), dst_vs) for src_v in src_vs], +#Get all partitionedges between the pair of neighboring partitions, sorted +#by the position of the source in the pg +function sorted_partitionedges(pg::PartitionedGraph, partitionpair::Pair) + src_vs, dst_vs = vertices(pg, PartitionVertex(first(partitionpair))), + vertices(pg, PartitionVertex(last(partitionpair))) + es = reduce( + vcat, + [ + [src_v => dst_v for dst_v in intersect(neighbors(pg, src_v), dst_vs)] for + src_v in src_vs + ], ) - es = map(x -> first(x) => only(last(x)), es) - return sort(PartitionEdge.(NamedEdge.(es)); by=x -> src(parent(x))) + es = sort(NamedEdge.(es); by=x -> findfirst(isequal(src(x)), src_vs)) + return PartitionEdge.(es) end #Functions to get the parellel partitionedges sitting above and below a partitionedge @@ -159,8 +164,9 @@ end #Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 function mps_gauge_update_sequence( - bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge + bmpsc::BoundaryMPSCache, pe1::Union{Nothing,PartitionEdge}, pe2::PartitionEdge ) + isnothing(pe1) && return mps_gauge_update_sequence(bmpsc, pe2) ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), planargraph_partitionpair(bmpsc, pe2) @assert ppgpe1 == ppgpe2 @@ -225,15 +231,18 @@ function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) end #Update all messages tensors within a partition -function partition_update(bmpsc::BoundaryMPSCache, partition::Int64) - vs = sort(planargraph_vertices(bmpsc, partition)) - bmpsc = partition_update(bmpsc, first(vs)) - bmpsc = partition_update(bmpsc, last(vs)) +function partition_update(bmpsc::BoundaryMPSCache, partition) + vs = planargraph_vertices(bmpsc, partition) + bmpsc = partition_update(bmpsc, first(vs), last(vs)) + bmpsc = partition_update(bmpsc, last(vs), first(vs)) return bmpsc end function partition_update_sequence(bmpsc::BoundaryMPSCache, v1, v2) - return PartitionEdge.(a_star(ppg(bmpsc), v1, v2)) + isnothing(v1) && return partition_update_sequence(bmpsc, v2) + pv = planargraph_partition(bmpsc, v1) + g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) + return PartitionEdge.(a_star(g, v1, v2)) end function partition_update_sequence(bmpsc::BoundaryMPSCache, v) pv = planargraph_partition(bmpsc, v) @@ -247,7 +256,7 @@ function partition_update(bmpsc::BoundaryMPSCache, args...) Algorithm("SimpleBP"), bmpsc, partition_update_sequence(bmpsc, args...); - message_update_kwargs=(; normalize=false), + message_update_function_kwargs=(; normalize=false), ) end @@ -285,8 +294,8 @@ function gauge_step( m2, m2r = only(message(bmpsc, pe2)), only(message(bmpsc, reverse(pe2))) top_cind, bottom_cind = commonind(m1, m2), commonind(m1r, m2r) m1_siteinds, m2_siteinds = commoninds(m1, m1r), commoninds(m2, m2r) - top_ncind, bottom_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]), - setdiff(inds(m1r), [m1_siteinds; bottom_cind]) + top_ncind = setdiff(inds(m1), [m1_siteinds; top_cind]) + bottom_ncind = setdiff(inds(m1r), [m1_siteinds; bottom_cind]) E = if isempty(top_ncind) m1 * m1r @@ -298,16 +307,14 @@ function gauge_step( S_sqrtinv = map_diag(x -> pinv(sqrt(x)), S) S_sqrt = map_diag(x -> sqrt(x), S) - set!(ms, pe1, ITensor[replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind)]) - set!( - ms, - reverse(pe1), - ITensor[replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind)], - ) - set!(ms, pe2, ITensor[replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind)]) - set!( - ms, reverse(pe2), ITensor[replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind)] - ) + m1 = replaceind((m1 * dag(V)) * S_sqrtinv, commonind(U, S), top_cind) + m1r = replaceind((m1r * dag(U)) * S_sqrtinv, commonind(V, S), bottom_cind) + m2 = replaceind((m2 * V) * S_sqrt, commonind(U, S), top_cind) + m2r = replaceind((m2r * U) * S_sqrt, commonind(V, S), bottom_cind) + set!(ms, pe1, ITensor[m1]) + set!(ms, reverse(pe1), ITensor[m1r]) + set!(ms, pe2, ITensor[m2]) + set!(ms, reverse(pe2), ITensor[m2r]) return bmpsc end @@ -352,19 +359,10 @@ function default_inserter( p_above, p_below = partitionedge_above(bmpsc, pe), partitionedge_below(bmpsc, pe) me = only(me) me_prev = only(message(bmpsc, pe)) - if !isnothing(p_above) - me = replaceind( - me, - commonind(me, only(message(bmpsc, reverse(p_above)))), - commonind(me_prev, only(message(bmpsc, p_above))), - ) - end - if !isnothing(p_below) - me = replaceind( - me, - commonind(me, only(message(bmpsc, reverse(p_below)))), - commonind(me_prev, only(message(bmpsc, p_below))), - ) + for pe in filter(x -> !isnothing(x), [p_above, p_below]) + ind1 = commonind(me, only(message(bmpsc, reverse(pe)))) + ind2 = commonind(me_prev, only(message(bmpsc, pe))) + me *= delta(ind1, ind2) end return set_message(bmpsc, pe, ITensor[me]) end @@ -372,32 +370,17 @@ end function default_updater( alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v ) - bmpsc = if !isnothing(prev_pe) - gauge(alg, bmpsc, reverse(prev_pe), reverse(update_pe)) - else - gauge(alg, bmpsc, reverse(update_pe)) - end - bmpsc = if !isnothing(prev_v) - partition_update(bmpsc, prev_v, cur_v) - else - partition_update(bmpsc, cur_v) - end + rev_prev_pe = isnothing(prev_pe) ? nothing : reverse(prev_pe) + bmpsc = gauge(alg, bmpsc, rev_prev_pe, reverse(update_pe)) + bmpsc = partition_update(bmpsc, prev_v, cur_v) return bmpsc end function default_updater( alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v ) - bmpsc = if !isnothing(prev_pe) - gauge(alg, bmpsc, prev_pe, update_pe) - else - gauge(alg, bmpsc, update_pe) - end - bmpsc = if !isnothing(prev_v) - partition_update(bmpsc, prev_v, cur_v) - else - partition_update(bmpsc, cur_v) - end + bmpsc = gauge(alg, bmpsc, prev_pe, update_pe) + bmpsc = partition_update(bmpsc, prev_v, cur_v) return bmpsc end @@ -460,9 +443,7 @@ function update( for update_pe in update_seq cur_v = parent(src(update_pe)) bmpsc = updater(alg, bmpsc, prev_pe, update_pe, prev_v, cur_v) - me = updated_message( - bmpsc, update_pe; message_update=ms -> default_message_update(ms; normalize) - ) + me = updated_message(bmpsc, update_pe; message_update_function_kwargs=(; normalize)) cf += costfunction(alg, bmpsc, update_pe, me) bmpsc = inserter(alg, bmpsc, update_pe, me) prev_v, prev_pe = cur_v, update_pe diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index 2c6758f5..b1b66acb 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -38,8 +38,8 @@ pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) #Return a sequence of pairs to go from item1 to item2 in an ordered_list function pair_sequence(ordered_list::Vector, item1, item2) - item1_pos, item2_pos = only(findall(x -> x == item1, ordered_list)), - only(findall(x -> x == item2, ordered_list)) + item1_pos, item2_pos = findfirst(x -> x == item1, ordered_list), + findfirst(x -> x == item2, ordered_list) item1_pos < item2_pos && return [ordered_list[i] => ordered_list[i + 1] for i in item1_pos:(item2_pos - 1)] return [ordered_list[i] => ordered_list[i - 1] for i in item1_pos:-1:(item2_pos + 1)] diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 430ba1e6..0549e186 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -117,11 +117,11 @@ using LinearAlgebra: norm x -> abs(real(x)), A, first(inds(A)), last(inds(A)); ishermitian=true ) end - message_update_f = ms -> make_posdef.(default_message_update(ms)) + f = ms -> make_posdef.(default_message_update(ms)) ψ_vidal = VidalITensorNetwork( ψ; cache_update_kwargs=(; - maxiter=50, tol=1e-14, message_update_kwargs=(; message_update=message_update_f) + maxiter=50, tol=1e-14, message_update_kwargs=(; message_update_function=f) ), ) cache_ref = Ref{BeliefPropagationCache}() From 4ae3b5347645d623e92644658845c0e185d503ec Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 10 Jan 2025 14:39:43 -0500 Subject: [PATCH 41/47] Updated expect, inner and environment interfaces --- src/caches/abstractbeliefpropagationcache.jl | 15 +++---- src/caches/beliefpropagationcache.jl | 16 +++++-- src/caches/boundarympscache.jl | 45 +++++++++++++++++-- src/contract.jl | 6 +-- src/environment.jl | 11 ++--- src/expect.jl | 2 +- src/inner.jl | 8 ++-- test/test_boundarymps.jl | 47 ++++++++------------ 8 files changed, 92 insertions(+), 58 deletions(-) diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index d010043d..b0372c65 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -43,7 +43,6 @@ default_partitioned_vertices(ψ::AbstractITensorNetwork) = group(v -> v, vertice function default_partitioned_vertices(f::AbstractFormNetwork) return group(v -> original_state_vertex(f, v), vertices(f)) end -default_cache_update_kwargs(cache) = (; maxiter=25, tol=1e-8) partitioned_tensornetwork(bpc::AbstractBeliefPropagationCache) = not_implemented() messages(bpc::AbstractBeliefPropagationCache) = not_implemented() @@ -70,6 +69,8 @@ end function region_scalar(bpc::AbstractBeliefPropagationCache, pe::PartitionEdge; kwargs...) return not_implemented() end +partitions(bpc::AbstractBeliefPropagationCache) = not_implemented() +partitionpairs(bpc::AbstractBeliefPropagationCache) = not_implemented() function default_edge_sequence( bpc::AbstractBeliefPropagationCache; alg=default_message_update_alg(bpc) @@ -99,18 +100,12 @@ function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) return factors(bpc, vertices(bpc, vertex)) end -function vertex_scalars( - bpc::AbstractBeliefPropagationCache, - pvs=partitionvertices(partitioned_tensornetwork(bpc)); - kwargs..., -) +function vertex_scalars(bpc::AbstractBeliefPropagationCache, pvs=partitions(bpc); kwargs...) return map(pv -> region_scalar(bpc, pv; kwargs...), pvs) end function edge_scalars( - bpc::AbstractBeliefPropagationCache, - pes=partitionedges(partitioned_tensornetwork(bpc)); - kwargs..., + bpc::AbstractBeliefPropagationCache, pes=partitionpairs(bpc); kwargs... ) return map(pe -> region_scalar(bpc, pe; kwargs...), pes) end @@ -203,7 +198,7 @@ function updated_message( end function update( - alg::Algorithm"SimpleBP", + alg::Algorithm"simplebp", bpc::AbstractBeliefPropagationCache, edge::PartitionEdge; kwargs..., diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index 97dd6471..c7db68d2 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -18,6 +18,10 @@ function default_cache_construction_kwargs(alg::Algorithm"bp", ψ::AbstractITens return (; partitioned_vertices=default_partitioned_vertices(ψ)) end +function default_cache_construction_kwargs(alg::Algorithm"bp", pg::PartitionedGraph) + return (;) +end + struct BeliefPropagationCache{PTN,MTS} <: AbstractBeliefPropagationCache partitioned_tensornetwork::PTN messages::MTS @@ -44,6 +48,7 @@ end function cache(alg::Algorithm"bp", tn; kwargs...) return BeliefPropagationCache(tn; kwargs...) end +default_cache_update_kwargs(alg::Algorithm"bp") = (; maxiter=25, tol=1e-8) function partitioned_tensornetwork(bp_cache::BeliefPropagationCache) return bp_cache.partitioned_tensornetwork @@ -61,20 +66,23 @@ function Base.copy(bp_cache::BeliefPropagationCache) ) end -default_message_update_alg(bp_cache::BeliefPropagationCache) = "SimpleBP" +default_message_update_alg(bp_cache::BeliefPropagationCache) = "simplebp" -function default_bp_maxiter(alg::Algorithm"SimpleBP", bp_cache::BeliefPropagationCache) +function default_bp_maxiter(alg::Algorithm"simplebp", bp_cache::BeliefPropagationCache) return default_bp_maxiter(partitioned_graph(bp_cache)) end -function default_edge_sequence(alg::Algorithm"SimpleBP", bp_cache::BeliefPropagationCache) +function default_edge_sequence(alg::Algorithm"simplebp", bp_cache::BeliefPropagationCache) return default_edge_sequence(partitioned_tensornetwork(bp_cache)) end function default_message_update_kwargs( - alg::Algorithm"SimpleBP", bpc::AbstractBeliefPropagationCache + alg::Algorithm"simplebp", bpc::AbstractBeliefPropagationCache ) return (;) end +partitions(bpc::BeliefPropagationCache) = partitionvertices(partitioned_tensornetwork(bpc)) +partitionpairs(bpc::BeliefPropagationCache) = partitionedges(partitioned_tensornetwork(bpc)) + function set_messages(cache::BeliefPropagationCache, messages) return BeliefPropagationCache(partitioned_tensornetwork(cache), messages) end diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index fd69c5f9..cf5fc7ba 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -45,6 +45,30 @@ function default_message_update_kwargs( ) return (; niters=3, tolerance=nothing) end +default_boundarymps_message_rank(tn::AbstractITensorNetwork) = maxlinkdim(tn)^2 +partitions(bmpsc::BoundaryMPSCache) = parent.(collect(partitionvertices(ppg(bmpsc)))) +partitionpairs(bmpsc::BoundaryMPSCache) = pair.(partitionedges(ppg(bmpsc))) + +function cache( + alg::Algorithm"boundarymps", + tn; + bp_cache_construction_kwargs=default_cache_construction_kwargs(Algorithm("bp"), tn), + kwargs..., +) + return BoundaryMPSCache( + BeliefPropagationCache(tn; bp_cache_construction_kwargs...); kwargs... + ) +end + +function default_cache_construction_kwargs(alg::Algorithm"boundarymps", tn) + return (; + bp_cache_construction_kwargs=default_cache_construction_kwargs(Algorithm("bp"), tn) + ) +end + +function default_cache_update_kwargs(alg::Algorithm"boundarymps") + return (; alg="orthogonal", message_update_kwargs=(; niters=25, tolerance=1e-10)) +end function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache( @@ -100,7 +124,7 @@ function BoundaryMPSCache( bpc::BeliefPropagationCache; grouping_function::Function=v -> first(v), group_sorting_function::Function=v -> last(v), - message_rank::Int64=1, + message_rank::Int64=default_boundarymps_message_rank(tensornetwork(bpc)), ) bpc = insert_pseudo_planar_edges(bpc; grouping_function) planar_graph = partitioned_graph(bpc) @@ -113,7 +137,7 @@ function BoundaryMPSCache( return set_interpartition_messages(bmpsc) end -function BoundaryMPSCache(tn::AbstractITensorNetwork, args...; kwargs...) +function BoundaryMPSCache(tn, args...; kwargs...) return BoundaryMPSCache(BeliefPropagationCache(tn, args...); kwargs...) end @@ -253,7 +277,7 @@ end #Update all messages within a partition along the path from from v1 to v2 function partition_update(bmpsc::BoundaryMPSCache, args...) return update( - Algorithm("SimpleBP"), + Algorithm("simplebp"), bmpsc, partition_update_sequence(bmpsc, args...); message_update_function_kwargs=(; normalize=false), @@ -465,3 +489,18 @@ function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwa bmpsc = partition_update(bmpsc, pv) return environment(bp_cache(bmpsc), verts; kwargs...) end + +function region_scalar(bmpsc::BoundaryMPSCache, partition) + partition_vs = planargraph_vertices(bmpsc, partition) + bmpsc = partition_update(bmpsc, first(partition_vs), last(partition_vs)) + return region_scalar(bp_cache(bmpsc), PartitionVertex(last(partition_vs))) +end + +function region_scalar(bmpsc::BoundaryMPSCache, partitionpair::Pair) + pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + out = ITensor(1.0) + for pe in pes + out = (out * (only(message(bmpsc, pe)))) * only(message(bmpsc, reverse(pe))) + end + return out[] +end diff --git a/src/contract.jl b/src/contract.jl index 4adb0e10..70036566 100644 --- a/src/contract.jl +++ b/src/contract.jl @@ -30,7 +30,7 @@ function NDTensors.contract( return contract_approx(alg, tn, output_structure; kwargs...) end -function ITensors.scalar(alg::Algorithm, tn::AbstractITensorNetwork; kwargs...) +function ITensors.scalar(alg::Algorithm"exact", tn::AbstractITensorNetwork; kwargs...) return contract(alg, tn; kwargs...)[] end @@ -54,7 +54,7 @@ function logscalar( (cache!)=nothing, cache_construction_kwargs=default_cache_construction_kwargs(alg, tn), update_cache=isnothing(cache!), - cache_update_kwargs=default_cache_update_kwargs(cache!), + cache_update_kwargs=default_cache_update_kwargs(alg), ) if isnothing(cache!) cache! = Ref(cache(alg, tn; cache_construction_kwargs...)) @@ -77,6 +77,6 @@ function logscalar( return sum(log.(numerator_terms)) - sum(log.((denominator_terms))) end -function ITensors.scalar(alg::Algorithm"bp", tn::AbstractITensorNetwork; kwargs...) +function ITensors.scalar(alg::Algorithm, tn::AbstractITensorNetwork; kwargs...) return exp(logscalar(alg, tn; kwargs...)) end diff --git a/src/environment.jl b/src/environment.jl index f3c424c0..cec13f21 100644 --- a/src/environment.jl +++ b/src/environment.jl @@ -1,7 +1,7 @@ using ITensors: contract using NamedGraphs.PartitionedGraphs: PartitionedGraph -default_environment_algorithm() = "exact" +default_environment_algorithm() = "bp" function environment( tn::AbstractITensorNetwork, @@ -19,15 +19,16 @@ function environment( end function environment( - ::Algorithm"bp", + alg::Algorithm, ptn::PartitionedGraph, vertices::Vector; (cache!)=nothing, update_cache=isnothing(cache!), - cache_update_kwargs=default_cache_update_kwargs(cache!), + cache_construction_kwargs=default_cache_construction_kwargs(alg, ptn), + cache_update_kwargs=default_cache_update_kwargs(alg), ) if isnothing(cache!) - cache! = Ref(BeliefPropagationCache(ptn)) + cache! = Ref(cache(alg, ptn; cache_construction_kwargs...)) end if update_cache @@ -38,7 +39,7 @@ function environment( end function environment( - alg::Algorithm"bp", + alg::Algorithm, tn::AbstractITensorNetwork, vertices::Vector; partitioned_vertices=default_partitioned_vertices(tn), diff --git a/src/expect.jl b/src/expect.jl index e1d46a9f..3414dd21 100644 --- a/src/expect.jl +++ b/src/expect.jl @@ -24,7 +24,7 @@ function ITensorMPS.expect( ops; (cache!)=nothing, update_cache=isnothing(cache!), - cache_update_kwargs=default_cache_update_kwargs(cache!), + cache_update_kwargs=default_cache_update_kwargs(alg), cache_construction_function=tn -> cache(alg, tn; default_cache_construction_kwargs(alg, tn)...), kwargs..., diff --git a/src/inner.jl b/src/inner.jl index 43486703..6f1cdc86 100644 --- a/src/inner.jl +++ b/src/inner.jl @@ -90,7 +90,7 @@ function ITensorMPS.loginner( end function ITensorMPS.loginner( - alg::Algorithm"bp", + alg::Algorithm, ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; dual_link_index_map=sim, @@ -101,7 +101,7 @@ function ITensorMPS.loginner( end function ITensorMPS.loginner( - alg::Algorithm"bp", + alg::Algorithm, ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, ψ::AbstractITensorNetwork; @@ -113,7 +113,7 @@ function ITensorMPS.loginner( end function ITensors.inner( - alg::Algorithm"bp", + alg::Algorithm, ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; dual_link_index_map=sim, @@ -124,7 +124,7 @@ function ITensors.inner( end function ITensors.inner( - alg::Algorithm"bp", + alg::Algorithm, ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, ψ::AbstractITensorNetwork; diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 0549e186..29fe4ad1 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -55,35 +55,26 @@ using LinearAlgebra: norm rng = StableRNG(1234) #First a comb tree (which is still a planar graph) and a flat tensor network - g = named_comb_tree((4, 4)) + g = named_comb_tree((3, 3)) χ = 2 tn = random_tensornetwork(rng, elt, g; link_space=χ) vc = first(center(g)) - ∂tn_∂vc_bp = contract(environment(tn, [vc]; alg="bp"); sequence="automatic") - ∂tn_∂vc_bp /= norm(∂tn_∂vc_bp) - - ∂tn_∂vc = subgraph(tn, setdiff(vertices(tn), [vc])) - ∂tn_∂vc_exact = contract(∂tn_∂vc; sequence=contraction_sequence(∂tn_∂vc; alg="greedy")) - ∂tn_∂vc_exact /= norm(∂tn_∂vc_exact) - - #Orthogonal Boundary MPS, group by row - tn_boundaryMPS = BoundaryMPSCache(tn; grouping_function=v -> last(v), message_rank=1) - tn_boundaryMPS = update(tn_boundaryMPS) - ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") - ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) - - @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) - @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) - - #Biorthogonal Boundary MPS, , group by row - tn_boundaryMPS = BoundaryMPSCache(tn; grouping_function=v -> last(v), message_rank=1) - tn_boundaryMPS = update(tn_boundaryMPS; alg="biorthogonal") - ∂tn_∂vc_boundaryMPS = contract(environment(tn_boundaryMPS, [vc]); sequence="automatic") - ∂tn_∂vc_boundaryMPS /= norm(∂tn_∂vc_boundaryMPS) + λ_bp = scalar(tn; alg="bp") + λ_exact = scalar(tn; alg="exact") + #Orthogonal boundary MPS group by column (default) + λ_ortho_bmps = scalar(tn; alg="boundarymps") + #Orthogonal boundary MPS group by column + λ_biortho_bmps = scalar( + tn; + alg="boundarymps", + cache_construction_kwargs=(; grouping_function=v -> last(v)), + cache_update_kwargs=(; maxiter=1, alg="biorthogonal"), + ) - @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_exact) <= 10 * eps(real(elt)) - @test norm(∂tn_∂vc_boundaryMPS - ∂tn_∂vc_bp) <= 10 * eps(real(elt)) + @test abs(λ_ortho_bmps - λ_exact) <= 10 * eps(real(elt)) + @test abs(λ_ortho_bmps - λ_bp) <= 10 * eps(real(elt)) + @test abs(λ_ortho_bmps - λ_biortho_bmps) <= 10 * eps(real(elt)) #Now the norm tensor network of a square graph with a few vertices missing for added complexity g = named_grid((5, 5)) @@ -98,14 +89,14 @@ using LinearAlgebra: norm ρ_exact /= tr(ρ_exact) #Orthogonal Boundary MPS, group by column (default) - ψIψ_boundaryMPS = BoundaryMPSCache(ψIψ; message_rank=χ * χ) - ψIψ_boundaryMPS = update(ψIψ_boundaryMPS) - ρ_boundaryMPS = contract(environment(ψIψ_boundaryMPS, [vc]); sequence="automatic") + ρ_boundaryMPS = contract( + environment(ψIψ, [vc]; alg="boundarymps"); sequence="automatic" + ) ρ_boundaryMPS /= tr(ρ_boundaryMPS) @test norm(ρ_boundaryMPS - ρ_exact) <= 10 * eps(real(elt)) - #Now we test BP and orthogonal and biorthogonl Boundary MPS are equivalent when run from in the symmetric gauge + #Now we test BP and orthogonal and biorthogonal Boundary MPS are equivalent when run from in the symmetric gauge g = named_hexagonal_lattice_graph(3, 3) s = siteinds("S=1/2", g) χ = 2 From df7a4c5de8bd3c8f54cf327cf4447b9357677d4e Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 10 Jan 2025 16:06:23 -0500 Subject: [PATCH 42/47] Fix failing tests --- src/gauging.jl | 2 +- test/test_forms.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gauging.jl b/src/gauging.jl index 2ad9d0f4..49384ca6 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -128,7 +128,7 @@ function VidalITensorNetwork( ψ::ITensorNetwork; (cache!)=nothing, update_cache=isnothing(cache!), - cache_update_kwargs=default_cache_update_kwargs(cache!), + cache_update_kwargs=default_cache_update_kwargs(Algorithm("bp")), kwargs..., ) if isnothing(cache!) diff --git a/test/test_forms.jl b/test/test_forms.jl index a58822e5..7e6ada8b 100644 --- a/test/test_forms.jl +++ b/test/test_forms.jl @@ -62,7 +62,7 @@ using Test: @test, @testset @test underlying_graph(ket_network(qf)) == underlying_graph(ψket) @test underlying_graph(operator_network(qf)) == underlying_graph(A) - ∂qf_∂v = only(environment(qf, state_vertices(qf, [v]))) + ∂qf_∂v = only(environment(qf, state_vertices(qf, [v]); alg="exact")) @test (∂qf_∂v) * (qf[ket_vertex(qf, v)] * qf[bra_vertex(qf, v)]) ≈ contract(qf) ∂qf_∂v_bp = environment(qf, state_vertices(qf, [v]); alg="bp", update_cache=false) From e9e0336c568d89475c287742d8fdfe483e1e55b1 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 13 Jan 2025 17:11:06 -0500 Subject: [PATCH 43/47] Two site working --- src/caches/abstractbeliefpropagationcache.jl | 16 +- src/caches/beliefpropagationcache.jl | 2 +- src/caches/boundarympscache.jl | 321 ++++++++++++------- src/caches/boundarympscacheutils.jl | 9 - test/test_boundarymps.jl | 15 +- 5 files changed, 234 insertions(+), 129 deletions(-) diff --git a/src/caches/abstractbeliefpropagationcache.jl b/src/caches/abstractbeliefpropagationcache.jl index b0372c65..f3a3baac 100644 --- a/src/caches/abstractbeliefpropagationcache.jl +++ b/src/caches/abstractbeliefpropagationcache.jl @@ -96,8 +96,14 @@ function factors(bpc::AbstractBeliefPropagationCache, verts::Vector) return ITensor[tensornetwork(bpc)[v] for v in verts] end -function factor(bpc::AbstractBeliefPropagationCache, vertex::PartitionVertex) - return factors(bpc, vertices(bpc, vertex)) +function factors( + bpc::AbstractBeliefPropagationCache, partition_verts::Vector{<:PartitionVertex} +) + return factors(bpc, vertices(bpc, partition_verts)) +end + +function factors(bpc::AbstractBeliefPropagationCache, partition_vertex::PartitionVertex) + return factors(bpc, [partition_vertex]) end function vertex_scalars(bpc::AbstractBeliefPropagationCache, pvs=partitions(bpc); kwargs...) @@ -190,7 +196,7 @@ function updated_message( ) vertex = src(edge) incoming_ms = incoming_messages(bpc, vertex; ignore_edges=PartitionEdge[reverse(edge)]) - state = factor(bpc, vertex) + state = factors(bpc, vertex) return message_update_function( ITensor[incoming_ms; state]; message_update_function_kwargs... @@ -203,9 +209,7 @@ function update( edge::PartitionEdge; kwargs..., ) - new_m = updated_message(bpc, edge; kwargs...) - bpc = set_message(bpc, edge, new_m) - return bpc + return set_message(bpc, edge, updated_message(bpc, edge; kwargs...)) end """ diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index c7db68d2..1143f3f8 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -100,7 +100,7 @@ function region_scalar( contract_kwargs=(; sequence="automatic"), ) incoming_mts = incoming_messages(bp_cache, [pv]) - local_state = factor(bp_cache, pv) + local_state = factors(bp_cache, pv) return contract(vcat(incoming_mts, local_state); contract_kwargs...)[] end diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index cf5fc7ba..0c586978 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -9,10 +9,9 @@ using SplitApplyCombine: group using ITensors: commoninds, random_itensor using LinearAlgebra: pinv -struct BoundaryMPSCache{BPC,PG,PP} <: AbstractBeliefPropagationCache +struct BoundaryMPSCache{BPC,PG} <: AbstractBeliefPropagationCache bp_cache::BPC partitionedplanargraph::PG - partitionpair_partitionedges::PP maximum_virtual_dimension::Int64 end @@ -20,7 +19,6 @@ bp_cache(bmpsc::BoundaryMPSCache) = bmpsc.bp_cache partitionedplanargraph(bmpsc::BoundaryMPSCache) = bmpsc.partitionedplanargraph ppg(bmpsc) = partitionedplanargraph(bmpsc) maximum_virtual_dimension(bmpsc::BoundaryMPSCache) = bmpsc.maximum_virtual_dimension -partitionpair_partitionedges(bmpsc::BoundaryMPSCache) = bmpsc.partitionpair_partitionedges planargraph(bmpsc::BoundaryMPSCache) = unpartitioned_graph(partitionedplanargraph(bmpsc)) function partitioned_tensornetwork(bmpsc::BoundaryMPSCache) @@ -72,10 +70,7 @@ end function Base.copy(bmpsc::BoundaryMPSCache) return BoundaryMPSCache( - copy(bp_cache(bmpsc)), - copy(ppg(bmpsc)), - copy(partitionpair_partitionedges(bmpsc)), - maximum_virtual_dimension(bmpsc), + copy(bp_cache(bmpsc)), copy(ppg(bmpsc)), maximum_virtual_dimension(bmpsc) ) end @@ -86,9 +81,7 @@ end function virtual_index_dimension( bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge ) - pes = planargraph_partitionpair_partitionedges( - bmpsc, planargraph_partitionpair(bmpsc, pe1) - ) + pes = planargraph_sorted_partitionedges(bmpsc, planargraph_partitionpair(bmpsc, pe1)) if findfirst(x -> x == pe1, pes) > findfirst(x -> x == pe2, pes) lower_pe, upper_pe = pe2, pe1 @@ -114,10 +107,8 @@ end function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return pair(partitionedge(ppg(bmpsc), parent(pe))) end -function planargraph_partitionpair_partitionedges( - bmpsc::BoundaryMPSCache, partition_pair::Pair -) - return partitionpair_partitionedges(bmpsc)[partition_pair] +function planargraph_sorted_partitionedges(bmpsc::BoundaryMPSCache, partition_pair::Pair) + return sorted_partitionedges(ppg(bmpsc), partition_pair) end function BoundaryMPSCache( @@ -131,9 +122,7 @@ function BoundaryMPSCache( vertex_groups = group(grouping_function, collect(vertices(planar_graph))) vertex_groups = map(x -> sort(x; by=group_sorting_function), vertex_groups) ppg = PartitionedGraph(planar_graph, vertex_groups) - partitionpairs = vcat(pair.(partitionedges(ppg)), reverse.(pair.(partitionedges(ppg)))) - pp_pe = Dictionary(partitionpairs, sorted_partitionedges.((ppg,), partitionpairs)) - bmpsc = BoundaryMPSCache(bpc, ppg, pp_pe, message_rank) + bmpsc = BoundaryMPSCache(bpc, ppg, message_rank) return set_interpartition_messages(bmpsc) end @@ -159,17 +148,13 @@ end #Functions to get the parellel partitionedges sitting above and below a partitionedge function partitionedges_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) - pes = planargraph_partitionpair_partitionedges( - bmpsc, planargraph_partitionpair(bmpsc, pe) - ) + pes = planargraph_sorted_partitionedges(bmpsc, planargraph_partitionpair(bmpsc, pe)) pe_pos = only(findall(x -> x == pe, pes)) return PartitionEdge[pes[i] for i in (pe_pos + 1):length(pes)] end function partitionedges_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) - pes = planargraph_partitionpair_partitionedges( - bmpsc, planargraph_partitionpair(bmpsc, pe) - ) + pes = planargraph_sorted_partitionedges(bmpsc, planargraph_partitionpair(bmpsc, pe)) pe_pos = only(findall(x -> x == pe, pes)) return PartitionEdge[pes[i] for i in 1:(pe_pos - 1)] end @@ -186,36 +171,59 @@ function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return last(pes_below) end -#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from pe1 to pe2 +#Given a sequence of message tensor updates in a partition, get the sequence of gauge moves needed to move the MPS gauge +#from the start of the sequence to the end of the sequence function mps_gauge_update_sequence( - bmpsc::BoundaryMPSCache, pe1::Union{Nothing,PartitionEdge}, pe2::PartitionEdge + bmpsc::BoundaryMPSCache, + partition_update_seq::Vector{<:PartitionEdge}, + partition_pair::Pair, +) + vs = unique(reduce(vcat, [[src(pe), dst(pe)] for pe in parent.(partition_update_seq)])) + g = planargraph(bmpsc) + dst_vs = planargraph_vertices(bmpsc, last(partition_pair)) + pe_sequence = [v => intersect(neighbors(g, v), dst_vs) for v in vs] + pe_sequence = filter(x -> !isempty(last(x)), pe_sequence) + pe_sequence = map(x -> first(x) => only(last(x)), pe_sequence) + return [ + (PartitionEdge(pe_sequence[i]), PartitionEdge(pe_sequence[i + 1])) for + i in 1:(length(pe_sequence) - 1) + ] +end + +#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from one region of partitionedges to another +function mps_gauge_update_sequence( + bmpsc::BoundaryMPSCache, + pe_region1::Vector{<:PartitionEdge}, + pe_region2::Vector{<:PartitionEdge}, ) - isnothing(pe1) && return mps_gauge_update_sequence(bmpsc, pe2) - ppgpe1, ppgpe2 = planargraph_partitionpair(bmpsc, pe1), - planargraph_partitionpair(bmpsc, pe2) - @assert ppgpe1 == ppgpe2 - pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe1) - return pair_sequence(pes, pe1, pe2) + issetequal(pe_region1, pe_region2) && return [] + partitionpair = planargraph_partitionpair(bmpsc, first(pe_region2)) + seq = partition_update_sequence( + bmpsc, parent.(src.(pe_region1)), parent.(src.(pe_region2)) + ) + return mps_gauge_update_sequence(bmpsc, seq, partitionpair) +end + +function mps_gauge_update_sequence( + bmpsc::BoundaryMPSCache, pe_region::Vector{<:PartitionEdge} +) + partitionpair = planargraph_partitionpair(bmpsc, first(pe_region)) + pes = planargraph_sorted_partitionedges(bmpsc, partitionpair) + return mps_gauge_update_sequence(bmpsc, pes, pe_region) end -#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge onto pe function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) - ppgpe = planargraph_partitionpair(bmpsc, pe) - pes = planargraph_partitionpair_partitionedges(bmpsc, ppgpe) - return vcat( - mps_gauge_update_sequence(bmpsc, last(pes), pe), - mps_gauge_update_sequence(bmpsc, first(pes), pe), - ) + return mps_gauge_update_sequence(bmpsc, [pe]) end -#Initialise all the message tensors for the pairs of neighboring partitions, with virtual rank given by message rank +#Initialise all the message tensors for the pairs of neighboring partitions function set_interpartition_messages( bmpsc::BoundaryMPSCache, partitionpairs::Vector{<:Pair} ) bmpsc = copy(bmpsc) ms = messages(bmpsc) for partitionpair in partitionpairs - pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + pes = planargraph_sorted_partitionedges(bmpsc, partitionpair) for pe in pes set!(ms, pe, ITensor[dense(delta(linkinds(bmpsc, pe)))]) end @@ -230,7 +238,7 @@ function set_interpartition_messages( return bmpsc end -#Initialise all the interpartition message tensors with virtual rank given by message rank +#Initialise all the interpartition message tensors function set_interpartition_messages(bmpsc::BoundaryMPSCache) partitionpairs = pair.(partitionedges(ppg(bmpsc))) return set_interpartition_messages(bmpsc, vcat(partitionpairs, reverse.(partitionpairs))) @@ -248,33 +256,41 @@ end #Switch the message tensors from partitionpair i -> i + 1 with those from i + 1 -> i function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) - for pe in planargraph_partitionpair_partitionedges(bmpsc, partitionpair) + for pe in planargraph_sorted_partitionedges(bmpsc, partitionpair) bmpsc = switch_message(bmpsc, pe) end return bmpsc end -#Update all messages tensors within a partition -function partition_update(bmpsc::BoundaryMPSCache, partition) +#Get sequence necessary to update all message tensors in a partition +function partition_update_sequence(bmpsc::BoundaryMPSCache, partition) vs = planargraph_vertices(bmpsc, partition) - bmpsc = partition_update(bmpsc, first(vs), last(vs)) - bmpsc = partition_update(bmpsc, last(vs), first(vs)) - return bmpsc + return vcat( + partition_update_sequence(bmpsc, [first(vs)]), + partition_update_sequence(bmpsc, [last(vs)]), + ) end -function partition_update_sequence(bmpsc::BoundaryMPSCache, v1, v2) - isnothing(v1) && return partition_update_sequence(bmpsc, v2) - pv = planargraph_partition(bmpsc, v1) +#Get sequence necessary to move correct message tensors in a partition from region1 to region 2 +function partition_update_sequence( + bmpsc::BoundaryMPSCache, region1::Vector, region2::Vector +) + issetequal(region1, region2) && return PartitionEdge[] + pv = planargraph_partition(bmpsc, first(region2)) g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) - return PartitionEdge.(a_star(g, v1, v2)) + st = steiner_tree(g, union(region1, region2)) + path = post_order_dfs_edges(st, first(region2)) + path = filter(e -> !((src(e) ∈ region2) && (dst(e) ∈ region2)), path) + return PartitionEdge.(path) end -function partition_update_sequence(bmpsc::BoundaryMPSCache, v) - pv = planargraph_partition(bmpsc, v) - g = subgraph(unpartitioned_graph(ppg(bmpsc)), planargraph_vertices(bmpsc, pv)) - return PartitionEdge.(post_order_dfs_edges(g, v)) + +#Get sequence necessary to move correct message tensors to region +function partition_update_sequence(bmpsc::BoundaryMPSCache, region::Vector) + pv = planargraph_partition(bmpsc, first(region)) + return partition_update_sequence(bmpsc, planargraph_vertices(bmpsc, pv), region) end -#Update all messages within a partition along the path from from v1 to v2 +#Update all messages tensors within a partition by finding the path needed function partition_update(bmpsc::BoundaryMPSCache, args...) return update( Algorithm("simplebp"), @@ -365,47 +381,119 @@ function biorthogonalize(bmpsc::BoundaryMPSCache, args...; kwargs...) return gauge(Algorithm("biorthogonal"), bmpsc, args...; kwargs...) end +default_inserter_transform(alg::Algorithm"biorthogonal") = identity +default_inserter_transform(alg::Algorithm"orthogonal") = dag +default_region_transform(alg::Algorithm"biorthogonal") = identity +default_region_transform(alg::Algorithm"orthogonal") = reverse + function default_inserter( - alg::Algorithm"orthogonal", + alg::Algorithm, bmpsc::BoundaryMPSCache, - pe::PartitionEdge, - me::Vector{ITensor}, + update_pe_region::Vector{<:PartitionEdge}, + ms::Vector{ITensor}; + inserter_transform=default_inserter_transform(alg), + region_transform=default_region_transform(alg), + nsites::Int64=1, + cutoff=nothing, + normalize=true, ) - return set_message(bmpsc, reverse(pe), dag.(me)) + update_pe_region = region_transform.(update_pe_region) + m = contract(ms; sequence="automatic") + if normalize + m /= norm(m) + end + if nsites == 1 + bmpsc = set_message(bmpsc, only(update_pe_region), ITensor[inserter_transform(m)]) + elseif nsites == 2 + pe1, pe2 = first(update_pe_region), last(update_pe_region) + me1, me2 = only(message(bmpsc, pe1)), only(message(bmpsc, pe2)) + upper_inds, cind = uniqueinds(me1, me2), commonind(me1, me2) + me1, me2 = factorize( + m, upper_inds; tags=tags(cind), cutoff, maxdim=maximum_virtual_dimension(bmpsc) + ) + bmpsc = set_message(bmpsc, pe1, ITensor[inserter_transform(me1)]) + bmpsc = set_message(bmpsc, pe2, ITensor[inserter_transform(me2)]) + else + error("Nsites > 2 not supported at the moment for Boundary MPS updating") + end + return bmpsc end -function default_inserter( - alg::Algorithm"biorthogonal", +function default_updater( + alg::Algorithm, bmpsc::BoundaryMPSCache, prev_pe_region, update_pe_region +) + if !isnothing(prev_pe_region) + bmpsc = gauge(alg, bmpsc, reverse.(prev_pe_region), reverse.(update_pe_region)) + bmpsc = partition_update( + bmpsc, parent.(src.(prev_pe_region)), parent.(src.(update_pe_region)) + ) + else + bmpsc = gauge(alg, bmpsc, reverse.(update_pe_region)) + bmpsc = partition_update(bmpsc, parent.(src.(update_pe_region))) + end + return bmpsc +end + +function default_extracter( + alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, - pe::PartitionEdge, - me::Vector{ITensor}, + update_pe_region::Vector{<:PartitionEdge}; + nsites::Int64=1, ) - p_above, p_below = partitionedge_above(bmpsc, pe), partitionedge_below(bmpsc, pe) - me = only(me) - me_prev = only(message(bmpsc, pe)) - for pe in filter(x -> !isnothing(x), [p_above, p_below]) - ind1 = commonind(me, only(message(bmpsc, reverse(pe)))) - ind2 = commonind(me_prev, only(message(bmpsc, pe))) - me *= delta(ind1, ind2) + nsites == 1 && return updated_message( + bmpsc, only(update_pe_region); message_update_function_kwargs=(; normalize=false) + ) + if nsites == 2 + pv1, pv2 = src(first(update_pe_region)), src(last(update_pe_region)) + partition = planargraph_partition(bmpsc, parent(pv1)) + g = subgraph(planargraph(bmpsc), planargraph_vertices(bmpsc, partition)) + path = a_star(g, parent(pv1), parent(pv2)) + pvs = PartitionVertex.(vcat(src.(path), [parent(pv2)])) + local_tensors = factors(bmpsc, pvs) + ms = incoming_messages(bmpsc, pvs; ignore_edges=reverse.(update_pe_region)) + return ITensor[local_tensors; ms] + else + error("Nsites > 2 not supported at the moment") end - return set_message(bmpsc, pe, ITensor[me]) end -function default_updater( - alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v +function ITensors.commonind(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge) + m1, m2 = message(bmpsc, pe1), message(bmpsc, pe2) + return commonind(only(m1), only(m2)) +end + +function virtual_index_transformers( + bmpsc::BoundaryMPSCache, pe_region::Vector{<:PartitionEdge} ) - rev_prev_pe = isnothing(prev_pe) ? nothing : reverse(prev_pe) - bmpsc = gauge(alg, bmpsc, rev_prev_pe, reverse(update_pe)) - bmpsc = partition_update(bmpsc, prev_v, cur_v) - return bmpsc + partitionpair = planargraph_partitionpair(bmpsc, first(pe_region)) + pes = planargraph_sorted_partitionedges(bmpsc, partitionpair) + sorted_pes = sort(pe_region; by=pe -> findfirst(x -> x == pe, pes)) + pe1, pe2 = first(sorted_pes), last(sorted_pes) + pe_a, pe_b = partitionedge_above(bmpsc, pe2), partitionedge_below(bmpsc, pe1) + transformers = ITensor[] + if !isnothing(pe_b) + transformers = [ + transformers + delta(commonind(bmpsc, pe_b, pe1), commonind(bmpsc, reverse(pe_b), reverse(pe1))) + ] + end + if !isnothing(pe_a) + transformers = [ + transformers + delta(commonind(bmpsc, pe_a, pe2), commonind(bmpsc, reverse(pe_a), reverse(pe2))) + ] + end + return transformers end -function default_updater( - alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, prev_pe, update_pe, prev_v, cur_v +function default_extracter( + alg::Algorithm"biorthogonal", + bmpsc::BoundaryMPSCache, + update_pe_region::Vector{<:PartitionEdge}; + nsites, ) - bmpsc = gauge(alg, bmpsc, prev_pe, update_pe) - bmpsc = partition_update(bmpsc, prev_v, cur_v) - return bmpsc + ms = default_extracter(Algorithm("orthogonal"), bmpsc, update_pe_region; nsites) + return [ms; virtual_index_transformers(bmpsc, update_pe_region)] end function default_cache_prep_function( @@ -425,22 +513,41 @@ default_tolerance(alg::Algorithm"orthogonal") = 1e-10 default_tolerance(alg::Algorithm"biorthogonal") = nothing function default_costfunction( - alg::Algorithm"orthogonal", - bmpsc::BoundaryMPSCache, - pe::PartitionEdge, - me::Vector{ITensor}, + alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, pe_region::Vector{<:PartitionEdge} ) - return region_scalar(bp_cache(bmpsc), src(pe)) / norm(only(me)) + bmpsc = copy(bmpsc) + pe = first(pe_region) + bmpsc = gauge(alg, bmpsc, reverse.(pe_region), [reverse(pe)]) + bmpsc = partition_update(bmpsc, parent.(src.(pe_region)), [parent(src(pe))]) + return region_scalar(bp_cache(bmpsc), src(pe)) / norm(only(message(bmpsc, pe))) end function default_costfunction( alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, - pe::PartitionEdge, - me::Vector{ITensor}, + pe_region::Vector{<:PartitionEdge}; + nsites::Int64=1, ) - return region_scalar(bp_cache(bmpsc), src(pe)) / - dot(only(me), only(message(bmpsc, reverse(pe)))) + bmpsc = copy(bmpsc) + pe = first(pe_region) + bmpsc = gauge(alg, bmpsc, reverse.(pe_region), [reverse(pe)]) + bmpsc = partition_update(bmpsc, parent.(src.(pe_region)), [parent(src(pe))]) + ms = [only(message(bmpsc, pe)), only(message(bmpsc, reverse(pe)))] + ms = [ms; virtual_index_transformers(bmpsc, [pe])] + return region_scalar(bp_cache(bmpsc), src(pe)) / contract(ms; sequence="automatic")[] +end + +function update_sequence( + alg::Algorithm, bmpsc::BoundaryMPSCache, partitionpair::Pair; nsites::Int64=1 +) + pes = planargraph_sorted_partitionedges(bmpsc, partitionpair) + if nsites == 1 + return vcat([[pe] for pe in pes], [[pe] for pe in reverse(pes[2:(length(pes) - 1)])]) + elseif nsites == 2 + seq = [[pes[i], pes[i + 1]] for i in 1:(length(pes) - 1)] + #TODO: Why does this not work reversing the elements of seq? + return seq + end end #Update all the message tensors on an interpartition via a specified fitting procedure @@ -452,25 +559,24 @@ function update( inserter=default_inserter, costfunction=default_costfunction, updater=default_updater, + extracter=default_extracter, cache_prep_function=default_cache_prep_function, niters::Int64=default_niters(alg), tolerance=default_tolerance(alg), normalize=true, + nsites::Int64=1, ) bmpsc = cache_prep_function(alg, bmpsc, partitionpair) - pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) - update_seq = vcat(pes, reverse(pes)[2:length(pes)]) - prev_v, prev_pe = nothing, nothing + update_seq = update_sequence(alg, bmpsc, partitionpair; nsites) prev_cf = 0 for i in 1:niters cf = 0 - for update_pe in update_seq - cur_v = parent(src(update_pe)) - bmpsc = updater(alg, bmpsc, prev_pe, update_pe, prev_v, cur_v) - me = updated_message(bmpsc, update_pe; message_update_function_kwargs=(; normalize)) - cf += costfunction(alg, bmpsc, update_pe, me) - bmpsc = inserter(alg, bmpsc, update_pe, me) - prev_v, prev_pe = cur_v, update_pe + for (j, update_pe_region) in enumerate(update_seq) + prev_pe_region = j == 1 ? nothing : update_seq[j - 1] + bmpsc = updater(alg, bmpsc, prev_pe_region, update_pe_region) + ms = extracter(alg, bmpsc, update_pe_region; nsites) + bmpsc = inserter(alg, bmpsc, update_pe_region, ms; nsites, normalize) + cf += !isnothing(tolerance) ? costfunction(alg, bmpsc, update_pe_region) : 0.0 end epsilon = abs(cf - prev_cf) / length(update_seq) if !isnothing(tolerance) && epsilon < tolerance @@ -482,23 +588,22 @@ function update( return cache_prep_function(alg, bmpsc, partitionpair) end -#Assume all vertices live in the same partition for now +#Assert all vertices live in the same partition for now function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) - pv = only(planargraph_partitions(bmpsc, vs)) - bmpsc = partition_update(bmpsc, pv) + bmpsc = partition_update(bmpsc, vs) return environment(bp_cache(bmpsc), verts; kwargs...) end function region_scalar(bmpsc::BoundaryMPSCache, partition) partition_vs = planargraph_vertices(bmpsc, partition) - bmpsc = partition_update(bmpsc, first(partition_vs), last(partition_vs)) + bmpsc = partition_update(bmpsc, [first(partition_vs)], [last(partition_vs)]) return region_scalar(bp_cache(bmpsc), PartitionVertex(last(partition_vs))) end function region_scalar(bmpsc::BoundaryMPSCache, partitionpair::Pair) - pes = planargraph_partitionpair_partitionedges(bmpsc, partitionpair) - out = ITensor(1.0) + pes = planargraph_sorted_partitionedges(bmpsc, partitionpair) + out = ITensor(one(Bool)) for pe in pes out = (out * (only(message(bmpsc, pe)))) * only(message(bmpsc, reverse(pe))) end diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index b1b66acb..4cf2cf9b 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -35,12 +35,3 @@ function insert_pseudo_planar_edges( end pair(pe::PartitionEdge) = parent(src(pe)) => parent(dst(pe)) - -#Return a sequence of pairs to go from item1 to item2 in an ordered_list -function pair_sequence(ordered_list::Vector, item1, item2) - item1_pos, item2_pos = findfirst(x -> x == item1, ordered_list), - findfirst(x -> x == item2, ordered_list) - item1_pos < item2_pos && - return [ordered_list[i] => ordered_list[i + 1] for i in item1_pos:(item2_pos - 1)] - return [ordered_list[i] => ordered_list[i - 1] for i in item1_pos:-1:(item2_pos + 1)] -end diff --git a/test/test_boundarymps.jl b/test/test_boundarymps.jl index 29fe4ad1..3c9ed9c8 100644 --- a/test/test_boundarymps.jl +++ b/test/test_boundarymps.jl @@ -80,7 +80,7 @@ using LinearAlgebra: norm g = named_grid((5, 5)) g = rem_vertices(g, [(2, 2), (3, 3)]) s = siteinds("S=1/2", g) - χ = 3 + χ = 2 ψ = random_tensornetwork(rng, elt, s; link_space=χ) ψIψ = QuadraticFormNetwork(ψ) vc = (first(center(g)), "operator") @@ -88,9 +88,14 @@ using LinearAlgebra: norm ρ_exact = contract(ρ; sequence=contraction_sequence(ρ; alg="greedy")) ρ_exact /= tr(ρ_exact) - #Orthogonal Boundary MPS, group by column (default) + #Orthogonal Boundary MPS, group by column (default), do two-site fitting ρ_boundaryMPS = contract( - environment(ψIψ, [vc]; alg="boundarymps"); sequence="automatic" + environment( + ψIψ, + [vc]; + alg="boundarymps", + cache_update_kwargs=(; message_update_kwargs=(; nsites=2)), + ), ) ρ_boundaryMPS /= tr(ρ_boundaryMPS) @@ -128,7 +133,7 @@ using LinearAlgebra: norm m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) #Prune the dimension 1 virtual index from boundary MPS message tensor m_boundarymps = - m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) + m_boundarymps * ITensor(one(Bool), filter(i -> dim(i) == 1, inds(m_boundarymps))) m_bp = only(message(bp_cache, pe)) m_bp /= tr(m_bp) m_boundarymps /= tr(m_boundarymps) @@ -143,7 +148,7 @@ using LinearAlgebra: norm m_boundarymps = only(message(ψIψ_boundaryMPS, pe)) #Prune the dimension 1 virtual index from boundary MPS message tensor m_boundarymps = - m_boundarymps * ITensor(1.0, filter(i -> dim(i) == 1, inds(m_boundarymps))) + m_boundarymps * ITensor(one(Bool), filter(i -> dim(i) == 1, inds(m_boundarymps))) m_bp = only(message(bp_cache, pe)) m_bp /= tr(m_bp) m_boundarymps /= tr(m_boundarymps) From e98a056f4cb90217290b509e6e96922d313b78b2 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 14 Jan 2025 15:15:57 -0500 Subject: [PATCH 44/47] Cleanup --- src/caches/boundarympscache.jl | 81 +++++++++++++++++------------ src/caches/boundarympscacheutils.jl | 2 - 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index 0c586978..e07fe446 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -78,6 +78,7 @@ function default_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge; kwargs...) return default_message(bp_cache(bmpsc), pe::PartitionEdge; kwargs...) end +#Get the dimension of the virtual index between the two message tensors on pe1 and pe2 function virtual_index_dimension( bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::PartitionEdge ) @@ -95,22 +96,43 @@ function virtual_index_dimension( )) end +#Vertices of the planargraph function planargraph_vertices(bmpsc::BoundaryMPSCache, partition) return vertices(ppg(bmpsc), PartitionVertex(partition)) end -function planargraph_partition(bmpsc::BoundaryMPSCache, vertex) - return parent(partitionvertex(ppg(bmpsc), vertex)) + +#Get partition(s) of vertices of the planargraph +function planargraph_partitions(bmpsc::BoundaryMPSCache, vertices::Vector) + return parent.(partitionvertices(ppg(bmpsc), vertices)) end -function planargraph_partitions(bmpsc::BoundaryMPSCache, verts) - return parent.(partitionvertices(ppg(bmpsc), verts)) + +function planargraph_partition(bmpsc::BoundaryMPSCache, vertex) + return only(planargraph_partitions(bmpsc, [vertex])) end + +#Get interpartition pairs of partition edges in the underlying partitioned tensornetwork function planargraph_partitionpair(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return pair(partitionedge(ppg(bmpsc), parent(pe))) end -function planargraph_sorted_partitionedges(bmpsc::BoundaryMPSCache, partition_pair::Pair) - return sorted_partitionedges(ppg(bmpsc), partition_pair) + +#Sort (bottom to top) partitoonedges between pair of partitions in the planargraph +function planargraph_sorted_partitionedges(bmpsc::BoundaryMPSCache, partitionpair::Pair) + pg = ppg(bmpsc) + src_vs, dst_vs = vertices(pg, PartitionVertex(first(partitionpair))), + vertices(pg, PartitionVertex(last(partitionpair))) + es = reduce( + vcat, + [ + [src_v => dst_v for dst_v in intersect(neighbors(pg, src_v), dst_vs)] for + src_v in src_vs + ], + ) + es = sort(NamedEdge.(es); by=x -> findfirst(isequal(src(x)), src_vs)) + return PartitionEdge.(es) end +#Constructor, inserts missing edge in the planar graph to ensure each partition is connected +#allowing the code to work for arbitrary grids and not just square grids function BoundaryMPSCache( bpc::BeliefPropagationCache; grouping_function::Function=v -> first(v), @@ -130,22 +152,6 @@ function BoundaryMPSCache(tn, args...; kwargs...) return BoundaryMPSCache(BeliefPropagationCache(tn, args...); kwargs...) end -#Get all partitionedges between the pair of neighboring partitions, sorted -#by the position of the source in the pg -function sorted_partitionedges(pg::PartitionedGraph, partitionpair::Pair) - src_vs, dst_vs = vertices(pg, PartitionVertex(first(partitionpair))), - vertices(pg, PartitionVertex(last(partitionpair))) - es = reduce( - vcat, - [ - [src_v => dst_v for dst_v in intersect(neighbors(pg, src_v), dst_vs)] for - src_v in src_vs - ], - ) - es = sort(NamedEdge.(es); by=x -> findfirst(isequal(src(x)), src_vs)) - return PartitionEdge.(es) -end - #Functions to get the parellel partitionedges sitting above and below a partitionedge function partitionedges_above(bmpsc::BoundaryMPSCache, pe::PartitionEdge) pes = planargraph_sorted_partitionedges(bmpsc, planargraph_partitionpair(bmpsc, pe)) @@ -171,8 +177,8 @@ function partitionedge_below(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return last(pes_below) end -#Given a sequence of message tensor updates in a partition, get the sequence of gauge moves needed to move the MPS gauge -#from the start of the sequence to the end of the sequence +#Given a sequence of message tensor updates within a partition, get the sequence of gauge moves on the interpartition +# needed to move the MPS gauge from the start of the sequence to the end of the sequence function mps_gauge_update_sequence( bmpsc::BoundaryMPSCache, partition_update_seq::Vector{<:PartitionEdge}, @@ -190,7 +196,7 @@ function mps_gauge_update_sequence( ] end -#Get the sequence of pairs partitionedges that need to be updated to move the MPS gauge from one region of partitionedges to another +#Returns the sequence of pairs of partitionedges that need to be updated to move the MPS gauge between regions function mps_gauge_update_sequence( bmpsc::BoundaryMPSCache, pe_region1::Vector{<:PartitionEdge}, @@ -216,7 +222,7 @@ function mps_gauge_update_sequence(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return mps_gauge_update_sequence(bmpsc, [pe]) end -#Initialise all the message tensors for the pairs of neighboring partitions +#Initialise all the interpartition message tensors function set_interpartition_messages( bmpsc::BoundaryMPSCache, partitionpairs::Vector{<:Pair} ) @@ -238,13 +244,12 @@ function set_interpartition_messages( return bmpsc end -#Initialise all the interpartition message tensors function set_interpartition_messages(bmpsc::BoundaryMPSCache) partitionpairs = pair.(partitionedges(ppg(bmpsc))) return set_interpartition_messages(bmpsc, vcat(partitionpairs, reverse.(partitionpairs))) end -#Switch the message on partition edge pe with its reverse (and dagger them) +#Switch the message tensors on partition edges with their reverse (and dagger them) function switch_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge) bmpsc = copy(bmpsc) ms = messages(bmpsc) @@ -254,7 +259,6 @@ function switch_message(bmpsc::BoundaryMPSCache, pe::PartitionEdge) return bmpsc end -#Switch the message tensors from partitionpair i -> i + 1 with those from i + 1 -> i function switch_messages(bmpsc::BoundaryMPSCache, partitionpair::Pair) for pe in planargraph_sorted_partitionedges(bmpsc, partitionpair) bmpsc = switch_message(bmpsc, pe) @@ -271,7 +275,7 @@ function partition_update_sequence(bmpsc::BoundaryMPSCache, partition) ) end -#Get sequence necessary to move correct message tensors in a partition from region1 to region 2 +#Get sequence necessary to move correct message tensors in a partition from region1 to region2 function partition_update_sequence( bmpsc::BoundaryMPSCache, region1::Vector, region2::Vector ) @@ -284,7 +288,7 @@ function partition_update_sequence( return PartitionEdge.(path) end -#Get sequence necessary to move correct message tensors to region +#Get sequence necessary to move correct message tensors to a region function partition_update_sequence(bmpsc::BoundaryMPSCache, region::Vector) pv = planargraph_partition(bmpsc, first(region)) return partition_update_sequence(bmpsc, planargraph_vertices(bmpsc, pv), region) @@ -386,6 +390,7 @@ default_inserter_transform(alg::Algorithm"orthogonal") = dag default_region_transform(alg::Algorithm"biorthogonal") = identity default_region_transform(alg::Algorithm"orthogonal") = reverse +#Default inserter for the MPS fitting (one and two-site support) function default_inserter( alg::Algorithm, bmpsc::BoundaryMPSCache, @@ -419,6 +424,7 @@ function default_inserter( return bmpsc end +#Default updater for the MPS fitting function default_updater( alg::Algorithm, bmpsc::BoundaryMPSCache, prev_pe_region, update_pe_region ) @@ -434,6 +440,7 @@ function default_updater( return bmpsc end +#Default extracter for the MPS fitting (1 and two-site support) function default_extracter( alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, @@ -462,6 +469,8 @@ function ITensors.commonind(bmpsc::BoundaryMPSCache, pe1::PartitionEdge, pe2::Pa return commonind(only(m1), only(m2)) end +#Transformers for switching the virtual index of message tensors on boundary of pe_region +# to those of their reverse function virtual_index_transformers( bmpsc::BoundaryMPSCache, pe_region::Vector{<:PartitionEdge} ) @@ -486,6 +495,7 @@ function virtual_index_transformers( return transformers end +#Biorthogonal extracter for MPS fitting (needs virtual index transformer) function default_extracter( alg::Algorithm"biorthogonal", bmpsc::BoundaryMPSCache, @@ -512,6 +522,7 @@ default_niters(alg::Algorithm"biorthogonal") = 3 default_tolerance(alg::Algorithm"orthogonal") = 1e-10 default_tolerance(alg::Algorithm"biorthogonal") = nothing +#Cost functions function default_costfunction( alg::Algorithm"orthogonal", bmpsc::BoundaryMPSCache, pe_region::Vector{<:PartitionEdge} ) @@ -537,6 +548,7 @@ function default_costfunction( return region_scalar(bp_cache(bmpsc), src(pe)) / contract(ms; sequence="automatic")[] end +#Sequences function update_sequence( alg::Algorithm, bmpsc::BoundaryMPSCache, partitionpair::Pair; nsites::Int64=1 ) @@ -550,8 +562,7 @@ function update_sequence( end end -#Update all the message tensors on an interpartition via a specified fitting procedure -#TODO: Make two-site possible +#Update all the message tensors on an interpartition via an n-site fitting procedure function update( alg::Algorithm, bmpsc::BoundaryMPSCache, @@ -579,6 +590,7 @@ function update( cf += !isnothing(tolerance) ? costfunction(alg, bmpsc, update_pe_region) : 0.0 end epsilon = abs(cf - prev_cf) / length(update_seq) + @show cf if !isnothing(tolerance) && epsilon < tolerance return cache_prep_function(alg, bmpsc, partitionpair) else @@ -588,13 +600,14 @@ function update( return cache_prep_function(alg, bmpsc, partitionpair) end -#Assert all vertices live in the same partition for now +#Environment support, assume all vertices live in the same partition for now function ITensorNetworks.environment(bmpsc::BoundaryMPSCache, verts::Vector; kwargs...) vs = parent.((partitionvertices(bp_cache(bmpsc), verts))) bmpsc = partition_update(bmpsc, vs) return environment(bp_cache(bmpsc), verts; kwargs...) end +#Region scalars, allowing computation of the free energy within boundary MPS function region_scalar(bmpsc::BoundaryMPSCache, partition) partition_vs = planargraph_vertices(bmpsc, partition) bmpsc = partition_update(bmpsc, [first(partition_vs)], [last(partition_vs)]) diff --git a/src/caches/boundarympscacheutils.jl b/src/caches/boundarympscacheutils.jl index 4cf2cf9b..48dd3f36 100644 --- a/src/caches/boundarympscacheutils.jl +++ b/src/caches/boundarympscacheutils.jl @@ -1,7 +1,6 @@ using ITensorNetworks: ITensorNetworks, BeliefPropagationCache using NamedGraphs.PartitionedGraphs: PartitionedGraph -#Add partition edges that may not have meaning in the underlying graph function add_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) g = partitioned_graph(pg) g = add_edges(g, parent.(pes)) @@ -10,7 +9,6 @@ function add_partitionedges(pg::PartitionedGraph, pes::Vector{<:PartitionEdge}) ) end -#Add partition edges that may not have meaning in the underlying graph function add_partitionedges(bpc::BeliefPropagationCache, pes::Vector{<:PartitionEdge}) pg = add_partitionedges(partitioned_tensornetwork(bpc), pes) return BeliefPropagationCache(pg, messages(bpc)) From 5b9266e83c401c6788819012d91cee257661d215 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 15 Jan 2025 12:23:13 -0500 Subject: [PATCH 45/47] Remove @show --- src/caches/boundarympscache.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/caches/boundarympscache.jl b/src/caches/boundarympscache.jl index e07fe446..0b965992 100644 --- a/src/caches/boundarympscache.jl +++ b/src/caches/boundarympscache.jl @@ -590,7 +590,6 @@ function update( cf += !isnothing(tolerance) ? costfunction(alg, bmpsc, update_pe_region) : 0.0 end epsilon = abs(cf - prev_cf) / length(update_seq) - @show cf if !isnothing(tolerance) && epsilon < tolerance return cache_prep_function(alg, bmpsc, partitionpair) else From 0228aa1118478debc0e336a4bc9517e59631a596 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 16 Jan 2025 08:56:11 -0500 Subject: [PATCH 46/47] Fix Bug in Expect --- src/expect.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/expect.jl b/src/expect.jl index 3414dd21..69ce42ae 100644 --- a/src/expect.jl +++ b/src/expect.jl @@ -25,13 +25,12 @@ function ITensorMPS.expect( (cache!)=nothing, update_cache=isnothing(cache!), cache_update_kwargs=default_cache_update_kwargs(alg), - cache_construction_function=tn -> - cache(alg, tn; default_cache_construction_kwargs(alg, tn)...), + cache_construction_kwargs=default_cache_construction_kwargs(alg, inner_network(ψ, ψ)), kwargs..., ) ψIψ = inner_network(ψ, ψ) if isnothing(cache!) - cache! = Ref(cache_construction_function(ψIψ)) + cache! = Ref(cache(alg, ψIψ; cache_construction_kwargs...)) end if update_cache From 1fcf9479b154356214098b915f19d4ad1e1851c3 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 16 Jan 2025 09:29:25 -0500 Subject: [PATCH 47/47] Fix expect test --- test/test_expect.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/test_expect.jl b/test/test_expect.jl index 75ed8504..c654138e 100644 --- a/test/test_expect.jl +++ b/test/test_expect.jl @@ -29,11 +29,13 @@ using Test: @test, @testset s = siteinds("S=1/2", g) rng = StableRNG(1234) ψ = random_tensornetwork(rng, s; link_space=χ) - cache_construction_function = - f -> BeliefPropagationCache( - f; partitioned_vertices=group(v -> (original_state_vertex(f, v)[1]), vertices(f)) - ) - sz_bp = expect(ψ, "Sz"; alg="bp", cache_construction_function) + quadratic_form_vertices = reduce( + vcat, [[(v, "ket"), (v, "bra"), (v, "operator")] for v in vertices(ψ)] + ) + cache_construction_kwargs = (; + partitioned_vertices=group(v -> first(first(v)), quadratic_form_vertices) + ) + sz_bp = expect(ψ, "Sz"; alg="bp", cache_construction_kwargs) sz_exact = expect(ψ, "Sz"; alg="exact") @test sz_bp ≈ sz_exact