From 85fbd0c1cae6d3ec0ef96ce595b3845c40881eb9 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 11 Jan 2024 10:58:21 -0500 Subject: [PATCH 01/12] Removed Partitioning Interface. Added PartitionedITensorNetwork type --- src/ITensorNetworks.jl | 2 +- src/exports.jl | 1 + src/partition.jl | 341 ------------------------- src/partitioneditensornetwork.jl | 3 + test/test_partitioneditensornetwork.jl | 28 ++ 5 files changed, 33 insertions(+), 342 deletions(-) delete mode 100644 src/partition.jl create mode 100644 src/partitioneditensornetwork.jl create mode 100644 test/test_partitioneditensornetwork.jl diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 5b660bc9..11aa8ede 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -70,7 +70,7 @@ include("observers.jl") include("visualize.jl") include("graphs.jl") include("itensors.jl") -include("partition.jl") +include("partitioneditensornetwork.jl") include("lattices.jl") include("abstractindsnetwork.jl") include("indextags.jl") diff --git a/src/exports.jl b/src/exports.jl index 5f08950e..29fa2daf 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -71,6 +71,7 @@ export AbstractITensorNetwork, TreeTensorNetwork, TTN, random_ttn, + PartitionedITensorNetwork, ProjTTN, ProjTTNSum, ProjTTNApply, diff --git a/src/partition.jl b/src/partition.jl deleted file mode 100644 index 0709b77d..00000000 --- a/src/partition.jl +++ /dev/null @@ -1,341 +0,0 @@ -""" -Graph partitioning backend -""" -struct Backend{T} end - -Backend(s::Symbol) = Backend{s}() -Backend(s::String) = Backend(Symbol(s)) -Backend(backend::Backend) = backend - -macro Backend_str(s) - return :(Backend{$(Expr(:quote, Symbol(s)))}) -end - -""" -Current default graph partitioning backend -""" -const CURRENT_PARTITIONING_BACKEND = Ref{Union{Missing,Backend}}(missing) - -""" -Get the graph partitioning backend -""" -current_partitioning_backend() = CURRENT_PARTITIONING_BACKEND[] - -""" -Set the graph partitioning backend -""" -function set_partitioning_backend!(backend::Union{Missing,Backend,String}) - CURRENT_PARTITIONING_BACKEND[] = Backend(backend) - return nothing -end - -# KaHyPar configuration options -# -# configurations = readdir(joinpath(pkgdir(KaHyPar), "src", "config")) -# "cut_kKaHyPar_sea20.ini" -# "cut_rKaHyPar_sea20.ini" -# "km1_kKaHyPar-E_sea20.ini" -# "km1_kKaHyPar_eco_sea20.ini" -# "km1_kKaHyPar_sea20.ini" -# "km1_rKaHyPar_sea20.ini" -# -const kahypar_configurations = Dict([ - (objective="edge_cut", alg="kway") => "cut_kKaHyPar_sea20.ini", - (objective="edge_cut", alg="recursive") => "cut_rKaHyPar_sea20.ini", - (objective="connectivity", alg="kway") => "km1_kKaHyPar_sea20.ini", - (objective="connectivity", alg="recursive") => "km1_rKaHyPar_sea20.ini", -]) - -# Metis configuration options -const metis_algs = Dict(["kway" => :KWAY, "recursive" => :RECURSIVE]) - -function _npartitions( - g::AbstractGraph, npartitions::Integer, nvertices_per_partition::Nothing -) - return npartitions -end - -function _npartitions( - g::AbstractGraph, npartitions::Nothing, nvertices_per_partition::Integer -) - return nv(g) ÷ nvertices_per_partition -end - -function _npartitions(g::AbstractGraph, npartitions::Int, nvertices_per_partition::Int) - return error("Can't specify both `npartitions` and `nvertices_per_partition`") -end - -function _npartitions( - g::AbstractGraph, npartitions::Nothing, nvertices_per_partition::Nothing -) - return error("Must specify either `npartitions` or `nvertices_per_partition`") -end - -function subgraph_vertices( - g::Graph; - npartitions=nothing, - nvertices_per_partition=nothing, - backend=current_partitioning_backend(), - kwargs..., -) - #Metis cannot handle the edge case npartitions = 1, so we will fix it here for now - if (_npartitions(g, npartitions, nvertices_per_partition) == 1) - return group(v -> 1, collect(vertices(g))) - end - - return subgraph_vertices( - Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... - ) -end - -function subgraph_vertices( - g::NamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - vertex_partitions = subgraph_vertices( - parent_graph(g); npartitions, nvertices_per_partition, kwargs... - ) - #[inv(vertex_to_parent_vertex(g))[v] for v in partitions] - # TODO: output the reverse of this dictionary (a Vector of Vector - # of the vertices in each partition). - # return Dictionary(vertices(g), partitions) - return [ - parent_vertices_to_vertices(g, vertex_partition) for - vertex_partition in vertex_partitions - ] -end - -function subgraph_vertices( - g::AbstractDataGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return subgraph_vertices( - underlying_graph(g); npartitions, nvertices_per_partition, kwargs... - ) -end - -""" - partition_vertices(g::AbstractGraph, subgraph_vertices::Vector) - -Given a graph (`g`) and groups of vertices defining subgraphs of that -graph (`subgraph_vertices`), return a DataGraph storing the subgraph -vertices on the vertices of the graph and with edges denoting -which subgraphs of the original graph have edges connecting them, along with -edge data storing the original edges that were connecting the subgraphs. -""" -function partition_vertices(g::AbstractGraph, subgraph_vertices) - partitioned_vertices = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), Dictionary(subgraph_vertices) - ) - for e in edges(g) - s1 = findfirst_on_vertices( - subgraph_vertices -> src(e) ∈ subgraph_vertices, partitioned_vertices - ) - s2 = findfirst_on_vertices( - subgraph_vertices -> dst(e) ∈ subgraph_vertices, partitioned_vertices - ) - if (!has_edge(partitioned_vertices, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_vertices, s1, s2) - partitioned_vertices[s1 => s2] = Vector{edgetype(g)}() - end - if has_edge(partitioned_vertices, s1, s2) - push!(partitioned_vertices[s1 => s2], e) - end - end - return partitioned_vertices -end - -""" - partition_vertices(g::AbstractGraph; npartitions, nvertices_per_partition, kwargs...) - -Given a graph `g`, partition the vertices of `g` into 'npartitions' partitions -or into partitions with `nvertices_per_partition` vertices per partition. -Try to keep all subgraphs the same size and minimise edges cut between them -Returns a datagraph where each vertex contains the list of vertices involved in that subgraph. The edges state which subgraphs are connected. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function partition_vertices( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return partition_vertices( - g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...) - ) -end - -""" -Find all vertices `v` such that `f(graph[v]) == true` -""" -function findall_on_vertices(f::Function, graph::AbstractDataGraph) - return findall(f, vertex_data(graph)) -end - -""" -Find the vertex `v` such that `f(graph[v]) == true` -""" -function findfirst_on_vertices(f::Function, graph::AbstractDataGraph) - return findfirst(f, vertex_data(graph)) -end - -""" -Find all edges `e` such that `f(graph[e]) == true` -""" -function findall_on_edges(f::Function, graph::AbstractDataGraph) - return findall(f, edge_data(graph)) -end - -""" -Find the edge `e` such that `f(graph[e]) == true` -""" -function findfirst_on_edges(f::Function, graph::AbstractDataGraph) - return findfirst(f, edge_data(graph)) -end - -""" -Find the subgraph which contains the specified vertex. - -TODO: Rename something more general, like: - -findfirst_in_vertex_data(item, graph::AbstractDataGraph) -""" -function find_subgraph(vertex, subgraphs::DataGraph) - return findfirst_on_vertices(subgraph -> vertex ∈ vertices(subgraph), subgraphs) -end - -function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) - return subgraphs(NamedGraph(g), subgraph_vertices) -end - -""" - subgraphs(g::AbstractGraph, subgraph_vertices) - -Return a collection of subgraphs of `g` defined by the subgraph -vertices `subgraph_vertices`. -""" -function subgraphs(g::AbstractGraph, subgraph_vertices) - return map(vs -> subgraph(g, vs), subgraph_vertices) -end - -""" - subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition, -returning a list of subgraphs. -Try to keep all subgraphs the same size and minimise edges cut between them. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function subgraphs( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) -end - -function partition(g::AbstractSimpleGraph, subgraph_vertices) - return partition(NamedGraph(g), subgraph_vertices) -end - -function partition(g::AbstractGraph, subgraph_vertices) - partitioned_graph = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), subgraphs(g, Dictionary(subgraph_vertices)) - ) - for e in edges(g) - s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) - s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) - if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_graph, s1, s2) - partitioned_graph[s1 => s2] = Dictionary( - [:edges, :edge_data], - [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], - ) - end - if has_edge(partitioned_graph, s1, s2) - push!(partitioned_graph[s1 => s2][:edges], e) - if isassigned(g, e) - set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) - end - end - end - return partitioned_graph -end - -""" - partition(g::AbstractGraph; npartitions::Integer, kwargs...) - partition(g::AbstractGraph, subgraph_vertices) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition. -The partitioning tries to keep all subgraphs the same size and minimize -edges cut between them. - -Alternatively, specify a desired partitioning with a collection of sugraph -vertices. - -Returns a data graph where each vertex contains the corresponding subgraph as vertex data. -The edges indicates which subgraphs are connected, and the edge data stores a dictionary -with two fields. The field `:edges` stores a list of the edges of the original graph -that were connecting the two subgraphs, and `:edge_data` stores a dictionary -mapping edges of the original graph to the data living on the edges of the original -graph, if it existed. - -Therefore, one should be able to extract that data and recreate the original -graph from the results of `partition`. - -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work -if the subgraph vertices aren't specified explicitly. -""" -function partition( - g::AbstractGraph; - npartitions=nothing, - nvertices_per_partition=nothing, - subgraph_vertices=nothing, - kwargs..., -) - if count(isnothing, (npartitions, nvertices_per_partition, subgraph_vertices)) != 2 - error( - "Error: Cannot give multiple/ no partitioning options. Please specify exactly one." - ) - end - - if isnothing(subgraph_vertices) - subgraph_vertices = ITensorNetworks.subgraph_vertices( - g; npartitions, nvertices_per_partition, kwargs... - ) - end - - return partition(g, subgraph_vertices) -end - -"""Given a nested graph fetch all the vertices down to the lowest levels and return the grouping -at the highest level. Keyword argument is used to state whether we are at the top""" -function nested_graph_leaf_vertices(g; toplevel=true) - vertex_groups = [] - for v in vertices(g) - if hasmethod(vertices, Tuple{typeof(g[v])}) - if !toplevel - push!(vertex_groups, nested_graph_leaf_vertices(g[v]; toplevel=false)...) - else - push!(vertex_groups, nested_graph_leaf_vertices(g[v]; toplevel=false)) - end - else - push!(vertex_groups, [v]...) - end - end - - return vertex_groups -end - -""" - TODO: do we want to make it a public function? -""" -function _noncommoninds(partition::DataGraph) - networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] - network = vcat(networks...) - return noncommoninds(network...) -end - -# Util functions for partition -function _commoninds(partition::DataGraph) - networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] - network = vcat(networks...) - outinds = noncommoninds(network...) - allinds = mapreduce(t -> [i for i in inds(t)], vcat, network) - return Vector(setdiff(allinds, outinds)) -end diff --git a/src/partitioneditensornetwork.jl b/src/partitioneditensornetwork.jl new file mode 100644 index 00000000..4cddbe27 --- /dev/null +++ b/src/partitioneditensornetwork.jl @@ -0,0 +1,3 @@ +const PartitionedITensorNetwork{V, PV} = PartitionedGraph + +NamedGraphs.parent_graph(pitn::PartitionedITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(unpartitioned_graph(pitn))) diff --git a/test/test_partitioneditensornetwork.jl b/test/test_partitioneditensornetwork.jl new file mode 100644 index 00000000..670c700d --- /dev/null +++ b/test/test_partitioneditensornetwork.jl @@ -0,0 +1,28 @@ +using Dictionaries +using Distributions +using GraphsFlows +using ITensors +using ITensorNetworks +using NamedGraphs +using Random +using Test +using SplitApplyCombine + +using NamedGraphs: which_partition, parent_graph + +@testset "PartitionedITensorNetwork" begin + g_dims = (1, 6) + g = named_grid(g_dims) + s = siteinds("S=1/2", g) + χ = 4 + Random.seed!(1234) + ψ = randomITensorNetwork(s; link_space=χ) + ψψ = ψ ⊗ prime(dag(ψ); sites=[]) + subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) + + pψψ = PartitionedITensorNetwork(ψψ, subgraph_vertices) + + @show which_partition(pψψ, ((1,1),1)) + + +end \ No newline at end of file From 4c237f59a866be2482df036c3c0b1ec2ca42fd32 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 11 Jan 2024 16:03:33 -0500 Subject: [PATCH 02/12] Reworked BP Interface for PartitionedITensorNetworks --- examples/belief_propagation/bpexample.jl | 77 +++----- src/ITensorNetworks.jl | 1 + src/beliefpropagation/beliefpropagation.jl | 164 ++++++----------- src/partition.jl | 198 +++++++++++++++++++++ test/test_binary_tree_partition.jl | 2 + test/test_partitioneditensornetwork.jl | 15 +- 6 files changed, 292 insertions(+), 165 deletions(-) create mode 100644 src/partition.jl diff --git a/examples/belief_propagation/bpexample.jl b/examples/belief_propagation/bpexample.jl index a0af8818..72ccd798 100644 --- a/examples/belief_propagation/bpexample.jl +++ b/examples/belief_propagation/bpexample.jl @@ -10,8 +10,7 @@ using ITensorNetworks: belief_propagation, approx_network_region, contract_inner, - message_tensors, - nested_graph_leaf_vertices + message_tensors function main() n = 4 @@ -31,41 +30,29 @@ function main() v = (1, 1) #Now do Simple Belief Propagation to Measure Sz on Site v - mts = message_tensors( - ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) - ) - - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact"), niters=20) - - numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork([apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) - sz_bp = contract(numerator_network)[] / contract(denominator_network)[] + pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + numerator_tensors = approx_network_region( + pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + sz_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] println( "Simple Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_bp) ) - #Now do General Belief Propagation to Measure Sz on Site v - nsites = 4 - Zp = partition( - partition(ψψ, group(v -> v[1], vertices(ψψ))); nvertices_per_partition=nsites - ) - Zpp = partition(ψψ; subgraph_vertices=nested_graph_leaf_vertices(Zp)) - mts = message_tensors(Zpp) - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact"), niters=20) - numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork([apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) - sz_bp = contract(numerator_network)[] / contract(denominator_network)[] + #Now do Column-wise General Belief Propagation to Measure Sz on Site v + pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + numerator_tensors = approx_network_region( + pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + sz_gen_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] println( - "General Belief Propagation (4-site subgraphs) Gives Sz on Site " * - string(v) * - " as " * - string(sz_bp), + "General Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_gen_bp) ) #Now do General Belief Propagation with Matrix Product State Message Tensors Measure Sz on Site v @@ -78,30 +65,16 @@ function main() ψψ = combine_linkinds(ψψ, combiners) ψOψ = combine_linkinds(ψOψ, combiners) - Z = partition(ψψ, group(v -> v[1], vertices(ψψ))) - maxdim = 8 - mts = message_tensors(Z) - - mts = belief_propagation( - ψψ, - mts; - contract_kwargs=(; - alg="density_matrix", - output_structure=path_graph_structure, - maxdim, - contraction_sequence_alg="optimal", - ), - ) - - numerator_network = approx_network_region(ψψ, mts, [v]; verts_tn=ITensorNetwork(ψOψ[v])) - denominator_network = approx_network_region(ψψ, mts, [v]) - sz_bp = contract(numerator_network)[] / contract(denominator_network)[] + pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ; itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e]) + mts = belief_propagation(pψψ, mts; contract_kwargs=(;alg="density_matrix",output_structure=path_graph_structure,maxdim = 8,contraction_sequence_alg="optimal")) + numerator_tensors = approx_network_region( + pψψ, mts, [v]; verts_tn=[ψOψ[v]]) + denominator_tensors = approx_network_region(pψψ, mts, [v]) + sz_MPS_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] println( - "General Belief Propagation with Column Partitioning and MPS Message Tensors (Max dim 8) Gives Sz on Site " * - string(v) * - " as " * - string(sz_bp), + "Column-Wise MPS Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_gen_bp) ) #Now do it exactly diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 11aa8ede..dafb3586 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -70,6 +70,7 @@ include("observers.jl") include("visualize.jl") include("graphs.jl") include("itensors.jl") +include("partition.jl") include("partitioneditensornetwork.jl") include("lattices.jl") include("abstractindsnetwork.jl") diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 8a1f17b4..744fae5c 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -1,104 +1,64 @@ function message_tensors( - tn::ITensorNetwork; - nvertices_per_partition=nothing, - npartitions=nothing, - subgraph_vertices=nothing, - kwargs..., + ptn::PartitionedITensorNetwork; + itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] ) - return message_tensors( - partition(tn; nvertices_per_partition, npartitions, subgraph_vertices); kwargs... - ) -end - -function message_tensors_skeleton(subgraphs::DataGraph) - mts = DataGraph{vertextype(subgraphs),vertex_data_type(subgraphs),ITensorNetwork}( - directed_graph(underlying_graph(subgraphs)) - ) - for v in vertices(mts) - mts[v] = subgraphs[v] - end - - return mts -end - -function message_tensors( - subgraphs::DataGraph; - itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e], -) - mts = message_tensors_skeleton(subgraphs) - for e in edges(subgraphs) - inds_e = commoninds(subgraphs[src(e)], subgraphs[dst(e)]) - itensors = itensor_constructor(inds_e) - mts[e] = ITensorNetwork(itensors) - mts[reverse(e)] = dag(mts[e]) + mts = Dict() + for e in PartitionEdge.(edges(partitioned_graph(ptn))) + src_e_itn = unpartitioned_graph(subgraph(ptn, [src(e)])) + dst_e_itn = unpartitioned_graph(subgraph(ptn, [dst(e)])) + inds_e = commoninds(src_e_itn, dst_e_itn) + mts[e] = itensor_constructor(inds_e) + mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = dag.(mts[e]) end return mts end """ -DO a single update of a message tensor using the current subgraph and the incoming mts +Do a single update of a message tensor using the current subgraph and the incoming mts """ function update_message_tensor( - tn::ITensorNetwork, - subgraph_vertices::Vector, - mts::Vector{ITensorNetwork}; + ptn::PartitionedITensorNetwork, + edge::PartitionEdge, + mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), ) - mts_itensors = reduce(vcat, ITensor.(mts); init=ITensor[]) + incoming_messages = [ + mts[PartitionEdge(e_in)] for e_in in setdiff(boundary_edges(partitioned_graph(ptn), [NamedGraphs.parent(src(edge))]; dir=:in), [reverse(NamedGraphs.parent(edge))]) + ] + incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) - contract_list = ITensor[mts_itensors; ITensor[tn[v] for v in subgraph_vertices]] - tn = if isone(length(contract_list)) - copy(only(contract_list)) - else - ITensorNetwork(contract_list) - end + contract_list = ITensor[incoming_messages; ITensor(unpartitioned_graph(subgraph(ptn, [src(edge)])))] if contract_kwargs.alg != "exact" - contract_output = contract(tn; contract_kwargs...) + mt, _ = contract(ITensorNetwork(contract_list); contract_kwargs...) else - contract_output = contract(tn; sequence=contraction_sequence(tn; alg="optimal")) + mt = contract(contract_list; sequence=contraction_sequence(contract_list; alg="optimal")) end - itn = if typeof(contract_output) == ITensor - ITensorNetwork(contract_output) - else - first(contract_output) - end - normalize!.(vertex_data(itn)) + mt = ITensor(mt) + normalize!(mt) - return itn -end - -function update_message_tensor( - tn::ITensorNetwork, subgraph::ITensorNetwork, mts::Vector{ITensorNetwork}; kwargs... -) - return update_message_tensor(tn, vertices(subgraph), mts; kwargs...) + return mt end """ Do a sequential update of message tensors on `edges` for a given ITensornetwork and its partition into sub graphs """ function belief_propagation_iteration( - tn::ITensorNetwork, - mts::DataGraph, - edges::Vector{<:AbstractEdge}; + ptn::PartitionedITensorNetwork, + mts, + edges::Vector{<:PartitionEdge}; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), compute_norm=false, ) new_mts = copy(mts) c = 0 for e in edges - environment_tensornetworks = ITensorNetwork[ - new_mts[e_in] for - e_in in setdiff(boundary_edges(new_mts, [src(e)]; dir=:in), [reverse(e)]) - ] - new_mts[src(e) => dst(e)] = update_message_tensor( - tn, new_mts[src(e)], environment_tensornetworks; contract_kwargs - ) + new_mts[e] = update_message_tensor(ptn, e, new_mts; contract_kwargs) if compute_norm - LHS, RHS = ITensors.contract(ITensor(mts[src(e) => dst(e)])), - ITensors.contract(ITensor(new_mts[src(e) => dst(e)])) + LHS, RHS = ITensors.contract(mts[e]), + ITensors.contract(new_mts[e]) LHS /= sum(diag(LHS)) RHS /= sum(diag(RHS)) c += 0.5 * norm(denseblocks(LHS) - denseblocks(RHS)) @@ -113,9 +73,9 @@ Currently we send the full message tensor data struct to belief_propagation_iter mts relevant to that subgraph. """ function belief_propagation_iteration( - tn::ITensorNetwork, - mts::DataGraph, - edge_groups::Vector{<:Vector{<:AbstractEdge}}; + ptn::PartitionedITensorNetwork, + mts, + edge_groups::Vector{<:Vector{<:PartitionEdge}}; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), compute_norm=false, ) @@ -123,7 +83,7 @@ function belief_propagation_iteration( c = 0 for edges in edge_groups updated_mts, ct = belief_propagation_iteration( - tn, mts, edges; contract_kwargs, compute_norm + ptn, mts, edges; contract_kwargs, compute_norm ) for e in edges new_mts[e] = updated_mts[e] @@ -134,22 +94,22 @@ function belief_propagation_iteration( end function belief_propagation_iteration( - tn::ITensorNetwork, - mts::DataGraph; + ptn::PartitionedITensorNetwork, + mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), compute_norm=false, - edges=edge_sequence(mts), + edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), ) - return belief_propagation_iteration(tn, mts, edges; contract_kwargs, compute_norm) + return belief_propagation_iteration(ptn, mts, edges; contract_kwargs, compute_norm) end function belief_propagation( - tn::ITensorNetwork, - mts::DataGraph; + ptn::PartitionedITensorNetwork, + mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), - niters=default_bp_niters(mts), + niters=default_bp_niters(partitioned_graph(ptn)), target_precision=nothing, - edges=edge_sequence(mts), + edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), verbose=false, ) compute_norm = !isnothing(target_precision) @@ -157,7 +117,7 @@ function belief_propagation( error("You need to specify a number of iterations for BP!") end for i in 1:niters - mts, c = belief_propagation_iteration(tn, mts, edges; contract_kwargs, compute_norm) + mts, c = belief_propagation_iteration(ptn, mts, edges; contract_kwargs, compute_norm) if compute_norm && c <= target_precision if verbose println("BP converged to desired precision after $i iterations.") @@ -168,36 +128,22 @@ function belief_propagation( return mts end -function belief_propagation( - tn::ITensorNetwork; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), - nvertices_per_partition=nothing, - npartitions=nothing, - subgraph_vertices=nothing, - niters=default_bp_niters(mts), - target_precision=nothing, - verbose=false, -) - mts = message_tensors(tn; nvertices_per_partition, npartitions, subgraph_vertices) - return belief_propagation(tn, mts; contract_kwargs, niters, target_precision, verbose) -end - """ Given a subet of vertices of a given Tensor Network and the Message Tensors for that network, return a Dictionary with the involved subgraphs as keys and the vector of tensors associated with that subgraph as values Specifically, the contraction of the environment tensors and tn[vertices] will be a scalar. """ -function get_environment(tn::ITensorNetwork, mts::DataGraph, verts::Vector; dir=:in) - subgraphs = unique([find_subgraph(v, mts) for v in verts]) +function get_environment(ptn::PartitionedITensorNetwork, mts, verts::Vector; dir=:in) + partition_vertices = unique([NamedGraphs.which_partition(ptn, v) for v in verts]) if dir == :out - return get_environment(tn, mts, setdiff(vertices(tn), verts)) + return get_environment(ptn, mts, setdiff(vertices(ptn), verts)) end - env_tns = ITensorNetwork[mts[e] for e in boundary_edges(mts, subgraphs; dir=:in)] - central_tn = ITensorNetwork( - ITensor[tn[v] for v in setdiff(flatten([vertices(mts[s]) for s in subgraphs]), verts)] - ) - return ITensorNetwork(vcat(env_tns, ITensorNetwork[central_tn])) + env_tensors = [mts[PartitionEdge(e)] for e in boundary_edges(partitioned_graph(ptn), NamedGraphs.parent.(partition_vertices); dir=:in)] + env_tensors = reduce(vcat, env_tensors; init = ITensor[]) + central_tensors = ITensor[(unpartitioned_graph(ptn))[v] for v in setdiff(vertices(ptn, partition_vertices), verts)] + + return vcat(env_tensors, central_tensors) end """ @@ -205,12 +151,12 @@ Calculate the contraction of a tensor network centred on the vertices verts. Usi Defaults to using tn[verts] as the local network but can be overriden """ function approx_network_region( - tn::ITensorNetwork, - mts::DataGraph, + ptn::PartitionedITensorNetwork, + mts, verts::Vector; - verts_tn=ITensorNetwork([tn[v] for v in verts]), + verts_tn=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], ) - environment_tn = get_environment(tn, mts, verts) + environment_tensors = get_environment(ptn, mts, verts) - return environment_tn ⊗ verts_tn + return vcat(environment_tensors, verts_tn) end diff --git a/src/partition.jl b/src/partition.jl new file mode 100644 index 00000000..08b0a88f --- /dev/null +++ b/src/partition.jl @@ -0,0 +1,198 @@ +""" + partition_vertices(g::AbstractGraph, subgraph_vertices::Vector) + +Given a graph (`g`) and groups of vertices defining subgraphs of that +graph (`subgraph_vertices`), return a DataGraph storing the subgraph +vertices on the vertices of the graph and with edges denoting +which subgraphs of the original graph have edges connecting them, along with +edge data storing the original edges that were connecting the subgraphs. +""" +function partition_vertices(g::AbstractGraph, subgraph_vertices) + partitioned_vertices = DataGraph( + NamedGraph(eachindex(subgraph_vertices)), Dictionary(subgraph_vertices) + ) + for e in edges(g) + s1 = findfirst_on_vertices( + subgraph_vertices -> src(e) ∈ subgraph_vertices, partitioned_vertices + ) + s2 = findfirst_on_vertices( + subgraph_vertices -> dst(e) ∈ subgraph_vertices, partitioned_vertices + ) + if (!has_edge(partitioned_vertices, s1, s2) && s1 ≠ s2) + add_edge!(partitioned_vertices, s1, s2) + partitioned_vertices[s1 => s2] = Vector{edgetype(g)}() + end + if has_edge(partitioned_vertices, s1, s2) + push!(partitioned_vertices[s1 => s2], e) + end + end + return partitioned_vertices +end + +""" + partition_vertices(g::AbstractGraph; npartitions, nvertices_per_partition, kwargs...) + +Given a graph `g`, partition the vertices of `g` into 'npartitions' partitions +or into partitions with `nvertices_per_partition` vertices per partition. +Try to keep all subgraphs the same size and minimise edges cut between them +Returns a datagraph where each vertex contains the list of vertices involved in that subgraph. The edges state which subgraphs are connected. +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. +""" +function partition_vertices( + g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + return partition_vertices( + g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...) + ) +end + +""" +Find all vertices `v` such that `f(graph[v]) == true` +""" +function findall_on_vertices(f::Function, graph::AbstractDataGraph) + return findall(f, vertex_data(graph)) +end + +""" +Find the vertex `v` such that `f(graph[v]) == true` +""" +function findfirst_on_vertices(f::Function, graph::AbstractDataGraph) + return findfirst(f, vertex_data(graph)) +end + +""" +Find all edges `e` such that `f(graph[e]) == true` +""" +function findall_on_edges(f::Function, graph::AbstractDataGraph) + return findall(f, edge_data(graph)) +end + +""" +Find the edge `e` such that `f(graph[e]) == true` +""" +function findfirst_on_edges(f::Function, graph::AbstractDataGraph) + return findfirst(f, edge_data(graph)) +end + +function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) + return subgraphs(NamedGraph(g), subgraph_vertices) +end + + """ + subgraphs(g::AbstractGraph, subgraph_vertices) + + Return a collection of subgraphs of `g` defined by the subgraph + vertices `subgraph_vertices`. + """ +function subgraphs(g::AbstractGraph, subgraph_vertices) + return map(vs -> subgraph(g, vs), subgraph_vertices) +end + + +""" + subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) + +Given a graph `g`, partition `g` into `npartitions` partitions +or into partitions with `nvertices_per_partition` vertices per partition, +returning a list of subgraphs. +Try to keep all subgraphs the same size and minimise edges cut between them. +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. +""" +function subgraphs( + g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) +end + +function partition(g::AbstractSimpleGraph, subgraph_vertices) + return partition(NamedGraph(g), subgraph_vertices) +end + +function partition(g::AbstractGraph, subgraph_vertices) + partitioned_graph = DataGraph( + NamedGraph(eachindex(subgraph_vertices)), subgraphs(g, Dictionary(subgraph_vertices)) + ) + for e in edges(g) + s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) + s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) + if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) + add_edge!(partitioned_graph, s1, s2) + partitioned_graph[s1 => s2] = Dictionary( + [:edges, :edge_data], + [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], + ) + end + if has_edge(partitioned_graph, s1, s2) + push!(partitioned_graph[s1 => s2][:edges], e) + if isassigned(g, e) + set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) + end + end + end + return partitioned_graph +end + +""" + partition(g::AbstractGraph; npartitions::Integer, kwargs...) + partition(g::AbstractGraph, subgraph_vertices) + +Given a graph `g`, partition `g` into `npartitions` partitions +or into partitions with `nvertices_per_partition` vertices per partition. +The partitioning tries to keep all subgraphs the same size and minimize +edges cut between them. + +Alternatively, specify a desired partitioning with a collection of sugraph +vertices. + +Returns a data graph where each vertex contains the corresponding subgraph as vertex data. +The edges indicates which subgraphs are connected, and the edge data stores a dictionary +with two fields. The field `:edges` stores a list of the edges of the original graph +that were connecting the two subgraphs, and `:edge_data` stores a dictionary +mapping edges of the original graph to the data living on the edges of the original +graph, if it existed. + +Therefore, one should be able to extract that data and recreate the original +graph from the results of `partition`. + +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work +if the subgraph vertices aren't specified explicitly. +""" +function partition( + g::AbstractGraph; + npartitions=nothing, + nvertices_per_partition=nothing, + subgraph_vertices=nothing, + kwargs..., +) + if count(isnothing, (npartitions, nvertices_per_partition, subgraph_vertices)) != 2 + error( + "Error: Cannot give multiple/ no partitioning options. Please specify exactly one." + ) + end + + if isnothing(subgraph_vertices) + subgraph_vertices = ITensorNetworks.subgraph_vertices( + g; npartitions, nvertices_per_partition, kwargs... + ) + end + + return partition(g, subgraph_vertices) +end + +""" + TODO: do we want to make it a public function? +""" +function _noncommoninds(partition::DataGraph) + networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] + network = vcat(networks...) + return noncommoninds(network...) +end + +# Util functions for partition +function _commoninds(partition::DataGraph) + networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] + network = vcat(networks...) + outinds = noncommoninds(network...) + allinds = mapreduce(t -> [i for i in inds(t)], vcat, network) + return Vector(setdiff(allinds, outinds)) +end \ No newline at end of file diff --git a/test/test_binary_tree_partition.jl b/test/test_binary_tree_partition.jl index 32942b4a..c558bcef 100644 --- a/test/test_binary_tree_partition.jl +++ b/test/test_binary_tree_partition.jl @@ -1,5 +1,6 @@ using ITensors, OMEinsumContractionOrders using Graphs, NamedGraphs +using ITensorNetworks using ITensors: contract using ITensorNetworks: _root, @@ -9,6 +10,7 @@ using ITensorNetworks: _contract_deltas_ignore_leaf_partitions, _rem_vertex!, _DensityMartrixAlgGraph +using Test @testset "test mincut functions on top of MPS" begin i = Index(2, "i") diff --git a/test/test_partitioneditensornetwork.jl b/test/test_partitioneditensornetwork.jl index 670c700d..e4ab7d3c 100644 --- a/test/test_partitioneditensornetwork.jl +++ b/test/test_partitioneditensornetwork.jl @@ -8,13 +8,16 @@ using Random using Test using SplitApplyCombine -using NamedGraphs: which_partition, parent_graph +using NamedGraphs: which_partition, parent_graph, partitioned_vertices + +using ITensorNetworks: message_tensors, update_message_tensor, belief_propagation_iteration, belief_propagation, + get_environment @testset "PartitionedITensorNetwork" begin - g_dims = (1, 6) + g_dims = (3, 3) g = named_grid(g_dims) s = siteinds("S=1/2", g) - χ = 4 + χ = 2 Random.seed!(1234) ψ = randomITensorNetwork(s; link_space=χ) ψψ = ψ ⊗ prime(dag(ψ); sites=[]) @@ -22,7 +25,11 @@ using NamedGraphs: which_partition, parent_graph pψψ = PartitionedITensorNetwork(ψψ, subgraph_vertices) - @show which_partition(pψψ, ((1,1),1)) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + + env_tensors = get_environment(pψψ, mts, [((1,1),1)]) + @show ITensors.contract(env_tensors) end \ No newline at end of file From e5c1da7e6029a18e4cd7bf1626f13343ec3122a3 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 12 Jan 2024 13:59:15 -0500 Subject: [PATCH 03/12] Adapting BP examples and test and gauging to match new BP interface --- examples/belief_propagation/bpexample.jl | 6 +- examples/belief_propagation/bpsequences.jl | 16 +- examples/belief_propagation/sqrt_bp.jl | 31 ++- .../dynamics/heavy_hex_ising_real_tebd.jl | 24 +-- examples/gauging/gauging_itns.jl | 32 ++- examples/partition/kahypar_vs_metis.jl | 44 ---- examples/partition/partitioning.jl | 31 --- src/ITensorNetworks.jl | 11 +- src/beliefpropagation/beliefpropagation.jl | 17 +- .../sqrt_beliefpropagation.jl | 93 ++++---- src/binary_tree_partition.jl | 129 ------------ src/gauging.jl | 56 ++--- src/itensornetwork.jl | 2 + src/partition.jl | 198 ------------------ src/partitioneditensornetwork.jl | 3 - src/requires/kahypar.jl | 33 --- src/requires/metis.jl | 33 --- test/test_belief_propagation.jl | 133 ++++++------ test/test_gauging.jl | 21 +- 19 files changed, 200 insertions(+), 713 deletions(-) delete mode 100644 examples/partition/kahypar_vs_metis.jl delete mode 100644 examples/partition/partitioning.jl delete mode 100644 src/binary_tree_partition.jl delete mode 100644 src/partition.jl delete mode 100644 src/partitioneditensornetwork.jl delete mode 100644 src/requires/kahypar.jl delete mode 100644 src/requires/metis.jl diff --git a/examples/belief_propagation/bpexample.jl b/examples/belief_propagation/bpexample.jl index 72ccd798..09e8344f 100644 --- a/examples/belief_propagation/bpexample.jl +++ b/examples/belief_propagation/bpexample.jl @@ -30,7 +30,7 @@ function main() v = (1, 1) #Now do Simple Belief Propagation to Measure Sz on Site v - pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) mts = message_tensors(pψψ) mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( @@ -43,7 +43,7 @@ function main() ) #Now do Column-wise General Belief Propagation to Measure Sz on Site v - pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) mts = message_tensors(pψψ) mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( @@ -65,7 +65,7 @@ function main() ψψ = combine_linkinds(ψψ, combiners) ψOψ = combine_linkinds(ψOψ, combiners) - pψψ = PartitionedITensorNetwork(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) mts = message_tensors(pψψ; itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e]) mts = belief_propagation(pψψ, mts; contract_kwargs=(;alg="density_matrix",output_structure=path_graph_structure,maxdim = 8,contraction_sequence_alg="optimal")) numerator_tensors = approx_network_region( diff --git a/examples/belief_propagation/bpsequences.jl b/examples/belief_propagation/bpsequences.jl index 456e96c7..1c84d80d 100644 --- a/examples/belief_propagation/bpsequences.jl +++ b/examples/belief_propagation/bpsequences.jl @@ -12,7 +12,6 @@ using ITensorNetworks: approx_network_region, contract_inner, message_tensors, - nested_graph_leaf_vertices, edge_sequence function main() @@ -39,36 +38,35 @@ function main() ψψ = ψ ⊗ prime(dag(ψ); sites=[]) #Initial message tensors for BP - mts_init = message_tensors( - ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) - ) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts_init = message_tensors(pψψ) println("\nFirst testing out a $g_label. Random network with bond dim $χ") #Now test out various sequences print("Parallel updates (sequence is irrelevant): ") belief_propagation( - ψψ, + pψψ, mts_init; contract_kwargs=(; alg="exact"), target_precision=1e-10, niters=100, - edges=edge_sequence(mts_init; alg="parallel"), + edges=[PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel")], verbose=true, ) print("Sequential updates (sequence is default edge list of the message tensors): ") belief_propagation( - ψψ, + pψψ, mts_init; contract_kwargs=(; alg="exact"), target_precision=1e-10, niters=100, - edges=[e for e in edges(mts_init)], + edges=PartitionEdge.([e for e in vcat(edges(partitioned_graph(pψψ)), reverse.(edges(partitioned_graph(pψψ))))]), verbose=true, ) print("Sequential updates (sequence is our custom sequence finder): ") belief_propagation( - ψψ, + pψψ, mts_init; contract_kwargs=(; alg="exact"), target_precision=1e-10, diff --git a/examples/belief_propagation/sqrt_bp.jl b/examples/belief_propagation/sqrt_bp.jl index 207fb941..d386d863 100644 --- a/examples/belief_propagation/sqrt_bp.jl +++ b/examples/belief_propagation/sqrt_bp.jl @@ -2,6 +2,7 @@ using ITensors using ITensorNetworks using Random using SplitApplyCombine +using NamedGraphs using ITensorNetworks: approx_network_region, @@ -12,7 +13,6 @@ using ITensorNetworks: function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) g_dims = (n, n) - @show g_dims g = named_grid(g_dims) s = siteinds("S=1/2", g) @@ -30,22 +30,19 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) # Site to take expectation value on v = (n ÷ 2, n ÷ 2) - @show v #Now do Simple Belief Propagation to Measure Sz on Site v - mts = message_tensors( - ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) - ) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) - mts = @time belief_propagation(ψψ, mts; niters, contract_kwargs=(; alg="exact")) + mts = @time belief_propagation(pψψ, mts; niters, contract_kwargs=(; alg="exact")) numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork([apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) + pψψ, mts, [(v, 1)]; verts_tn=ITensor[apply(op("Sz", s[v]), ψ[v])]) + denominator_network = approx_network_region(pψψ, mts, [(v, 1)]) sz_bp = - contract( + ITensors.contract( numerator_network; sequence=contraction_sequence(numerator_network; alg="optimal") - )[] / contract( + )[] / ITensors.contract( denominator_network; sequence=contraction_sequence(denominator_network; alg="optimal") )[] @@ -53,16 +50,12 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) "Simple Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_bp) ) - mts_sqrt = message_tensors( - ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) - ) - - mts_sqrt = @time sqrt_belief_propagation(ψ, mts_sqrt; niters) + mts_sqrt = message_tensors(pψψ) + mts_sqrt = @time sqrt_belief_propagation(pψψ, mts_sqrt; niters) numerator_network = approx_network_region( - ψψ, mts_sqrt, [(v, 1)]; verts_tn=ITensorNetwork([apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts_sqrt, [(v, 1)]) + pψψ, mts_sqrt, [(v, 1)]; verts_tn=ITensor[apply(op("Sz", s[v]), ψ[v])]) + denominator_network = approx_network_region(pψψ, mts_sqrt, [(v, 1)]) sz_sqrt_bp = contract( numerator_network; sequence=contraction_sequence(numerator_network; alg="optimal") diff --git a/examples/dynamics/heavy_hex_ising_real_tebd.jl b/examples/dynamics/heavy_hex_ising_real_tebd.jl index e8dcc863..d6e492e7 100644 --- a/examples/dynamics/heavy_hex_ising_real_tebd.jl +++ b/examples/dynamics/heavy_hex_ising_real_tebd.jl @@ -38,7 +38,7 @@ osprey_processor_graph() = ibm_processor_graph(6, 12) """Take the expectation value of o on an ITN using belief propagation""" function expect_state_SBP( - o::ITensor, ψ::AbstractITensorNetwork, ψψ::AbstractITensorNetwork, mts::DataGraph + o::ITensor, ψ::AbstractITensorNetwork, pψψ::PartitionedGraph, mts ) Oψ = apply(o, ψ; cutoff=1e-16) ψ = copy(ψ) @@ -46,14 +46,13 @@ function expect_state_SBP( vs = vertices(s)[findall(i -> (length(commoninds(s[i], inds(o))) != 0), vertices(s))] vs_braket = [(v, 1) for v in vs] - numerator_network = approx_network_region( - ψψ, mts, vs_braket; verts_tn=ITensorNetwork(ITensor[Oψ[v] for v in vs]) - ) - denominator_network = approx_network_region(ψψ, mts, vs_braket) - num_seq = contraction_sequence(numerator_network; alg="optimal") - den_seq = contraction_sequence(numerator_network; alg="optimal") - return contract(numerator_network; sequence=num_seq)[] / - contract(denominator_network; sequence=den_seq)[] + numerator_tensors = approx_network_region( + pψψ, mts, vs_braket; verts_tn=ITensor[Oψ[v] for v in vs]) + denominator_tensors = approx_network_region(pψψ, mts, vs_braket) + num_seq = contraction_sequence(numerator_tensors; alg="optimal") + den_seq = contraction_sequence(denominator_tensors; alg="optimal") + return contract(numerator_tensors; sequence=num_seq)[] / + contract(denominator_tensors; sequence=den_seq)[] end function main(θh::Float64, no_trotter_steps::Int64; apply_kwargs...) @@ -94,7 +93,7 @@ function main(θh::Float64, no_trotter_steps::Int64; apply_kwargs...) cur_C = vidal_itn_canonicalness(ψ, bond_tensors) if ((i + 1) % gauge_freq) == 0 && (cur_C >= target_c) println("Too far from the Vidal gauge. Regauging the state!") - ψ, _ = vidal_to_symmetric_gauge(ψ, bond_tensors) + ψ, _, _ = vidal_to_symmetric_gauge(ψ, bond_tensors) ψ, bond_tensors = vidal_gauge( ψ; target_canonicalness=target_c, niters=20, cutoff=1e-14 ) @@ -102,12 +101,11 @@ function main(θh::Float64, no_trotter_steps::Int64; apply_kwargs...) end #Calculate on-site magnetisations - ψ, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) - ψψ = norm_network(ψ) + ψ, pψψ, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) mag_dict = Dict( zip( [v for v in vertices(ψ)], - [expect_state_SBP(op("Z", s[v]), ψ, ψψ, mts) for v in vertices(ψ)], + [expect_state_SBP(op("Z", s[v]), ψ, pψψ, mts) for v in vertices(ψ)], ), ) diff --git a/examples/gauging/gauging_itns.jl b/examples/gauging/gauging_itns.jl index 7463d54a..19e7e631 100644 --- a/examples/gauging/gauging_itns.jl +++ b/examples/gauging/gauging_itns.jl @@ -7,38 +7,36 @@ using SplitApplyCombine using ITensorNetworks: message_tensors, - nested_graph_leaf_vertices, - belief_propagation_iteration, belief_propagation, - find_subgraph, vidal_gauge, symmetric_gauge, vidal_itn_canonicalness, - vidal_to_symmetric_gauge, initialize_bond_tensors, vidal_itn_isometries, norm_network, - edge_sequence + edge_sequence, + belief_propagation_iteration using NamedGraphs using NamedGraphs: add_edges!, rem_vertex!, hexagonal_lattice_graph using Graphs """Eager Gauging""" -function eager_gauging(ψ::ITensorNetwork, bond_tensors::DataGraph, mts::DataGraph) +function eager_gauging(ψ::ITensorNetwork, pψψ::PartitionedGraph, bond_tensors::DataGraph, mts) isometries = vidal_itn_isometries(ψ, bond_tensors) ψ = copy(ψ) mts = copy(mts) for e in edges(ψ) - s1, s2 = find_subgraph((src(e), 1), mts), find_subgraph((dst(e), 1), mts) + vsrc, vdst = src(e), dst(e) + pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) normalize!(isometries[e]) normalize!(isometries[reverse(e)]) - mts[s1 => s2], mts[s2 => s1] = ITensorNetwork(isometries[e]), + mts[pe], mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = ITensorNetwork(isometries[e]), ITensorNetwork(isometries[reverse(e)]) end - ψ, bond_tensors = vidal_gauge(ψ, mts, bond_tensors) + ψ, bond_tensors = vidal_gauge(ψ, pψψ, mts, bond_tensors) return ψ, bond_tensors, mts end @@ -58,12 +56,9 @@ function benchmark_state_gauging( ψψ = norm_network(ψ) ψinit = copy(ψ) - vertex_groups = nested_graph_leaf_vertices(partition(ψψ, group(v -> v[1], vertices(ψψ)))) - mts = message_tensors(partition(ψψ; subgraph_vertices=vertex_groups)) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) bond_tensors = initialize_bond_tensors(ψ) - for e in edges(mts) - mts[e] = ITensorNetwork(dense(delta(inds(ITensors.contract(ITensor(mts[e])))))) - end for i in 1:no_iterations println("On Iteration " * string(i)) @@ -71,17 +66,16 @@ function benchmark_state_gauging( if mode == "belief_propagation" if BP_update_order != "parallel" times_iters[i] = @elapsed mts, _ = belief_propagation_iteration( - ψψ, mts; contract_kwargs=(; alg="exact") + pψψ, mts; contract_kwargs=(; alg="exact") ) else times_iters[i] = @elapsed mts, _ = belief_propagation_iteration( - ψψ, mts; contract_kwargs=(; alg="exact"), edges=edge_sequence(mts; alg="parallel") - ) + pψψ, mts; contract_kwargs=(; alg="exact"), edges=[PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel")]) end - times_gauging[i] = @elapsed ψ, bond_tensors = vidal_gauge(ψinit, mts) + times_gauging[i] = @elapsed ψ, bond_tensors = vidal_gauge(ψinit, pψψ, mts) elseif mode == "eager" - times_iters[i] = @elapsed ψ, bond_tensors, mts = eager_gauging(ψ, bond_tensors, mts) + times_iters[i] = @elapsed ψ, bond_tensors, mts = eager_gauging(ψ, pψψ, bond_tensors, mts) else times_iters[i] = @elapsed begin for e in edges(ψ) diff --git a/examples/partition/kahypar_vs_metis.jl b/examples/partition/kahypar_vs_metis.jl deleted file mode 100644 index 36301a5b..00000000 --- a/examples/partition/kahypar_vs_metis.jl +++ /dev/null @@ -1,44 +0,0 @@ -using Graphs -using KaHyPar -using Metis -using ITensorNetworks - -g = grid((16,)) -npartitions = 4 - -kahypar_partitions = subgraph_vertices(g; npartitions, backend="KaHyPar") -metis_partitions = subgraph_vertices(g; npartitions, backend="Metis") -@show kahypar_partitions, length(kahypar_partitions) -@show metis_partitions, length(metis_partitions) - -g_parts = partition(g; npartitions) -@show nv(g_parts) == 4 -@show nv(g_parts[1]) == 4 -@show nv(g_parts[2]) == 4 -@show nv(g_parts[3]) == 4 -@show nv(g_parts[4]) == 4 -@show issetequal(metis_partitions[2], vertices(g_parts[2])) - -using ITensorNetworks -tn = ITensorNetwork(named_grid((4, 2)); link_space=3); - -# subgraph_vertices -tn_sv = subgraph_vertices(tn; npartitions=2) # Same as `partition_vertices(tn; nvertices_per_partition=4)` - -# partition_vertices -tn_pv = partition_vertices(tn; npartitions=2); -typeof(tn_pv) -tn_pv[1] -edges(tn_pv) -tn_pv[1 => 2] - -# subgraphs -tn_sg = subgraphs(tn; npartitions=2); -typeof(tn_sg) -tn_sg[1] - -# partition -tn_pg = partition(tn; npartitions=2); -typeof(tn_pg) -tn_pg[1] -tn_pg[1 => 2][:edges] diff --git a/examples/partition/partitioning.jl b/examples/partition/partitioning.jl deleted file mode 100644 index 0667d02b..00000000 --- a/examples/partition/partitioning.jl +++ /dev/null @@ -1,31 +0,0 @@ -using Graphs -using ITensors -using ITensorNetworks -using ITensorUnicodePlots -using KaHyPar -using Suppressor - -system_dims = (4, 4) -g = hypercubic_lattice_graph(system_dims) - -s = siteinds("S=1/2", g) - -χ = 5 -ψ = ITensorNetwork(s; link_space=χ) - -@visualize ψ edge_labels = (; plevs=true) - -ψ′ = ψ' -@visualize ψ′ edge_labels = (; plevs=true) - -v = (2, 2) -neighbor_edges = [v => nv for nv in neighbors(ψ, v)] -@show siteinds(ψ, v) -@show [e => linkinds(ψ, e) for e in neighbor_edges] - -npartitions = 4 -partitions = partition(ψ; npartitions) - -@show partitions - -nothing diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index dafb3586..f1c8acd1 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -70,8 +70,6 @@ include("observers.jl") include("visualize.jl") include("graphs.jl") include("itensors.jl") -include("partition.jl") -include("partitioneditensornetwork.jl") include("lattices.jl") include("abstractindsnetwork.jl") include("indextags.jl") @@ -87,11 +85,12 @@ include("tebd.jl") include("itensornetwork.jl") include("mincut.jl") include("contract_deltas.jl") -include("binary_tree_partition.jl") include(joinpath("approx_itensornetwork", "utils.jl")) include(joinpath("approx_itensornetwork", "density_matrix.jl")) include(joinpath("approx_itensornetwork", "ttn_svd.jl")) include(joinpath("approx_itensornetwork", "approx_itensornetwork.jl")) +include(joinpath("approx_itensornetwork", "partition.jl")) +include(joinpath("approx_itensornetwork", "binary_tree_partition.jl")) include("contract.jl") include("utility.jl") include("specialitensornetworks.jl") @@ -128,12 +127,6 @@ include(joinpath("treetensornetworks", "solvers", "tree_sweeping.jl")) include("exports.jl") function __init__() - @require KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" include( - joinpath("requires", "kahypar.jl") - ) - @require Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" include( - joinpath("requires", "metis.jl") - ) @require OMEinsumContractionOrders = "6f22d1fd-8eed-4bb7-9776-e7d684900715" include( joinpath("requires", "omeinsumcontractionorders.jl") ) diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 744fae5c..166feb2b 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -1,5 +1,5 @@ function message_tensors( - ptn::PartitionedITensorNetwork; + ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] ) mts = Dict() @@ -17,7 +17,7 @@ end Do a single update of a message tensor using the current subgraph and the incoming mts """ function update_message_tensor( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, edge::PartitionEdge, mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), @@ -45,7 +45,7 @@ end Do a sequential update of message tensors on `edges` for a given ITensornetwork and its partition into sub graphs """ function belief_propagation_iteration( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, mts, edges::Vector{<:PartitionEdge}; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), @@ -59,6 +59,7 @@ function belief_propagation_iteration( if compute_norm LHS, RHS = ITensors.contract(mts[e]), ITensors.contract(new_mts[e]) + #This line only makes sense if the message tensors are rank 2??? Should fix this. LHS /= sum(diag(LHS)) RHS /= sum(diag(RHS)) c += 0.5 * norm(denseblocks(LHS) - denseblocks(RHS)) @@ -73,7 +74,7 @@ Currently we send the full message tensor data struct to belief_propagation_iter mts relevant to that subgraph. """ function belief_propagation_iteration( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, mts, edge_groups::Vector{<:Vector{<:PartitionEdge}}; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), @@ -94,7 +95,7 @@ function belief_propagation_iteration( end function belief_propagation_iteration( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), compute_norm=false, @@ -104,7 +105,7 @@ function belief_propagation_iteration( end function belief_propagation( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), niters=default_bp_niters(partitioned_graph(ptn)), @@ -132,7 +133,7 @@ end Given a subet of vertices of a given Tensor Network and the Message Tensors for that network, return a Dictionary with the involved subgraphs as keys and the vector of tensors associated with that subgraph as values Specifically, the contraction of the environment tensors and tn[vertices] will be a scalar. """ -function get_environment(ptn::PartitionedITensorNetwork, mts, verts::Vector; dir=:in) +function get_environment(ptn::PartitionedGraph, mts, verts::Vector; dir=:in) partition_vertices = unique([NamedGraphs.which_partition(ptn, v) for v in verts]) if dir == :out @@ -151,7 +152,7 @@ Calculate the contraction of a tensor network centred on the vertices verts. Usi Defaults to using tn[verts] as the local network but can be overriden """ function approx_network_region( - ptn::PartitionedITensorNetwork, + ptn::PartitionedGraph, mts, verts::Vector; verts_tn=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], diff --git a/src/beliefpropagation/sqrt_beliefpropagation.jl b/src/beliefpropagation/sqrt_beliefpropagation.jl index 20ff3d8e..32850d2c 100644 --- a/src/beliefpropagation/sqrt_beliefpropagation.jl +++ b/src/beliefpropagation/sqrt_beliefpropagation.jl @@ -2,19 +2,12 @@ # using ITensorNetworks: find_subgraph, map_diag, sqrt_diag, boundary_edges function sqrt_belief_propagation_iteration( - tn::ITensorNetwork, sqrt_mts::DataGraph, edges::Vector{<:AbstractEdge} + pψψ::PartitionedGraph, sqrt_mts, edges::Vector{<:PartitionEdge} ) new_sqrt_mts = copy(sqrt_mts) c = 0.0 for e in edges - environment_tensornetworks = ITensorNetwork[ - new_sqrt_mts[e_in] for - e_in in setdiff(boundary_edges(new_sqrt_mts, [src(e)]; dir=:in), [reverse(e)]) - ] - - new_sqrt_mts[src(e) => dst(e)] = update_sqrt_message_tensor( - tn, new_sqrt_mts[src(e)], environment_tensornetworks; - ) + new_sqrt_mts[e] = ITensor[update_sqrt_message_tensor(pψψ, e, new_sqrt_mts;)] # if compute_norm # LHS, RHS = ITensors.contract(ITensor(sqrt_mts[src(e) => dst(e)])), @@ -28,12 +21,12 @@ function sqrt_belief_propagation_iteration( end function sqrt_belief_propagation_iteration( - tn::ITensorNetwork, sqrt_mts::DataGraph, edges::Vector{<:Vector{<:AbstractEdge}} + pψψ::PartitionedGraph, sqrt_mts, edges::Vector{<:Vector{<:PartitionEdge}} ) new_sqrt_mts = copy(sqrt_mts) c = 0.0 for e_group in edges - updated_sqrt_mts, ct = sqrt_belief_propagation_iteration(tn, sqr_mts, e_group) + updated_sqrt_mts, ct = sqrt_belief_propagation_iteration(pψψ, sqr_mts, e_group) for e in e_group new_sqrt_mts[e] = updated_sqrt_mts[e] end @@ -43,25 +36,25 @@ function sqrt_belief_propagation_iteration( end function sqrt_belief_propagation_iteration( - tn::ITensorNetwork, sqrt_mts::DataGraph; edges=edge_sequence(mts) + pψψ::PartitionedGraph, sqrt_mts; edges=edge_sequence(partitioned_graph(pψψ)) ) - return sqrt_belief_propagation_iteration(tn, sqrt_mts, edges) + return sqrt_belief_propagation_iteration(pψψ, sqrt_mts, edges) end function sqrt_belief_propagation( - tn::ITensorNetwork, - mts::DataGraph; - niters=default_bp_niters(mts), - edges=edge_sequence(mts), + pψψ::PartitionedGraph, + mts; + niters=default_bp_niters(partitioned_graph(pψψ)), + edges=PartitionEdge.(edge_sequence(partitioned_graph(pψψ))), # target_precision::Union{Float64,Nothing}=nothing, ) # compute_norm = target_precision == nothing ? false : true - sqrt_mts = sqrt_message_tensors(tn, mts) + sqrt_mts = sqrt_message_tensors(pψψ, mts) if isnothing(niters) error("You need to specify a number of iterations for BP!") end for i in 1:niters - sqrt_mts, c = sqrt_belief_propagation_iteration(tn, sqrt_mts, edges) #; compute_norm) + sqrt_mts, c = sqrt_belief_propagation_iteration(pψψ, sqrt_mts, edges) #; compute_norm) # if compute_norm && c <= target_precision # println( # "Belief Propagation finished. Reached a canonicalness of " * @@ -75,47 +68,43 @@ function sqrt_belief_propagation( end function update_sqrt_message_tensor( - tn::ITensorNetwork, subgraph_vertices::Vector, sqrt_mts::Vector{ITensorNetwork}; + pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts; ) - sqrt_mts_itensors = reduce(vcat, ITensor.(sqrt_mts); init=ITensor[]) - v = only(unique(first.(subgraph_vertices))) - site_itensor = tn[v] - contract_list = ITensor[sqrt_mts_itensors; site_itensor] + v = only(filter(v -> v[2] == 1, vertices(pψψ, src(edge)))) + site_itensor = unpartitioned_graph(pψψ)[v] + incoming_messages = [ + sqrt_mts[PartitionEdge(e_in)] for e_in in setdiff(boundary_edges(partitioned_graph(pψψ), [NamedGraphs.parent(src(edge))]; dir=:in), [reverse(NamedGraphs.parent(edge))]) + ] + incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) + contract_list = ITensor[incoming_messages; site_itensor] contract_output = contract( contract_list; sequence=contraction_sequence(contract_list; alg="optimal") ) - left_inds = [uniqueinds(contract_output, site_itensor); siteinds(tn, v)] + left_inds = [uniqueinds(contract_output, site_itensor); siteinds(unpartitioned_graph(pψψ), v)] Q, R = qr(contract_output, left_inds) normalize!(R) - return ITensorNetwork(R) -end - -function update_sqrt_message_tensor( - tn::ITensorNetwork, subgraph::ITensorNetwork, mts::Vector{ITensorNetwork}; kwargs... -) - return update_sqrt_message_tensor(tn, vertices(subgraph), mts; kwargs...) + return R end function sqrt_message_tensors( - ψ::ITensorNetwork, - mts::DataGraph; - eigen_message_tensor_cutoff=10 * eps(real(scalartype(ψ))), - regularization=10 * eps(real(scalartype(ψ))), + pψψ::PartitionedGraph, + mts; + eigen_message_tensor_cutoff=10 * eps(real(scalartype(unpartitioned_graph(pψψ)))), + regularization=10 * eps(real(scalartype(unpartitioned_graph(pψψ)))), ) sqrt_mts = copy(mts) - for e in edges(ψ) - vsrc, vdst = src(e), dst(e) - ψvsrc, ψvdst = ψ[vsrc], ψ[vdst] + for e in PartitionEdge.(edges(partitioned_graph(pψψ))) + vsrc, vdst = filter(v -> v[2] == 1, vertices(pψψ, src(e))), filter(v -> v[2] == 1, vertices(pψψ, dst(e))) + ψvsrc, ψvdst = unpartitioned_graph(pψψ)[only(vsrc)],unpartitioned_graph(pψψ)[only(vdst)] - s1, s2 = find_subgraph((vsrc, 1), mts), find_subgraph((vdst, 1), mts) - edge_ind = commoninds(ψ[vsrc], ψ[vdst]) + edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) X_D, X_U = eigen( - contract(ITensor(mts[s1 => s2])); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[e]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) Y_D, Y_U = eigen( - contract(ITensor(mts[s2 => s1])); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -124,26 +113,24 @@ function sqrt_message_tensors( rootX = X_U * rootX_D * prime(dag(X_U)) rootY = Y_U * rootY_D * prime(dag(Y_U)) - sqrt_mts[s1 => s2] = ITensorNetwork(rootX) - sqrt_mts[s2 => s1] = ITensorNetwork(rootY) + sqrt_mts[e] = ITensor[rootX] + sqrt_mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = ITensor[rootY] end return sqrt_mts end -function sqr_message_tensors(sqrt_mts::DataGraph) +function sqr_message_tensors(sqrt_mts) mts = copy(sqrt_mts) - for e in edges(sqrt_mts) - sqrt_mt_tn = sqrt_mts[e] - sqrt_mt = sqrt_mt_tn[only(vertices(sqrt_mt_tn))] - sqrt_mt_rev_tn = sqrt_mts[reverse(e)] - sqrt_mt_rev = sqrt_mt_rev_tn[only(vertices(sqrt_mt_rev_tn))] + for e in keys(sqrt_mts) + sqrt_mt = only(sqrt_mts[e]) + sqrt_mt_rev = only(sqrt_mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]) l = commoninds(sqrt_mt, sqrt_mt_rev) mt = dag(prime(sqrt_mt, l)) * sqrt_mt normalize!(mt) mt_rev = dag(prime(sqrt_mt_rev, l)) * sqrt_mt_rev normalize!(mt_rev) - mts[e] = ITensorNetwork(mt) - mts[reverse(e)] = ITensorNetwork(mt_rev) + mts[e] = ITensor[mt] + mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = ITensor[mt_rev] end return mts end diff --git a/src/binary_tree_partition.jl b/src/binary_tree_partition.jl deleted file mode 100644 index cff0c31e..00000000 --- a/src/binary_tree_partition.jl +++ /dev/null @@ -1,129 +0,0 @@ -function _binary_partition(tn::ITensorNetwork, source_inds::Vector{<:Index}) - external_inds = noncommoninds(Vector{ITensor}(tn)...) - # add delta tensor to each external ind - external_sim_ind = [sim(ind) for ind in external_inds] - tn = map_data(t -> replaceinds(t, external_inds => external_sim_ind), tn; edges=[]) - tn_wo_deltas = rename_vertices(v -> v[1], subgraph(v -> v[2] == 1, tn)) - deltas = Vector{ITensor}(subgraph(v -> v[2] == 2, tn)) - scalars = rename_vertices(v -> v[1], subgraph(v -> v[2] == 3, tn)) - new_deltas = [ - delta(external_inds[i], external_sim_ind[i]) for i in 1:length(external_inds) - ] - deltas = [deltas..., new_deltas...] - tn = disjoint_union(tn_wo_deltas, ITensorNetwork(deltas), scalars) - p1, p2 = _mincut_partition_maxweightoutinds( - tn, source_inds, setdiff(external_inds, source_inds) - ) - source_tn = _contract_deltas(subgraph(tn, p1)) - remain_tn = _contract_deltas(subgraph(tn, p2)) - outinds_source = noncommoninds(Vector{ITensor}(source_tn)...) - outinds_remain = noncommoninds(Vector{ITensor}(remain_tn)...) - common_inds = intersect(outinds_source, outinds_remain) - @assert ( - length(external_inds) == - length(union(outinds_source, outinds_remain)) - length(common_inds) - ) - # We want the output two tns be connected to each other, so that the output - # of `binary_tree_partition` is a partition with a binary tree structure. - # Below we check if `source_tn` and `remain_tn` are connected, if not adding - # each tn a scalar tensor to force them to be connected. - if common_inds == [] - @info "_binary_partition outputs are not connected" - ind = Index(1, "unit_scalar_ind") - t1 = ITensor([1.0], ind) - t2 = ITensor([1.0], ind) - v1 = (nv(scalars) + 1, 3) - v2 = (nv(scalars) + 2, 3) - add_vertex!(source_tn, v1) - add_vertex!(remain_tn, v2) - source_tn[v1] = t1 - remain_tn[v2] = t2 - end - return source_tn, remain_tn -end - -""" -Given an input tn and a rooted binary tree of indices, return a partition of tn with the -same binary tree structure as inds_btree. -Note: in the output partition, we add multiple delta tensors to the network so that - the output graph is guaranteed to be the same binary tree as inds_btree. -Note: in the output partition, we add multiple scalar tensors. These tensors are used to - make the output partition connected, even if the input `tn` is disconnected. -Note: in the output partition, tensor vertex names will be changed. For a given input - tensor with vertex name `v``, its name in the output partition will be `(v, 1)`. Any - delta tensor will have name `(v, 2)`, and any scalar tensor used to maintain the connectivity - of the partition will have name `(v, 3)`. -Note: for a given binary tree with n indices, the output partition will contain 2n-1 vertices, - with each leaf vertex corresponding to a sub tn adjacent to one output index. Keeping these - leaf vertices in the partition makes later `approx_itensornetwork` algorithms more efficient. -Note: name of vertices in the output partition are the same as the name of vertices - in `inds_btree`. -""" -function partition( - ::Algorithm"mincut_recursive_bisection", tn::ITensorNetwork, inds_btree::DataGraph -) - @assert _is_rooted_directed_binary_tree(inds_btree) - output_tns = Vector{ITensorNetwork}() - output_deltas_vector = Vector{Vector{ITensor}}() - scalars_vector = Vector{Vector{ITensor}}() - # Mapping each vertex of the binary tree to a tn representing the partition - # of the subtree containing this vertex and its descendant vertices. - leaves = leaf_vertices(inds_btree) - root = _root(inds_btree) - v_to_subtree_tn = Dict{vertextype(inds_btree),ITensorNetwork}() - v_to_subtree_tn[root] = disjoint_union(tn, ITensorNetwork()) - for v in pre_order_dfs_vertices(inds_btree, root) - @assert haskey(v_to_subtree_tn, v) - input_tn = v_to_subtree_tn[v] - if !is_leaf(inds_btree, v) - c1, c2 = child_vertices(inds_btree, v) - descendant_c1 = pre_order_dfs_vertices(inds_btree, c1) - indices = [inds_btree[l] for l in intersect(descendant_c1, leaves)] - tn1, input_tn = _binary_partition(input_tn, indices) - v_to_subtree_tn[c1] = tn1 - descendant_c2 = pre_order_dfs_vertices(inds_btree, c2) - indices = [inds_btree[l] for l in intersect(descendant_c2, leaves)] - tn1, input_tn = _binary_partition(input_tn, indices) - v_to_subtree_tn[c2] = tn1 - end - tn = rename_vertices(u -> u[1], subgraph(u -> u[2] == 1, input_tn)) - deltas = Vector{ITensor}(subgraph(u -> u[2] == 2, input_tn)) - scalars = Vector{ITensor}(subgraph(u -> u[2] == 3, input_tn)) - push!(output_tns, tn) - push!(output_deltas_vector, deltas) - push!(scalars_vector, scalars) - end - # In subgraph_vertices, each element is a vector of vertices to be - # grouped in one partition. - subgraph_vs = Vector{Vector{Tuple}}() - delta_num = 0 - scalar_num = 0 - for (tn, deltas, scalars) in zip(output_tns, output_deltas_vector, scalars_vector) - vs = Vector{Tuple}([(v, 1) for v in vertices(tn)]) - vs = vcat(vs, [(i + delta_num, 2) for i in 1:length(deltas)]) - vs = vcat(vs, [(i + scalar_num, 3) for i in 1:length(scalars)]) - push!(subgraph_vs, vs) - delta_num += length(deltas) - scalar_num += length(scalars) - end - out_tn = ITensorNetwork() - for tn in output_tns - for v in vertices(tn) - add_vertex!(out_tn, v) - out_tn[v] = tn[v] - end - end - tn_deltas = ITensorNetwork(vcat(output_deltas_vector...)) - tn_scalars = ITensorNetwork(vcat(scalars_vector...)) - par = partition(disjoint_union(out_tn, tn_deltas, tn_scalars), subgraph_vs) - @assert is_tree(par) - name_map = Dict() - for (i, v) in enumerate(pre_order_dfs_vertices(inds_btree, root)) - name_map[i] = v - end - return rename_vertices(par, name_map) -end - -function partition(tn::ITensorNetwork, inds_btree::DataGraph; alg::String) - return partition(Algorithm(alg), tn, inds_btree) -end diff --git a/src/gauging.jl b/src/gauging.jl index 00347046..7a6fb65a 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -13,7 +13,8 @@ end """Use an ITensorNetwork ψ, its bond tensors and gauging mts to put ψ into the vidal gauge, return the bond tensors and ψ_vidal.""" function vidal_gauge( ψ::ITensorNetwork, - mts::DataGraph, + pψψ::PartitionedGraph, + mts, bond_tensors::DataGraph; eigen_message_tensor_cutoff=10 * eps(real(scalartype(ψ))), regularization=10 * eps(real(scalartype(ψ))), @@ -26,15 +27,15 @@ function vidal_gauge( vsrc, vdst = src(e), dst(e) ψvsrc, ψvdst = ψ_vidal[vsrc], ψ_vidal[vdst] - s1, s2 = find_subgraph((vsrc, 1), mts), find_subgraph((vdst, 1), mts) - edge_ind = commoninds(ψ_vidal[vsrc], ψ_vidal[vdst]) + pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) X_D, X_U = eigen( - ITensor(mts[s1 => s2]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + ITensor(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) Y_D, Y_U = eigen( - ITensor(mts[s2 => s1]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + ITensor(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -77,7 +78,8 @@ end """Use an ITensorNetwork ψ in the symmetric gauge and its mts to put ψ into the vidal gauge. Return the bond tensors and ψ_vidal.""" function vidal_gauge( ψ::ITensorNetwork, - mts::DataGraph; + pψψ::PartitionedGraph, + mts; eigen_message_tensor_cutoff=10 * eps(real(scalartype(ψ))), regularization=10 * eps(real(scalartype(ψ))), edges=NamedGraphs.edges(ψ), @@ -85,7 +87,7 @@ function vidal_gauge( ) bond_tensors = initialize_bond_tensors(ψ) return vidal_gauge( - ψ, mts, bond_tensors; eigen_message_tensor_cutoff, regularization, edges, svd_kwargs... + ψ, pψψ, mts, bond_tensors; eigen_message_tensor_cutoff, regularization, edges, svd_kwargs... ) end @@ -100,41 +102,41 @@ function vidal_gauge( svd_kwargs..., ) ψψ = norm_network(ψ) - Z = partition(ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(Z) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) mts = belief_propagation( - ψψ, + pψψ, mts; contract_kwargs=(; alg="exact"), niters, target_precision=target_canonicalness, verbose, ) - return vidal_gauge(ψ, mts; eigen_message_tensor_cutoff, regularization, svd_kwargs...) + return vidal_gauge(ψ, pψψ, mts; eigen_message_tensor_cutoff, regularization, svd_kwargs...) end -"""Transform from an ITensor in the Vidal Gauge (bond tensors) to the Symmetric Gauge (message tensors)""" +"""Transform from an ITensor in the Vidal Gauge (bond tensors) to the Symmetric Gauge (partitionedgraph, message tensors)""" function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) ψsymm = copy(ψ) ψψsymm = norm_network(ψsymm) - Z = partition( - ψψsymm; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψsymm)))) - ) - ψsymm_mts = message_tensors_skeleton(Z) + pψψsymm = PartitionedGraph(ψψsymm, collect(values(group(v -> v[1], vertices(ψψsymm))))) + ψsymm_mts = message_tensors(pψψsymm) for e in edges(ψsymm) vsrc, vdst = src(e), dst(e) - s1, s2 = find_subgraph((vsrc, 1), ψsymm_mts), find_subgraph((vdst, 1), ψsymm_mts) + pe = NamedGraphs.partition_edge(pψψsymm, NamedEdge((vsrc, 1) => (vdst, 1))) root_S = sqrt_diag(bond_tensors[e]) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vsrc]), vsrc) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vdst]), vdst) - ψsymm_mts[s1 => s2], ψsymm_mts[s2 => s1] = ITensorNetwork(bond_tensors[e]), - ITensorNetwork(bond_tensors[e]) + ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy(ITensor[bond_tensors[e]]), copy(ITensor[bond_tensors[e]]) end - return ψsymm, ψsymm_mts + ψψsymm = norm_network(ψsymm) + pψψsymm = PartitionedGraph(ψψsymm, collect(values(group(v -> v[1], vertices(ψψsymm))))) + + return ψsymm, pψψsymm, ψsymm_mts end """Put an ITensorNetwork into the symmetric gauge and also return the message tensors (which are the diagonal bond matrices from the Vidal Gauge)""" @@ -146,7 +148,7 @@ function symmetric_gauge( target_canonicalness::Union{Nothing,Float64}=nothing, svd_kwargs..., ) - ψsymm, bond_tensors = vidal_gauge( + ψ_vidal, bond_tensors = vidal_gauge( ψ; eigen_message_tensor_cutoff, regularization, @@ -155,12 +157,12 @@ function symmetric_gauge( svd_kwargs..., ) - return vidal_to_symmetric_gauge(ψsymm, bond_tensors) + return vidal_to_symmetric_gauge(ψ_vidal, bond_tensors) end """Transform from the Symmetric Gauge (message tensors) to the Vidal Gauge (bond tensors)""" function symmetric_to_vidal_gauge( - ψ::ITensorNetwork, mts::DataGraph; regularization=10 * eps(real(scalartype(ψ))) + ψ::ITensorNetwork, pψψ::PartitionedGraph, mts; regularization=10 * eps(real(scalartype(ψ))) ) bond_tensors = DataGraph{vertextype(ψ),ITensor,ITensor}(underlying_graph(ψ)) @@ -168,8 +170,8 @@ function symmetric_to_vidal_gauge( for e in edges(ψ) vsrc, vdst = src(e), dst(e) - s1, s2 = find_subgraph((vsrc, 1), mts), find_subgraph((vdst, 1), mts) - bond_tensors[e] = ITensor(mts[s1 => s2]) + pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + bond_tensors[e] = only(mts[pe]) invroot_S = invsqrt_diag(map_diag(x -> x + regularization, bond_tensors[e])) setindex_preserve_graph!(ψ_vidal, noprime(invroot_S * ψ_vidal[vsrc]), vsrc) setindex_preserve_graph!(ψ_vidal, noprime(invroot_S * ψ_vidal[vdst]), vdst) @@ -218,8 +220,8 @@ function vidal_itn_canonicalness(ψ::ITensorNetwork, bond_tensors::DataGraph) end """Function to measure the 'canonicalness' of a state in the Symmetric Gauge""" -function symmetric_itn_canonicalness(ψ::ITensorNetwork, mts::DataGraph) - ψ_vidal, bond_tensors = symmetric_to_vidal_gauge(ψ, mts) +function symmetric_itn_canonicalness(ψ::ITensorNetwork, pψψ::PartitionedGraph, mts) + ψ_vidal, bond_tensors = symmetric_to_vidal_gauge(ψ, pψψ, mts) return vidal_itn_canonicalness(ψ_vidal, bond_tensors) end diff --git a/src/itensornetwork.jl b/src/itensornetwork.jl index 0b8178c3..85ae4b41 100644 --- a/src/itensornetwork.jl +++ b/src/itensornetwork.jl @@ -272,3 +272,5 @@ function ITensors.ITensor(ψ::ITensorNetwork) return ITensor[ψ[v] for v in vertices(ψ)] end end + +NamedGraphs.parent_graph(ψ::ITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(ψ)) \ No newline at end of file diff --git a/src/partition.jl b/src/partition.jl deleted file mode 100644 index 08b0a88f..00000000 --- a/src/partition.jl +++ /dev/null @@ -1,198 +0,0 @@ -""" - partition_vertices(g::AbstractGraph, subgraph_vertices::Vector) - -Given a graph (`g`) and groups of vertices defining subgraphs of that -graph (`subgraph_vertices`), return a DataGraph storing the subgraph -vertices on the vertices of the graph and with edges denoting -which subgraphs of the original graph have edges connecting them, along with -edge data storing the original edges that were connecting the subgraphs. -""" -function partition_vertices(g::AbstractGraph, subgraph_vertices) - partitioned_vertices = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), Dictionary(subgraph_vertices) - ) - for e in edges(g) - s1 = findfirst_on_vertices( - subgraph_vertices -> src(e) ∈ subgraph_vertices, partitioned_vertices - ) - s2 = findfirst_on_vertices( - subgraph_vertices -> dst(e) ∈ subgraph_vertices, partitioned_vertices - ) - if (!has_edge(partitioned_vertices, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_vertices, s1, s2) - partitioned_vertices[s1 => s2] = Vector{edgetype(g)}() - end - if has_edge(partitioned_vertices, s1, s2) - push!(partitioned_vertices[s1 => s2], e) - end - end - return partitioned_vertices -end - -""" - partition_vertices(g::AbstractGraph; npartitions, nvertices_per_partition, kwargs...) - -Given a graph `g`, partition the vertices of `g` into 'npartitions' partitions -or into partitions with `nvertices_per_partition` vertices per partition. -Try to keep all subgraphs the same size and minimise edges cut between them -Returns a datagraph where each vertex contains the list of vertices involved in that subgraph. The edges state which subgraphs are connected. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function partition_vertices( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return partition_vertices( - g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...) - ) -end - -""" -Find all vertices `v` such that `f(graph[v]) == true` -""" -function findall_on_vertices(f::Function, graph::AbstractDataGraph) - return findall(f, vertex_data(graph)) -end - -""" -Find the vertex `v` such that `f(graph[v]) == true` -""" -function findfirst_on_vertices(f::Function, graph::AbstractDataGraph) - return findfirst(f, vertex_data(graph)) -end - -""" -Find all edges `e` such that `f(graph[e]) == true` -""" -function findall_on_edges(f::Function, graph::AbstractDataGraph) - return findall(f, edge_data(graph)) -end - -""" -Find the edge `e` such that `f(graph[e]) == true` -""" -function findfirst_on_edges(f::Function, graph::AbstractDataGraph) - return findfirst(f, edge_data(graph)) -end - -function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) - return subgraphs(NamedGraph(g), subgraph_vertices) -end - - """ - subgraphs(g::AbstractGraph, subgraph_vertices) - - Return a collection of subgraphs of `g` defined by the subgraph - vertices `subgraph_vertices`. - """ -function subgraphs(g::AbstractGraph, subgraph_vertices) - return map(vs -> subgraph(g, vs), subgraph_vertices) -end - - -""" - subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition, -returning a list of subgraphs. -Try to keep all subgraphs the same size and minimise edges cut between them. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function subgraphs( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) -end - -function partition(g::AbstractSimpleGraph, subgraph_vertices) - return partition(NamedGraph(g), subgraph_vertices) -end - -function partition(g::AbstractGraph, subgraph_vertices) - partitioned_graph = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), subgraphs(g, Dictionary(subgraph_vertices)) - ) - for e in edges(g) - s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) - s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) - if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_graph, s1, s2) - partitioned_graph[s1 => s2] = Dictionary( - [:edges, :edge_data], - [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], - ) - end - if has_edge(partitioned_graph, s1, s2) - push!(partitioned_graph[s1 => s2][:edges], e) - if isassigned(g, e) - set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) - end - end - end - return partitioned_graph -end - -""" - partition(g::AbstractGraph; npartitions::Integer, kwargs...) - partition(g::AbstractGraph, subgraph_vertices) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition. -The partitioning tries to keep all subgraphs the same size and minimize -edges cut between them. - -Alternatively, specify a desired partitioning with a collection of sugraph -vertices. - -Returns a data graph where each vertex contains the corresponding subgraph as vertex data. -The edges indicates which subgraphs are connected, and the edge data stores a dictionary -with two fields. The field `:edges` stores a list of the edges of the original graph -that were connecting the two subgraphs, and `:edge_data` stores a dictionary -mapping edges of the original graph to the data living on the edges of the original -graph, if it existed. - -Therefore, one should be able to extract that data and recreate the original -graph from the results of `partition`. - -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work -if the subgraph vertices aren't specified explicitly. -""" -function partition( - g::AbstractGraph; - npartitions=nothing, - nvertices_per_partition=nothing, - subgraph_vertices=nothing, - kwargs..., -) - if count(isnothing, (npartitions, nvertices_per_partition, subgraph_vertices)) != 2 - error( - "Error: Cannot give multiple/ no partitioning options. Please specify exactly one." - ) - end - - if isnothing(subgraph_vertices) - subgraph_vertices = ITensorNetworks.subgraph_vertices( - g; npartitions, nvertices_per_partition, kwargs... - ) - end - - return partition(g, subgraph_vertices) -end - -""" - TODO: do we want to make it a public function? -""" -function _noncommoninds(partition::DataGraph) - networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] - network = vcat(networks...) - return noncommoninds(network...) -end - -# Util functions for partition -function _commoninds(partition::DataGraph) - networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] - network = vcat(networks...) - outinds = noncommoninds(network...) - allinds = mapreduce(t -> [i for i in inds(t)], vcat, network) - return Vector(setdiff(allinds, outinds)) -end \ No newline at end of file diff --git a/src/partitioneditensornetwork.jl b/src/partitioneditensornetwork.jl deleted file mode 100644 index 4cddbe27..00000000 --- a/src/partitioneditensornetwork.jl +++ /dev/null @@ -1,3 +0,0 @@ -const PartitionedITensorNetwork{V, PV} = PartitionedGraph - -NamedGraphs.parent_graph(pitn::PartitionedITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(unpartitioned_graph(pitn))) diff --git a/src/requires/kahypar.jl b/src/requires/kahypar.jl deleted file mode 100644 index fd4f7b38..00000000 --- a/src/requires/kahypar.jl +++ /dev/null @@ -1,33 +0,0 @@ -set_partitioning_backend!(Backend"KaHyPar"()) - -# https://github.com/kahypar/KaHyPar.jl/issues/20 -KaHyPar.HyperGraph(g::SimpleGraph) = incidence_matrix(g) - -""" - subgraph_vertices(::Backend"KaHyPar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) - -- default_configuration => "cut_kKaHyPar_sea20.ini" -- :edge_cut => "cut_kKaHyPar_sea20.ini" -- :connectivity => "km1_kKaHyPar_sea20.ini" -- imbalance::Number=0.03 -""" -function subgraph_vertices( - ::Backend"KaHyPar", - g::SimpleGraph, - npartitions::Integer; - objective="edge_cut", - alg="kway", - configuration=nothing, - kwargs..., -) - if isnothing(configuration) - configuration = joinpath( - pkgdir(KaHyPar), - "src", - "config", - kahypar_configurations[(objective=objective, alg=alg)], - ) - end - partitions = @suppress KaHyPar.partition(g, npartitions; configuration, kwargs...) - return groupfind(partitions .+ 1) -end diff --git a/src/requires/metis.jl b/src/requires/metis.jl deleted file mode 100644 index 19985484..00000000 --- a/src/requires/metis.jl +++ /dev/null @@ -1,33 +0,0 @@ -set_partitioning_backend!(Backend"Metis"()) - -""" - subgraph_vertices(::Backend"Metis", g::AbstractGraph, npartitions::Integer; alg="recursive") - -Partition the graph `G` in `n` parts. -The partition algorithm is defined by the `alg` keyword: - - :KWAY: multilevel k-way partitioning - - :RECURSIVE: multilevel recursive bisection -""" -function subgraph_vertices( - ::Backend"Metis", g::SimpleGraph, npartitions::Integer; alg="recursive", kwargs... -) - metis_alg = metis_algs[alg] - partitions = Metis.partition(g, npartitions; alg=metis_alg, kwargs...) - return groupfind(Int.(partitions)) -end - -## #= -## Metis.partition(G, n; alg = :KWAY) -## -## Partition the graph `G` in `n` parts. -## The partition algorithm is defined by the `alg` keyword: -## - :KWAY: multilevel k-way partitioning -## - :RECURSIVE: multilevel recursive bisection -## =# -## function partition(g::Metis.Graph, npartitions::Integer) -## return Metis.partition(g, npartitions; alg=:KWAY) -## end -## -## function partition(g::Graph, npartitions::Integer) -## return partition(Metis.graph(adjacency_matrix(g)), npartitions) -## end diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index d3747598..935a00f3 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -2,7 +2,6 @@ using ITensorNetworks using ITensorNetworks: ising_network, belief_propagation, - nested_graph_leaf_vertices, approx_network_region, split_index, contract_inner, @@ -38,15 +37,13 @@ ITensors.disable_warn_order() Oψ[v] = apply(op("Sz", s[v]), ψ[v]) exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) - Z = partition(ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(Z) - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact")) - - numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork(ITensor[apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) - bp_sz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + numerator_tensors = approx_network_region( + pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @test abs.(bp_sz - exact_sz) <= 1e-14 @@ -65,15 +62,13 @@ ITensors.disable_warn_order() Oψ[v] = apply(op("Sz", s[v]), ψ[v]) exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) - Z = partition(ψψ; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(Z) - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact")) - - numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork(ITensor[apply(op("Sz", s[v]), ψ[v])]) - ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) - bp_sz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + numerator_tensors = approx_network_region( + pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @test abs.(bp_sz - exact_sz) <= 1e-14 @@ -92,14 +87,14 @@ ITensors.disable_warn_order() ITensors.contract(ψψ; sequence=contract_seq)[] nsites = 2 - Z = partition(ψψ; nvertices_per_partition=nsites) - mts = message_tensors(Z) - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact"), niters=20) + p_vertices = NamedGraphs.partition_vertices(underlying_graph(ψψ); nvertices_per_partition = nsites) + pψψ = PartitionedGraph(ψψ, p_vertices) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), niters = 20) numerator_network = approx_network_region( - ψψ, mts, vs; verts_tn=ITensorNetwork(ITensor[ψOψ[v] for v in vs]) - ) + pψψ, mts, vs; verts_tn=ITensor[ψOψ[v] for v in vs]) - denominator_network = approx_network_region(ψψ, mts, vs) + denominator_network = approx_network_region(pψψ, mts, vs) bp_szsz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] @@ -114,21 +109,17 @@ ITensors.disable_warn_order() ψ = randomITensorNetwork(s; link_space=χ) ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - nsites = 2 - Zp = partition( - partition(ψψ, group(v -> v[1], vertices(ψψ))); nvertices_per_partition=nsites - ) - Zpp = partition(ψψ; subgraph_vertices=nested_graph_leaf_vertices(Zp)) - mts = message_tensors(Zpp) - mts = belief_propagation(ψψ, mts; contract_kwargs=(; alg="exact"), niters=20) + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = message_tensors(pψψ) + mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), niters = 20) ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) rdm = ITensors.contract( approx_network_region( - ψψ, + pψψ, mts, [(v, 2) for v in vs]; - verts_tn=ITensorNetwork(ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]]), + verts_tn=ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]], ), ) @@ -136,44 +127,44 @@ ITensors.disable_warn_order() rdm /= tr(rdm) eigs = eigvals(rdm) - @test all(>=(0), real(eigs)) && all(==(0), imag(eigs)) @test size(rdm) == (2^length(vs), 2^length(vs)) + @test all(>=(0), real(eigs)) && all(==(0), imag(eigs)) #Test more advanced block BP with MPS message tensors on a grid - g_dims = (5, 4) - g = named_grid(g_dims) - s = siteinds("S=1/2", g) - χ = 2 - ψ = randomITensorNetwork(s; link_space=χ) - maxdim = 16 - v = (2, 2) - - ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) - Oψ = copy(ψ) - Oψ[v] = apply(op("Sz", s[v]), ψ[v]) - ψOψ = flatten_networks(ψ, dag(Oψ); combine_linkinds=false, map_bra_linkinds=prime) - - combiners = linkinds_combiners(ψψ) - ψψ = combine_linkinds(ψψ, combiners) - ψOψ = combine_linkinds(ψOψ, combiners) - - Z = partition(ψψ, group(v -> v[1], vertices(ψψ))) - mts = message_tensors(Z) - mts = belief_propagation( - ψψ, - mts; - contract_kwargs=(; - alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-16, maxdim - ), - ) - - numerator_network = approx_network_region(ψψ, mts, [v]; verts_tn=ITensorNetwork(ψOψ[v])) - - denominator_network = approx_network_region(ψψ, mts, [v]) - bp_sz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] - - exact_sz = - contract_boundary_mps(ψOψ; cutoff=1e-16) / contract_boundary_mps(ψψ; cutoff=1e-16) - - @test abs.(bp_sz - exact_sz) <= 1e-5 + # g_dims = (5, 4) + # g = named_grid(g_dims) + # s = siteinds("S=1/2", g) + # χ = 2 + # ψ = randomITensorNetwork(s; link_space=χ) + # maxdim = 16 + # v = (2, 2) + + # ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) + # Oψ = copy(ψ) + # Oψ[v] = apply(op("Sz", s[v]), ψ[v]) + # ψOψ = flatten_networks(ψ, dag(Oψ); combine_linkinds=false, map_bra_linkinds=prime) + + # combiners = linkinds_combiners(ψψ) + # ψψ = combine_linkinds(ψψ, combiners) + # ψOψ = combine_linkinds(ψOψ, combiners) + + # Z = partition(ψψ, group(v -> v[1], vertices(ψψ))) + # mts = message_tensors(Z) + # mts = belief_propagation( + # ψψ, + # mts; + # contract_kwargs=(; + # alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-16, maxdim + # ), + # ) + + # numerator_network = approx_network_region(ψψ, mts, [v]; verts_tn=ITensorNetwork(ψOψ[v])) + + # denominator_network = approx_network_region(ψψ, mts, [v]) + # bp_sz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] + + # exact_sz = + # contract_boundary_mps(ψOψ; cutoff=1e-16) / contract_boundary_mps(ψψ; cutoff=1e-16) + + # @test abs.(bp_sz - exact_sz) <= 1e-5 end diff --git a/test/test_gauging.jl b/test/test_gauging.jl index 5d5e6c77..0d1ba3cb 100644 --- a/test/test_gauging.jl +++ b/test/test_gauging.jl @@ -25,32 +25,31 @@ using SplitApplyCombine Random.seed!(5467) ψ = randomITensorNetwork(s; link_space=χ) - ψ_symm, ψ_symm_mts = symmetric_gauge(ψ; niters=50) + ψ_symm, pψψ_symm, ψ_symm_mts = symmetric_gauge(ψ; niters=50) + + @test symmetric_itn_canonicalness(ψ_symm, pψψ_symm, ψ_symm_mts) < 1e-5 #Test we just did a gauge transform and didn't change the overall network @test contract_inner(ψ_symm, ψ) / sqrt(contract_inner(ψ_symm, ψ_symm) * contract_inner(ψ, ψ)) ≈ 1.0 - ψψ_symm = ψ_symm ⊗ prime(dag(ψ_symm); sites=[]) - Z = partition( - ψψ_symm; subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ_symm)))) + ψψ_symm_V2 = ψ_symm ⊗ prime(dag(ψ_symm); sites=[]) + pψψ_symm_V2 = PartitionedGraph( + ψψ_symm_V2, collect(values(group(v -> v[1], vertices(ψψ_symm_V2)))) ) - ψ_symm_mts_V2 = message_tensors(Z) + ψ_symm_mts_V2 = message_tensors(pψψ_symm_V2) ψ_symm_mts_V2 = belief_propagation( - ψψ_symm, ψ_symm_mts_V2; contract_kwargs=(; alg="exact"), niters=50 + pψψ_symm_V2, ψ_symm_mts_V2; contract_kwargs=(; alg="exact"), niters=50 ) - for e in edges(ψ_symm_mts_V2) + for m_e in values(ψ_symm_mts_V2) #Test all message tensors are approximately diagonal - m_e = ψ_symm_mts_V2[e][first(vertices(ψ_symm_mts_V2[e]))] @test diagITensor(vector(diag(m_e)), inds(m_e)) ≈ m_e atol = 1e-8 end - @test symmetric_itn_canonicalness(ψ_symm, ψ_symm_mts) < 1e-5 - ψ_vidal, bond_tensors = vidal_gauge(ψ; target_canonicalness=1e-6) @test vidal_itn_canonicalness(ψ_vidal, bond_tensors) < 1e-5 - ψ_vidal, bond_tensors = symmetric_to_vidal_gauge(ψ_symm, ψ_symm_mts) + ψ_vidal, bond_tensors = symmetric_to_vidal_gauge(ψ_symm, pψψ_symm, ψ_symm_mts) @test vidal_itn_canonicalness(ψ_vidal, bond_tensors) < 1e-5 end From cdcfec4b80345287bb2c75674234d878c51c4e84 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 17 Jan 2024 11:18:05 -0500 Subject: [PATCH 04/12] Modifications to apply_bp examples --- examples/apply/apply_bp/apply_bp.jl | 57 ++++++------ examples/apply/apply_bp/apply_bp_run.jl | 24 ++--- examples/belief_propagation/bpexample.jl | 15 ++-- examples/belief_propagation/sqrt_bp.jl | 10 +-- src/beliefpropagation/beliefpropagation.jl | 16 +++- .../sqrt_beliefpropagation.jl | 5 ++ src/gauging.jl | 6 +- test/test_apply.jl | 23 ++--- test/test_belief_propagation.jl | 89 +++++++++---------- test/test_gauging.jl | 5 +- 10 files changed, 119 insertions(+), 131 deletions(-) diff --git a/examples/apply/apply_bp/apply_bp.jl b/examples/apply/apply_bp/apply_bp.jl index 3b02b923..2defcc28 100644 --- a/examples/apply/apply_bp/apply_bp.jl +++ b/examples/apply/apply_bp/apply_bp.jl @@ -4,10 +4,8 @@ using ITensorNetworks: belief_propagation, get_environment, contract_inner, - find_subgraph, message_tensors, neighbor_vertices, - nested_graph_leaf_vertices, symmetric_gauge, vidal_gauge, vidal_to_symmetric_gauge, @@ -24,14 +22,13 @@ using LinearAlgebra using SplitApplyCombine using OMEinsumContractionOrders -function expect_bp(opname, v, ψ, mts) +function expect_bp(opname, v, ψ, pψψ, mts) s = siteinds(ψ) - ψψ = norm_network(ψ) - numerator_network = approx_network_region( - ψψ, mts, [(v, 1)]; verts_tn=ITensorNetwork(ITensor[apply(op(opname, s[v]), ψ[v])]) + numerator_tensors = approx_network_region( + pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op(opname, s[v]), ψ[v])] ) - denominator_network = approx_network_region(ψψ, mts, [(v, 1)]) - return contract(numerator_network)[] / contract(denominator_network)[] + denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + return contract(numerator_tensors)[] / contract(denominator_tensors)[] end function vertex_array(ψ, v, v⃗ⱼ) @@ -52,26 +49,26 @@ function simple_update_bp( ) println("Simple update, BP") ψψ = norm_network(ψ) - mts = message_tensors(partition(ψψ, group(v -> v[1], vertices(ψψ)))) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = belief_propagation( - ψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 + pψψ; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 ) + edges = PartitionEdge.(NamedGraphs.edges(partitioned_graph(pψψ))) for layer in eachindex(os) @show layer o⃗ = os[layer] for o in o⃗ v⃗ = neighbor_vertices(ψ, o) - for e in edges(mts) + for e in edges @assert order(only(mts[e])) == 2 - @assert order(only(mts[reverse(e)])) == 2 + @assert order(only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))])) == 2 end @assert length(v⃗) == 2 v1, v2 = v⃗ - s1 = find_subgraph((v1, 1), mts) - s2 = find_subgraph((v2, 1), mts) - envs = get_environment(ψψ, mts, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + pe = NamedGraphs.partition_edge(pψψ, NamedEdge((v1, 1) => (v2, 1))) + envs = get_environment(pψψ, mts, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) obs = observer() # TODO: Make a version of `apply` that accepts message tensors, # and computes the environment and does the message tensor update of the bond internally. @@ -92,29 +89,28 @@ function simple_update_bp( # Update message tensor ψψ = norm_network(ψ) - mts[s1] = ITensorNetwork(dictionary([(v1, 1) => ψψ[v1, 1], (v1, 2) => ψψ[v1, 2]])) - mts[s2] = ITensorNetwork(dictionary([(v2, 1) => ψψ[v2, 1], (v2, 2) => ψψ[v2, 2]])) - mts[s1 => s2] = ITensorNetwork(obs.singular_values) - mts[s2 => s1] = ITensorNetwork(obs.singular_values) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) + mts[pe] = dense.(obs.singular_values) + mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = dense.(obs.singular_values) end if regauge println("regauge") mts = belief_propagation( - ψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 + pψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 ) end end - return ψ, mts + return ψ, pψψ, mts end function simple_update_vidal(os, ψ::ITensorNetwork; maxdim, regauge=false) println("Simple update, Vidal gauge") ψψ = norm_network(ψ) - mts = message_tensors(partition(ψψ, group(v -> v[1], vertices(ψψ)))) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = belief_propagation( - ψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 + pψψ; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 ) - ψ, bond_tensors = vidal_gauge(ψ, mts) + ψ, bond_tensors = vidal_gauge(ψ, pψψ, mts) for layer in eachindex(os) @show layer o⃗ = os[layer] @@ -124,12 +120,11 @@ function simple_update_vidal(os, ψ::ITensorNetwork; maxdim, regauge=false) end if regauge println("regauge") - ψ_symmetric, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) - ψψ = norm_network(ψ_symmetric) + ψ_symmetric, pψψ_symmetric, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) mts = belief_propagation( - ψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 + pψψ_symmetric, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 ) - ψ, bond_tensors = vidal_gauge(ψ_symmetric, mts) + ψ, bond_tensors = vidal_gauge(ψ_symmetric, pψψ_symmetric, mts) end end return ψ, bond_tensors @@ -157,14 +152,14 @@ function main(; ] # BP SU - ψ_bp, mts = simple_update_bp( + ψ_bp, pψψ_bp, mts_bp = simple_update_bp( os, ψ; maxdim=χ, variational_optimization_only, regauge, reduced ) # ψ_bp, mts = vidal_to_symmetric_gauge(vidal_gauge(ψ_bp, mts)...) # Vidal SU ψ_vidal, bond_tensors = simple_update_vidal(os, ψ; maxdim=χ, regauge) - ψ_vidal, mts_vidal = vidal_to_symmetric_gauge(ψ_vidal, bond_tensors) + ψ_vidal, pψψ_vidal, mts_vidal = vidal_to_symmetric_gauge(ψ_vidal, bond_tensors) - return ψ_bp, mts, ψ_vidal, mts_vidal + return ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal end diff --git a/examples/apply/apply_bp/apply_bp_run.jl b/examples/apply/apply_bp/apply_bp_run.jl index 723e70ae..d9e40c90 100644 --- a/examples/apply/apply_bp/apply_bp_run.jl +++ b/examples/apply/apply_bp/apply_bp_run.jl @@ -8,9 +8,9 @@ opname = "RandomUnitary" # graph = named_comb_tree graph = named_grid -dims = (6, 6) +dims = (6,6) -ψ_bp, mts_bp, ψ_vidal, mts_vidal = main(; +ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal = main(; seed=1234, opname, graph, @@ -24,31 +24,33 @@ dims = (6, 6) v = dims .÷ 2 -sz_bp = @show expect_bp("Sz", v, ψ_bp, mts_bp) -sz_vidal = @show expect_bp("Sz", v, ψ_vidal, mts_vidal) +sz_bp = @show expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) +sz_vidal = @show expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) @show abs(sz_bp - sz_vidal) / abs(sz_vidal) # Run BP again mts_bp = belief_propagation( - norm_network(ψ_bp), + pψψ_bp, mts_bp; contract_kwargs=(; alg="exact"), niters=50, - target_precision=1e-5, + target_precision=1e-7, + verbose = true ) mts_vidal = belief_propagation( - norm_network(ψ_vidal), + pψψ_vidal, mts_vidal; contract_kwargs=(; alg="exact"), niters=50, - target_precision=1e-5, + target_precision=1e-7, + verbose = true ) -sz_bp = @show expect_bp("Sz", v, ψ_bp, mts_bp) -sz_vidal = @show expect_bp("Sz", v, ψ_vidal, mts_vidal) +sz_bp = @show expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) +sz_vidal = @show expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) @show abs(sz_bp - sz_vidal) / abs(sz_vidal) -ψ_symmetric, _ = symmetric_gauge(ψ_bp) +ψ_symmetric, _, _ = symmetric_gauge(ψ_bp) v⃗ⱼ = [v .+ (1, 0), v .- (1, 0), v .+ (0, 1), v .- (0, 1)] ψ_bp_v = vertex_array(ψ_bp, v, v⃗ⱼ) diff --git a/examples/belief_propagation/bpexample.jl b/examples/belief_propagation/bpexample.jl index 09e8344f..2fdcd87e 100644 --- a/examples/belief_propagation/bpexample.jl +++ b/examples/belief_propagation/bpexample.jl @@ -31,10 +31,9 @@ function main() #Now do Simple Belief Propagation to Measure Sz on Site v pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) sz_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -44,10 +43,9 @@ function main() #Now do Column-wise General Belief Propagation to Measure Sz on Site v pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) sz_gen_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -66,10 +64,9 @@ function main() ψOψ = combine_linkinds(ψOψ, combiners) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ; itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e]) - mts = belief_propagation(pψψ, mts; contract_kwargs=(;alg="density_matrix",output_structure=path_graph_structure,maxdim = 8,contraction_sequence_alg="optimal")) + mts = belief_propagation(pψψ; itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e], contract_kwargs=(;alg="density_matrix",output_structure=path_graph_structure,maxdim = 8,contraction_sequence_alg="optimal")) numerator_tensors = approx_network_region( - pψψ, mts, [v]; verts_tn=[ψOψ[v]]) + pψψ, mts, [v]; verts_tensors=[ψOψ[v]]) denominator_tensors = approx_network_region(pψψ, mts, [v]) sz_MPS_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] diff --git a/examples/belief_propagation/sqrt_bp.jl b/examples/belief_propagation/sqrt_bp.jl index d386d863..a0014569 100644 --- a/examples/belief_propagation/sqrt_bp.jl +++ b/examples/belief_propagation/sqrt_bp.jl @@ -33,11 +33,10 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) #Now do Simple Belief Propagation to Measure Sz on Site v pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = @time belief_propagation(pψψ, mts; niters, contract_kwargs=(; alg="exact")) + mts = @time belief_propagation(pψψ; niters, contract_kwargs=(; alg="exact")) numerator_network = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tn=ITensor[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])]) denominator_network = approx_network_region(pψψ, mts, [(v, 1)]) sz_bp = ITensors.contract( @@ -50,11 +49,10 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) "Simple Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_bp) ) - mts_sqrt = message_tensors(pψψ) - mts_sqrt = @time sqrt_belief_propagation(pψψ, mts_sqrt; niters) + mts_sqrt = @time sqrt_belief_propagation(pψψ; niters) numerator_network = approx_network_region( - pψψ, mts_sqrt, [(v, 1)]; verts_tn=ITensor[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts_sqrt, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])]) denominator_network = approx_network_region(pψψ, mts_sqrt, [(v, 1)]) sz_sqrt_bp = contract( diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 166feb2b..6c3c4e0d 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -35,8 +35,12 @@ function update_message_tensor( mt = contract(contract_list; sequence=contraction_sequence(contract_list; alg="optimal")) end - mt = ITensor(mt) - normalize!(mt) + if isa(mt, ITensor) + mt = ITensor[mt] + elseif isa(mt, ITensorNetwork) + mt = ITensor(mt) + end + normalize!.(mt) return mt end @@ -129,6 +133,10 @@ function belief_propagation( return mts end +function belief_propagation(ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))], kwargs...) + mts = message_tensors(ptn; itensor_constructor) + return belief_propagation(ptn, mts; kwargs...) +end """ Given a subet of vertices of a given Tensor Network and the Message Tensors for that network, return a Dictionary with the involved subgraphs as keys and the vector of tensors associated with that subgraph as values Specifically, the contraction of the environment tensors and tn[vertices] will be a scalar. @@ -155,9 +163,9 @@ function approx_network_region( ptn::PartitionedGraph, mts, verts::Vector; - verts_tn=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], + verts_tensors=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], ) environment_tensors = get_environment(ptn, mts, verts) - return vcat(environment_tensors, verts_tn) + return vcat(environment_tensors, verts_tensors) end diff --git a/src/beliefpropagation/sqrt_beliefpropagation.jl b/src/beliefpropagation/sqrt_beliefpropagation.jl index 32850d2c..1e20698a 100644 --- a/src/beliefpropagation/sqrt_beliefpropagation.jl +++ b/src/beliefpropagation/sqrt_beliefpropagation.jl @@ -67,6 +67,11 @@ function sqrt_belief_propagation( return sqr_message_tensors(sqrt_mts) end +function sqrt_belief_propagation(ptn::PartitionedGraph; kwargs...) + mts = message_tensors(ptn) + return sqrt_belief_propagation(ptn, mts; kwargs...) +end + function update_sqrt_message_tensor( pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts; ) diff --git a/src/gauging.jl b/src/gauging.jl index 7a6fb65a..7965e30e 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -32,10 +32,10 @@ function vidal_gauge( edge_ind_sim = sim(edge_ind) X_D, X_U = eigen( - ITensor(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) Y_D, Y_U = eigen( - ITensor(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -130,7 +130,7 @@ function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vsrc]), vsrc) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vdst]), vdst) - ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy(ITensor[bond_tensors[e]]), copy(ITensor[bond_tensors[e]]) + ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy(ITensor[dense(bond_tensors[e])]), copy(ITensor[dense(bond_tensors[e])]) end ψψsymm = norm_network(ψsymm) diff --git a/test/test_apply.jl b/test/test_apply.jl index 64909adf..bc0067ad 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -4,7 +4,6 @@ using ITensorNetworks: get_environment, contract_inner, message_tensors, - nested_graph_leaf_vertices, vidal_gauge, vidal_to_symmetric_gauge, norm_network @@ -30,24 +29,16 @@ using SplitApplyCombine ψψ = norm_network(ψ) #Simple Belief Propagation Grouping - vertex_groupsSBP = nested_graph_leaf_vertices( - partition(partition(ψψ, group(v -> v[1], vertices(ψψ))); nvertices_per_partition=1) - ) - Z = partition(ψψ; subgraph_vertices=vertex_groupsSBP) - mtsSBP = message_tensors(Z) - mtsSBP = belief_propagation(ψψ, mtsSBP; contract_kwargs=(; alg="exact"), niters=50) - envsSBP = get_environment(ψψ, mtsSBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + pψψ_SBP =PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mtsSBP = belief_propagation(pψψ_SBP; contract_kwargs=(; alg="exact"), niters=50) + envsSBP = get_environment(pψψ_SBP, mtsSBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) - ψ_vidal, bond_tensors = vidal_gauge(ψ, mtsSBP) + ψ_vidal, bond_tensors = vidal_gauge(ψ, pψψ_SBP, mtsSBP) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) - vertex_groupsGBP = nested_graph_leaf_vertices( - partition(partition(ψψ, group(v -> v[1][1], vertices(ψψ))); nvertices_per_partition=1) - ) - Z = partition(ψψ; subgraph_vertices=vertex_groupsSBP) - mtsGBP = message_tensors(Z) - mtsGBP = belief_propagation(ψψ, mtsGBP; contract_kwargs=(; alg="exact"), niters=50) - envsGBP = get_environment(ψψ, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + pψψ_GBP =PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) + mtsGBP = belief_propagation(pψψ_GBP; contract_kwargs=(; alg="exact"), niters=50) + envsGBP = get_environment(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) ngates = 5 diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index 935a00f3..6cc2f4a7 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -38,10 +38,9 @@ ITensors.disable_warn_order() exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -63,10 +62,9 @@ ITensors.disable_warn_order() exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tn=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -89,10 +87,9 @@ ITensors.disable_warn_order() nsites = 2 p_vertices = NamedGraphs.partition_vertices(underlying_graph(ψψ); nvertices_per_partition = nsites) pψψ = PartitionedGraph(ψψ, p_vertices) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), niters = 20) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters = 20) numerator_network = approx_network_region( - pψψ, mts, vs; verts_tn=ITensor[ψOψ[v] for v in vs]) + pψψ, mts, vs; verts_tensors=ITensor[ψOψ[v] for v in vs]) denominator_network = approx_network_region(pψψ, mts, vs) bp_szsz = @@ -110,8 +107,7 @@ ITensors.disable_warn_order() ψψ = ψ ⊗ prime(dag(ψ); sites=[]) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), niters = 20) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters = 20) ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) rdm = ITensors.contract( @@ -119,7 +115,7 @@ ITensors.disable_warn_order() pψψ, mts, [(v, 2) for v in vs]; - verts_tn=ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]], + verts_tensors=ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]], ), ) @@ -131,40 +127,37 @@ ITensors.disable_warn_order() @test all(>=(0), real(eigs)) && all(==(0), imag(eigs)) #Test more advanced block BP with MPS message tensors on a grid - # g_dims = (5, 4) - # g = named_grid(g_dims) - # s = siteinds("S=1/2", g) - # χ = 2 - # ψ = randomITensorNetwork(s; link_space=χ) - # maxdim = 16 - # v = (2, 2) - - # ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) - # Oψ = copy(ψ) - # Oψ[v] = apply(op("Sz", s[v]), ψ[v]) - # ψOψ = flatten_networks(ψ, dag(Oψ); combine_linkinds=false, map_bra_linkinds=prime) - - # combiners = linkinds_combiners(ψψ) - # ψψ = combine_linkinds(ψψ, combiners) - # ψOψ = combine_linkinds(ψOψ, combiners) - - # Z = partition(ψψ, group(v -> v[1], vertices(ψψ))) - # mts = message_tensors(Z) - # mts = belief_propagation( - # ψψ, - # mts; - # contract_kwargs=(; - # alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-16, maxdim - # ), - # ) - - # numerator_network = approx_network_region(ψψ, mts, [v]; verts_tn=ITensorNetwork(ψOψ[v])) - - # denominator_network = approx_network_region(ψψ, mts, [v]) - # bp_sz = ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] - - # exact_sz = - # contract_boundary_mps(ψOψ; cutoff=1e-16) / contract_boundary_mps(ψψ; cutoff=1e-16) - - # @test abs.(bp_sz - exact_sz) <= 1e-5 + g_dims = (5, 4) + g = named_grid(g_dims) + s = siteinds("S=1/2", g) + χ = 2 + ψ = randomITensorNetwork(s; link_space=χ) + maxdim = 16 + v = (2, 2) + + ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) + Oψ = copy(ψ) + Oψ[v] = apply(op("Sz", s[v]), ψ[v]) + ψOψ = flatten_networks(ψ, dag(Oψ); combine_linkinds=false, map_bra_linkinds=prime) + + combiners = linkinds_combiners(ψψ) + ψψ = combine_linkinds(ψψ, combiners) + ψOψ = combine_linkinds(ψOψ, combiners) + + pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + mts = belief_propagation( + pψψ; + contract_kwargs=(; + alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-16, maxdim + ), + ) + + numerator_tensors = approx_network_region(pψψ, mts, [v]; verts_tensors=ITensor[ψOψ[v]]) + denominator_tensors = approx_network_region(pψψ, mts, [v]) + bp_sz = ITensors.contract(numerator_tensors)[] / ITensors.contract(denominator_tensors)[] + + exact_sz = + contract_boundary_mps(ψOψ; cutoff=1e-16) / contract_boundary_mps(ψψ; cutoff=1e-16) + + @test abs.(bp_sz - exact_sz) <= 1e-5 end diff --git a/test/test_gauging.jl b/test/test_gauging.jl index 0d1ba3cb..04067b43 100644 --- a/test/test_gauging.jl +++ b/test/test_gauging.jl @@ -37,14 +37,13 @@ using SplitApplyCombine pψψ_symm_V2 = PartitionedGraph( ψψ_symm_V2, collect(values(group(v -> v[1], vertices(ψψ_symm_V2)))) ) - ψ_symm_mts_V2 = message_tensors(pψψ_symm_V2) ψ_symm_mts_V2 = belief_propagation( - pψψ_symm_V2, ψ_symm_mts_V2; contract_kwargs=(; alg="exact"), niters=50 + pψψ_symm_V2; contract_kwargs=(; alg="exact"), niters=50 ) for m_e in values(ψ_symm_mts_V2) #Test all message tensors are approximately diagonal - @test diagITensor(vector(diag(m_e)), inds(m_e)) ≈ m_e atol = 1e-8 + @test diagITensor(vector(diag(only(m_e))), inds(only(m_e))) ≈ only(m_e) atol = 1e-8 end ψ_vidal, bond_tensors = vidal_gauge(ψ; target_canonicalness=1e-6) From b5471b45b01a92967934c539e278b3f2febfc93e Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 17 Jan 2024 12:23:18 -0500 Subject: [PATCH 05/12] File Management --- examples/boundary.jl | 5 +- examples/distances.jl | 2 +- .../dynamics/heavy_hex_ising_real_tebd.jl | 2 +- examples/group_partition.jl | 17 -- .../binary_tree_partition.jl | 129 ++++++++++++ src/approx_itensornetwork/partition.jl | 198 ++++++++++++++++++ test/test_examples/test_examples.jl | 3 - test/test_partitioneditensornetwork.jl | 35 ---- 8 files changed, 332 insertions(+), 59 deletions(-) delete mode 100644 examples/group_partition.jl create mode 100644 src/approx_itensornetwork/binary_tree_partition.jl create mode 100644 src/approx_itensornetwork/partition.jl delete mode 100644 test/test_partitioneditensornetwork.jl diff --git a/examples/boundary.jl b/examples/boundary.jl index c10aecbf..0d86e058 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -8,8 +8,9 @@ tn = ITensorNetwork(named_grid((6, 3)); link_space=4) @visualize tn -g = subgraph_vertices(tn; nvertices_per_partition=2) -sub_vs_1, sub_vs_2 = g[1], g[2] + +ptn = PartitionedGraph(tn, NamedGraphs.partition_vertices(underlying_graph(tn); nvertices_per_partition = 2)) +sub_vs_1, sub_vs_2 =vertices(ptn, PartitionVertex(1)), vertices(ptn, PartitionVertex(2)) @show (1, 1) ∈ sub_vs_1 @show (6, 3) ∈ sub_vs_2 diff --git a/examples/distances.jl b/examples/distances.jl index 38df7296..dcd71ab1 100644 --- a/examples/distances.jl +++ b/examples/distances.jl @@ -14,4 +14,4 @@ t = dijkstra_tree(ψ, only(center(ψ))) @show a_star(ψ, (2, 1), (2, 5)) @show mincut_partitions(ψ) @show mincut_partitions(ψ, (1, 1), (3, 5)) -@show subgraph_vertices(ψ; npartitions=2) +@show NamedGraphs.partition_vertices(underlying_graph(ψ); npartitions=2) diff --git a/examples/dynamics/heavy_hex_ising_real_tebd.jl b/examples/dynamics/heavy_hex_ising_real_tebd.jl index d6e492e7..f4ad3eca 100644 --- a/examples/dynamics/heavy_hex_ising_real_tebd.jl +++ b/examples/dynamics/heavy_hex_ising_real_tebd.jl @@ -47,7 +47,7 @@ function expect_state_SBP( vs_braket = [(v, 1) for v in vs] numerator_tensors = approx_network_region( - pψψ, mts, vs_braket; verts_tn=ITensor[Oψ[v] for v in vs]) + pψψ, mts, vs_braket; verts_tensors=ITensor[Oψ[v] for v in vs]) denominator_tensors = approx_network_region(pψψ, mts, vs_braket) num_seq = contraction_sequence(numerator_tensors; alg="optimal") den_seq = contraction_sequence(denominator_tensors; alg="optimal") diff --git a/examples/group_partition.jl b/examples/group_partition.jl deleted file mode 100644 index 1a3c12ae..00000000 --- a/examples/group_partition.jl +++ /dev/null @@ -1,17 +0,0 @@ -using ITensors -using Graphs -using NamedGraphs -using ITensorNetworks -using SplitApplyCombine -using Metis - -s = siteinds("S=1/2", named_grid(8)) -tn = ITensorNetwork(s; link_space=2) -Z = prime(tn; sites=[]) ⊗ tn -vertex_groups = group(v -> v[1], vertices(Z)) -# Create two layers of partitioning -Z_p = partition(partition(Z, vertex_groups); nvertices_per_partition=2) -# Flatten the partitioned partitions -Z_verts = [ - reduce(vcat, (vertices(Z_p[vp][v]) for v in vertices(Z_p[vp]))) for vp in vertices(Z_p) -] diff --git a/src/approx_itensornetwork/binary_tree_partition.jl b/src/approx_itensornetwork/binary_tree_partition.jl new file mode 100644 index 00000000..cff0c31e --- /dev/null +++ b/src/approx_itensornetwork/binary_tree_partition.jl @@ -0,0 +1,129 @@ +function _binary_partition(tn::ITensorNetwork, source_inds::Vector{<:Index}) + external_inds = noncommoninds(Vector{ITensor}(tn)...) + # add delta tensor to each external ind + external_sim_ind = [sim(ind) for ind in external_inds] + tn = map_data(t -> replaceinds(t, external_inds => external_sim_ind), tn; edges=[]) + tn_wo_deltas = rename_vertices(v -> v[1], subgraph(v -> v[2] == 1, tn)) + deltas = Vector{ITensor}(subgraph(v -> v[2] == 2, tn)) + scalars = rename_vertices(v -> v[1], subgraph(v -> v[2] == 3, tn)) + new_deltas = [ + delta(external_inds[i], external_sim_ind[i]) for i in 1:length(external_inds) + ] + deltas = [deltas..., new_deltas...] + tn = disjoint_union(tn_wo_deltas, ITensorNetwork(deltas), scalars) + p1, p2 = _mincut_partition_maxweightoutinds( + tn, source_inds, setdiff(external_inds, source_inds) + ) + source_tn = _contract_deltas(subgraph(tn, p1)) + remain_tn = _contract_deltas(subgraph(tn, p2)) + outinds_source = noncommoninds(Vector{ITensor}(source_tn)...) + outinds_remain = noncommoninds(Vector{ITensor}(remain_tn)...) + common_inds = intersect(outinds_source, outinds_remain) + @assert ( + length(external_inds) == + length(union(outinds_source, outinds_remain)) - length(common_inds) + ) + # We want the output two tns be connected to each other, so that the output + # of `binary_tree_partition` is a partition with a binary tree structure. + # Below we check if `source_tn` and `remain_tn` are connected, if not adding + # each tn a scalar tensor to force them to be connected. + if common_inds == [] + @info "_binary_partition outputs are not connected" + ind = Index(1, "unit_scalar_ind") + t1 = ITensor([1.0], ind) + t2 = ITensor([1.0], ind) + v1 = (nv(scalars) + 1, 3) + v2 = (nv(scalars) + 2, 3) + add_vertex!(source_tn, v1) + add_vertex!(remain_tn, v2) + source_tn[v1] = t1 + remain_tn[v2] = t2 + end + return source_tn, remain_tn +end + +""" +Given an input tn and a rooted binary tree of indices, return a partition of tn with the +same binary tree structure as inds_btree. +Note: in the output partition, we add multiple delta tensors to the network so that + the output graph is guaranteed to be the same binary tree as inds_btree. +Note: in the output partition, we add multiple scalar tensors. These tensors are used to + make the output partition connected, even if the input `tn` is disconnected. +Note: in the output partition, tensor vertex names will be changed. For a given input + tensor with vertex name `v``, its name in the output partition will be `(v, 1)`. Any + delta tensor will have name `(v, 2)`, and any scalar tensor used to maintain the connectivity + of the partition will have name `(v, 3)`. +Note: for a given binary tree with n indices, the output partition will contain 2n-1 vertices, + with each leaf vertex corresponding to a sub tn adjacent to one output index. Keeping these + leaf vertices in the partition makes later `approx_itensornetwork` algorithms more efficient. +Note: name of vertices in the output partition are the same as the name of vertices + in `inds_btree`. +""" +function partition( + ::Algorithm"mincut_recursive_bisection", tn::ITensorNetwork, inds_btree::DataGraph +) + @assert _is_rooted_directed_binary_tree(inds_btree) + output_tns = Vector{ITensorNetwork}() + output_deltas_vector = Vector{Vector{ITensor}}() + scalars_vector = Vector{Vector{ITensor}}() + # Mapping each vertex of the binary tree to a tn representing the partition + # of the subtree containing this vertex and its descendant vertices. + leaves = leaf_vertices(inds_btree) + root = _root(inds_btree) + v_to_subtree_tn = Dict{vertextype(inds_btree),ITensorNetwork}() + v_to_subtree_tn[root] = disjoint_union(tn, ITensorNetwork()) + for v in pre_order_dfs_vertices(inds_btree, root) + @assert haskey(v_to_subtree_tn, v) + input_tn = v_to_subtree_tn[v] + if !is_leaf(inds_btree, v) + c1, c2 = child_vertices(inds_btree, v) + descendant_c1 = pre_order_dfs_vertices(inds_btree, c1) + indices = [inds_btree[l] for l in intersect(descendant_c1, leaves)] + tn1, input_tn = _binary_partition(input_tn, indices) + v_to_subtree_tn[c1] = tn1 + descendant_c2 = pre_order_dfs_vertices(inds_btree, c2) + indices = [inds_btree[l] for l in intersect(descendant_c2, leaves)] + tn1, input_tn = _binary_partition(input_tn, indices) + v_to_subtree_tn[c2] = tn1 + end + tn = rename_vertices(u -> u[1], subgraph(u -> u[2] == 1, input_tn)) + deltas = Vector{ITensor}(subgraph(u -> u[2] == 2, input_tn)) + scalars = Vector{ITensor}(subgraph(u -> u[2] == 3, input_tn)) + push!(output_tns, tn) + push!(output_deltas_vector, deltas) + push!(scalars_vector, scalars) + end + # In subgraph_vertices, each element is a vector of vertices to be + # grouped in one partition. + subgraph_vs = Vector{Vector{Tuple}}() + delta_num = 0 + scalar_num = 0 + for (tn, deltas, scalars) in zip(output_tns, output_deltas_vector, scalars_vector) + vs = Vector{Tuple}([(v, 1) for v in vertices(tn)]) + vs = vcat(vs, [(i + delta_num, 2) for i in 1:length(deltas)]) + vs = vcat(vs, [(i + scalar_num, 3) for i in 1:length(scalars)]) + push!(subgraph_vs, vs) + delta_num += length(deltas) + scalar_num += length(scalars) + end + out_tn = ITensorNetwork() + for tn in output_tns + for v in vertices(tn) + add_vertex!(out_tn, v) + out_tn[v] = tn[v] + end + end + tn_deltas = ITensorNetwork(vcat(output_deltas_vector...)) + tn_scalars = ITensorNetwork(vcat(scalars_vector...)) + par = partition(disjoint_union(out_tn, tn_deltas, tn_scalars), subgraph_vs) + @assert is_tree(par) + name_map = Dict() + for (i, v) in enumerate(pre_order_dfs_vertices(inds_btree, root)) + name_map[i] = v + end + return rename_vertices(par, name_map) +end + +function partition(tn::ITensorNetwork, inds_btree::DataGraph; alg::String) + return partition(Algorithm(alg), tn, inds_btree) +end diff --git a/src/approx_itensornetwork/partition.jl b/src/approx_itensornetwork/partition.jl new file mode 100644 index 00000000..2ac4c0dc --- /dev/null +++ b/src/approx_itensornetwork/partition.jl @@ -0,0 +1,198 @@ +""" + partition_vertices(g::AbstractGraph, subgraph_vertices::Vector) + +Given a graph (`g`) and groups of vertices defining subgraphs of that +graph (`subgraph_vertices`), return a DataGraph storing the subgraph +vertices on the vertices of the graph and with edges denoting +which subgraphs of the original graph have edges connecting them, along with +edge data storing the original edges that were connecting the subgraphs. +""" +function partition_vertices(g::AbstractGraph, subgraph_vertices) + partitioned_vertices = DataGraph( + NamedGraph(eachindex(subgraph_vertices)), Dictionary(subgraph_vertices) + ) + for e in edges(g) + s1 = findfirst_on_vertices( + subgraph_vertices -> src(e) ∈ subgraph_vertices, partitioned_vertices + ) + s2 = findfirst_on_vertices( + subgraph_vertices -> dst(e) ∈ subgraph_vertices, partitioned_vertices + ) + if (!has_edge(partitioned_vertices, s1, s2) && s1 ≠ s2) + add_edge!(partitioned_vertices, s1, s2) + partitioned_vertices[s1 => s2] = Vector{edgetype(g)}() + end + if has_edge(partitioned_vertices, s1, s2) + push!(partitioned_vertices[s1 => s2], e) + end + end + return partitioned_vertices +end + +""" + partition_vertices(g::AbstractGraph; npartitions, nvertices_per_partition, kwargs...) + +Given a graph `g`, partition the vertices of `g` into 'npartitions' partitions +or into partitions with `nvertices_per_partition` vertices per partition. +Try to keep all subgraphs the same size and minimise edges cut between them +Returns a datagraph where each vertex contains the list of vertices involved in that subgraph. The edges state which subgraphs are connected. +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. +""" +function partition_vertices( + g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + return partition_vertices( + g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...) + ) +end + +""" +Find all vertices `v` such that `f(graph[v]) == true` +""" +function findall_on_vertices(f::Function, graph::AbstractDataGraph) + return findall(f, vertex_data(graph)) +end + +""" +Find the vertex `v` such that `f(graph[v]) == true` +""" +function findfirst_on_vertices(f::Function, graph::AbstractDataGraph) + return findfirst(f, vertex_data(graph)) +end + +""" +Find all edges `e` such that `f(graph[e]) == true` +""" +function findall_on_edges(f::Function, graph::AbstractDataGraph) + return findall(f, edge_data(graph)) +end + +""" +Find the edge `e` such that `f(graph[e]) == true` +""" +function findfirst_on_edges(f::Function, graph::AbstractDataGraph) + return findfirst(f, edge_data(graph)) +end + +function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) + return subgraphs(NamedGraph(g), subgraph_vertices) +end + + """ + subgraphs(g::AbstractGraph, subgraph_vertices) + + Return a collection of subgraphs of `g` defined by the subgraph + vertices `subgraph_vertices`. + """ +function subgraphs(g::AbstractGraph, subgraph_vertices) + return map(vs -> subgraph(g, vs), subgraph_vertices) +end + + +""" + subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) + +Given a graph `g`, partition `g` into `npartitions` partitions +or into partitions with `nvertices_per_partition` vertices per partition, +returning a list of subgraphs. +Try to keep all subgraphs the same size and minimise edges cut between them. +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. +""" +function subgraphs( + g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) +end + +function partition(g::AbstractSimpleGraph, subgraph_vertices) + return partition(NamedGraph(g), subgraph_vertices) +end + +function partition(g::AbstractGraph, subgraph_vertices) + partitioned_graph = DataGraph( + NamedGraph(eachindex(subgraph_vertices)), subgraphs(g, Dictionary(subgraph_vertices)) + ) + for e in edges(g) + s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) + s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) + if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) + add_edge!(partitioned_graph, s1, s2) + partitioned_graph[s1 => s2] = Dictionary( + [:edges, :edge_data], + [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], + ) + end + if has_edge(partitioned_graph, s1, s2) + push!(partitioned_graph[s1 => s2][:edges], e) + if isassigned(g, e) + set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) + end + end + end + return partitioned_graph +end + +""" + partition(g::AbstractGraph; npartitions::Integer, kwargs...) + partition(g::AbstractGraph, subgraph_vertices) + +Given a graph `g`, partition `g` into `npartitions` partitions +or into partitions with `nvertices_per_partition` vertices per partition. +The partitioning tries to keep all subgraphs the same size and minimize +edges cut between them. + +Alternatively, specify a desired partitioning with a collection of sugraph +vertices. + +Returns a data graph where each vertex contains the corresponding subgraph as vertex data. +The edges indicates which subgraphs are connected, and the edge data stores a dictionary +with two fields. The field `:edges` stores a list of the edges of the original graph +that were connecting the two subgraphs, and `:edge_data` stores a dictionary +mapping edges of the original graph to the data living on the edges of the original +graph, if it existed. + +Therefore, one should be able to extract that data and recreate the original +graph from the results of `partition`. + +A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work +if the subgraph vertices aren't specified explicitly. +""" +function partition( + g::AbstractGraph; + npartitions=nothing, + nvertices_per_partition=nothing, + subgraph_vertices=nothing, + kwargs..., +) + if count(isnothing, (npartitions, nvertices_per_partition, subgraph_vertices)) != 2 + error( + "Error: Cannot give multiple/ no partitioning options. Please specify exactly one." + ) + end + + if isnothing(subgraph_vertices) + subgraph_vertices = NamedGraphs.partition_vertices( + g; npartitions, nvertices_per_partition, kwargs... + ) + end + + return partition(g, subgraph_vertices) +end + +""" + TODO: do we want to make it a public function? +""" +function _noncommoninds(partition::DataGraph) + networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] + network = vcat(networks...) + return noncommoninds(network...) +end + +# Util functions for partition +function _commoninds(partition::DataGraph) + networks = [Vector{ITensor}(partition[v]) for v in vertices(partition)] + network = vcat(networks...) + outinds = noncommoninds(network...) + allinds = mapreduce(t -> [i for i in inds(t)], vcat, network) + return Vector(setdiff(allinds, outinds)) +end \ No newline at end of file diff --git a/test/test_examples/test_examples.jl b/test/test_examples/test_examples.jl index 8afc3be4..e348b2c6 100644 --- a/test/test_examples/test_examples.jl +++ b/test/test_examples/test_examples.jl @@ -8,7 +8,6 @@ using Test "boundary.jl", "distances.jl", "examples.jl", - "group_partition.jl", "mincut.jl", "mps.jl", "peps.jl", @@ -28,8 +27,6 @@ using Test if !Sys.iswindows() example_files = [ joinpath("contraction_sequence", "contraction_sequence.jl"), - joinpath("partition", "kahypar_vs_metis.jl"), - joinpath("partition", "partitioning.jl"), ] @testset "Test $example_file (using KaHyPar, so no Windows support)" for example_file in example_files diff --git a/test/test_partitioneditensornetwork.jl b/test/test_partitioneditensornetwork.jl deleted file mode 100644 index e4ab7d3c..00000000 --- a/test/test_partitioneditensornetwork.jl +++ /dev/null @@ -1,35 +0,0 @@ -using Dictionaries -using Distributions -using GraphsFlows -using ITensors -using ITensorNetworks -using NamedGraphs -using Random -using Test -using SplitApplyCombine - -using NamedGraphs: which_partition, parent_graph, partitioned_vertices - -using ITensorNetworks: message_tensors, update_message_tensor, belief_propagation_iteration, belief_propagation, - get_environment - -@testset "PartitionedITensorNetwork" begin - g_dims = (3, 3) - g = named_grid(g_dims) - s = siteinds("S=1/2", g) - χ = 2 - Random.seed!(1234) - ψ = randomITensorNetwork(s; link_space=χ) - ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - subgraph_vertices=collect(values(group(v -> v[1], vertices(ψψ)))) - - pψψ = PartitionedITensorNetwork(ψψ, subgraph_vertices) - - mts = message_tensors(pψψ) - mts = belief_propagation(pψψ, mts; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) - - env_tensors = get_environment(pψψ, mts, [((1,1),1)]) - - @show ITensors.contract(env_tensors) - -end \ No newline at end of file From d1f2c6e899102e198da09e0fc6da4478cc6a4231 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 18 Jan 2024 10:46:43 -0500 Subject: [PATCH 06/12] Formatting --- examples/apply/apply_bp/apply_bp.jl | 6 ++- examples/apply/apply_bp/apply_bp_run.jl | 6 +-- examples/belief_propagation/bpexample.jl | 38 +++++++++++------ examples/belief_propagation/bpsequences.jl | 15 +++---- examples/belief_propagation/sqrt_bp.jl | 6 ++- examples/boundary.jl | 7 ++-- .../dynamics/heavy_hex_ising_real_tebd.jl | 7 ++-- examples/gauging/gauging_itns.jl | 20 +++++++-- src/approx_itensornetwork/partition.jl | 21 +++++----- src/beliefpropagation/beliefpropagation.jl | 41 +++++++++++++------ .../sqrt_beliefpropagation.jl | 28 ++++++++----- src/gauging.jl | 31 ++++++++++---- src/itensornetwork.jl | 2 +- test/test_apply.jl | 4 +- test/test_belief_propagation.jl | 25 +++++++---- test/test_examples/test_apply_bp.jl | 6 +-- test/test_examples/test_examples.jl | 4 +- test/test_indsnetwork.jl | 1 + 18 files changed, 172 insertions(+), 96 deletions(-) diff --git a/examples/apply/apply_bp/apply_bp.jl b/examples/apply/apply_bp/apply_bp.jl index 2defcc28..c8ed449d 100644 --- a/examples/apply/apply_bp/apply_bp.jl +++ b/examples/apply/apply_bp/apply_bp.jl @@ -122,7 +122,11 @@ function simple_update_vidal(os, ψ::ITensorNetwork; maxdim, regauge=false) println("regauge") ψ_symmetric, pψψ_symmetric, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) mts = belief_propagation( - pψψ_symmetric, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 + pψψ_symmetric, + mts; + contract_kwargs=(; alg="exact"), + niters=50, + target_precision=1e-5, ) ψ, bond_tensors = vidal_gauge(ψ_symmetric, pψψ_symmetric, mts) end diff --git a/examples/apply/apply_bp/apply_bp_run.jl b/examples/apply/apply_bp/apply_bp_run.jl index d9e40c90..7c6ff8df 100644 --- a/examples/apply/apply_bp/apply_bp_run.jl +++ b/examples/apply/apply_bp/apply_bp_run.jl @@ -8,7 +8,7 @@ opname = "RandomUnitary" # graph = named_comb_tree graph = named_grid -dims = (6,6) +dims = (6, 6) ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal = main(; seed=1234, @@ -35,7 +35,7 @@ mts_bp = belief_propagation( contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-7, - verbose = true + verbose=true, ) mts_vidal = belief_propagation( pψψ_vidal, @@ -43,7 +43,7 @@ mts_vidal = belief_propagation( contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-7, - verbose = true + verbose=true, ) sz_bp = @show expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) diff --git a/examples/belief_propagation/bpexample.jl b/examples/belief_propagation/bpexample.jl index 2fdcd87e..62eba8fa 100644 --- a/examples/belief_propagation/bpexample.jl +++ b/examples/belief_propagation/bpexample.jl @@ -7,10 +7,7 @@ using SplitApplyCombine using NamedGraphs using ITensorNetworks: - belief_propagation, - approx_network_region, - contract_inner, - message_tensors + belief_propagation, approx_network_region, contract_inner, message_tensors function main() n = 4 @@ -31,9 +28,12 @@ function main() #Now do Simple Belief Propagation to Measure Sz on Site v pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation( + pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 + ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + ) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) sz_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -43,9 +43,12 @@ function main() #Now do Column-wise General Belief Propagation to Measure Sz on Site v pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation( + pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 + ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + ) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) sz_gen_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -64,14 +67,25 @@ function main() ψOψ = combine_linkinds(ψOψ, combiners) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e], contract_kwargs=(;alg="density_matrix",output_structure=path_graph_structure,maxdim = 8,contraction_sequence_alg="optimal")) - numerator_tensors = approx_network_region( - pψψ, mts, [v]; verts_tensors=[ψOψ[v]]) + mts = belief_propagation( + pψψ; + itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e], + contract_kwargs=(; + alg="density_matrix", + output_structure=path_graph_structure, + maxdim=8, + contraction_sequence_alg="optimal", + ), + ) + numerator_tensors = approx_network_region(pψψ, mts, [v]; verts_tensors=[ψOψ[v]]) denominator_tensors = approx_network_region(pψψ, mts, [v]) sz_MPS_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] println( - "Column-Wise MPS Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_gen_bp) + "Column-Wise MPS Belief Propagation Gives Sz on Site " * + string(v) * + " as " * + string(sz_gen_bp), ) #Now do it exactly diff --git a/examples/belief_propagation/bpsequences.jl b/examples/belief_propagation/bpsequences.jl index 1c84d80d..8f3b1438 100644 --- a/examples/belief_propagation/bpsequences.jl +++ b/examples/belief_propagation/bpsequences.jl @@ -8,11 +8,7 @@ using Graphs using NamedGraphs using ITensorNetworks: - belief_propagation, - approx_network_region, - contract_inner, - message_tensors, - edge_sequence + belief_propagation, approx_network_region, contract_inner, message_tensors, edge_sequence function main() g_labels = [ @@ -51,7 +47,9 @@ function main() contract_kwargs=(; alg="exact"), target_precision=1e-10, niters=100, - edges=[PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel")], + edges=[ + PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel") + ], verbose=true, ) print("Sequential updates (sequence is default edge list of the message tensors): ") @@ -61,7 +59,10 @@ function main() contract_kwargs=(; alg="exact"), target_precision=1e-10, niters=100, - edges=PartitionEdge.([e for e in vcat(edges(partitioned_graph(pψψ)), reverse.(edges(partitioned_graph(pψψ))))]), + edges=PartitionEdge.([ + e for + e in vcat(edges(partitioned_graph(pψψ)), reverse.(edges(partitioned_graph(pψψ)))) + ]), verbose=true, ) print("Sequential updates (sequence is our custom sequence finder): ") diff --git a/examples/belief_propagation/sqrt_bp.jl b/examples/belief_propagation/sqrt_bp.jl index a0014569..512e5981 100644 --- a/examples/belief_propagation/sqrt_bp.jl +++ b/examples/belief_propagation/sqrt_bp.jl @@ -36,7 +36,8 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) mts = @time belief_propagation(pψψ; niters, contract_kwargs=(; alg="exact")) numerator_network = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])] + ) denominator_network = approx_network_region(pψψ, mts, [(v, 1)]) sz_bp = ITensors.contract( @@ -52,7 +53,8 @@ function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) mts_sqrt = @time sqrt_belief_propagation(pψψ; niters) numerator_network = approx_network_region( - pψψ, mts_sqrt, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts_sqrt, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])] + ) denominator_network = approx_network_region(pψψ, mts_sqrt, [(v, 1)]) sz_sqrt_bp = contract( diff --git a/examples/boundary.jl b/examples/boundary.jl index 0d86e058..5d4f30c5 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -8,9 +8,10 @@ tn = ITensorNetwork(named_grid((6, 3)); link_space=4) @visualize tn - -ptn = PartitionedGraph(tn, NamedGraphs.partition_vertices(underlying_graph(tn); nvertices_per_partition = 2)) -sub_vs_1, sub_vs_2 =vertices(ptn, PartitionVertex(1)), vertices(ptn, PartitionVertex(2)) +ptn = PartitionedGraph( + tn, NamedGraphs.partition_vertices(underlying_graph(tn); nvertices_per_partition=2) +) +sub_vs_1, sub_vs_2 = vertices(ptn, PartitionVertex(1)), vertices(ptn, PartitionVertex(2)) @show (1, 1) ∈ sub_vs_1 @show (6, 3) ∈ sub_vs_2 diff --git a/examples/dynamics/heavy_hex_ising_real_tebd.jl b/examples/dynamics/heavy_hex_ising_real_tebd.jl index f4ad3eca..d17084a9 100644 --- a/examples/dynamics/heavy_hex_ising_real_tebd.jl +++ b/examples/dynamics/heavy_hex_ising_real_tebd.jl @@ -37,9 +37,7 @@ hummingbird_processor_graph() = ibm_processor_graph(2, 4) osprey_processor_graph() = ibm_processor_graph(6, 12) """Take the expectation value of o on an ITN using belief propagation""" -function expect_state_SBP( - o::ITensor, ψ::AbstractITensorNetwork, pψψ::PartitionedGraph, mts -) +function expect_state_SBP(o::ITensor, ψ::AbstractITensorNetwork, pψψ::PartitionedGraph, mts) Oψ = apply(o, ψ; cutoff=1e-16) ψ = copy(ψ) s = siteinds(ψ) @@ -47,7 +45,8 @@ function expect_state_SBP( vs_braket = [(v, 1) for v in vs] numerator_tensors = approx_network_region( - pψψ, mts, vs_braket; verts_tensors=ITensor[Oψ[v] for v in vs]) + pψψ, mts, vs_braket; verts_tensors=ITensor[Oψ[v] for v in vs] + ) denominator_tensors = approx_network_region(pψψ, mts, vs_braket) num_seq = contraction_sequence(numerator_tensors; alg="optimal") den_seq = contraction_sequence(denominator_tensors; alg="optimal") diff --git a/examples/gauging/gauging_itns.jl b/examples/gauging/gauging_itns.jl index 19e7e631..fc5ef175 100644 --- a/examples/gauging/gauging_itns.jl +++ b/examples/gauging/gauging_itns.jl @@ -22,7 +22,9 @@ using NamedGraphs: add_edges!, rem_vertex!, hexagonal_lattice_graph using Graphs """Eager Gauging""" -function eager_gauging(ψ::ITensorNetwork, pψψ::PartitionedGraph, bond_tensors::DataGraph, mts) +function eager_gauging( + ψ::ITensorNetwork, pψψ::PartitionedGraph, bond_tensors::DataGraph, mts +) isometries = vidal_itn_isometries(ψ, bond_tensors) ψ = copy(ψ) @@ -32,7 +34,9 @@ function eager_gauging(ψ::ITensorNetwork, pψψ::PartitionedGraph, bond_tensors pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) normalize!(isometries[e]) normalize!(isometries[reverse(e)]) - mts[pe], mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = ITensorNetwork(isometries[e]), + mts[pe], mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = ITensorNetwork( + isometries[e] + ), ITensorNetwork(isometries[reverse(e)]) end @@ -70,12 +74,20 @@ function benchmark_state_gauging( ) else times_iters[i] = @elapsed mts, _ = belief_propagation_iteration( - pψψ, mts; contract_kwargs=(; alg="exact"), edges=[PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel")]) + pψψ, + mts; + contract_kwargs=(; alg="exact"), + edges=[ + PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel") + ], + ) end times_gauging[i] = @elapsed ψ, bond_tensors = vidal_gauge(ψinit, pψψ, mts) elseif mode == "eager" - times_iters[i] = @elapsed ψ, bond_tensors, mts = eager_gauging(ψ, pψψ, bond_tensors, mts) + times_iters[i] = @elapsed ψ, bond_tensors, mts = eager_gauging( + ψ, pψψ, bond_tensors, mts + ) else times_iters[i] = @elapsed begin for e in edges(ψ) diff --git a/src/approx_itensornetwork/partition.jl b/src/approx_itensornetwork/partition.jl index 2ac4c0dc..d9d60aa6 100644 --- a/src/approx_itensornetwork/partition.jl +++ b/src/approx_itensornetwork/partition.jl @@ -75,19 +75,18 @@ function findfirst_on_edges(f::Function, graph::AbstractDataGraph) end function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) - return subgraphs(NamedGraph(g), subgraph_vertices) + return subgraphs(NamedGraph(g), subgraph_vertices) end - - """ - subgraphs(g::AbstractGraph, subgraph_vertices) - - Return a collection of subgraphs of `g` defined by the subgraph - vertices `subgraph_vertices`. - """ + +""" + subgraphs(g::AbstractGraph, subgraph_vertices) + +Return a collection of subgraphs of `g` defined by the subgraph +vertices `subgraph_vertices`. +""" function subgraphs(g::AbstractGraph, subgraph_vertices) - return map(vs -> subgraph(g, vs), subgraph_vertices) + return map(vs -> subgraph(g, vs), subgraph_vertices) end - """ subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) @@ -195,4 +194,4 @@ function _commoninds(partition::DataGraph) outinds = noncommoninds(network...) allinds = mapreduce(t -> [i for i in inds(t)], vcat, network) return Vector(setdiff(allinds, outinds)) -end \ No newline at end of file +end diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 6c3c4e0d..5a30ef2d 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -1,6 +1,5 @@ function message_tensors( - ptn::PartitionedGraph; - itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] + ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] ) mts = Dict() for e in PartitionEdge.(edges(partitioned_graph(ptn))) @@ -23,19 +22,27 @@ function update_message_tensor( contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), ) incoming_messages = [ - mts[PartitionEdge(e_in)] for e_in in setdiff(boundary_edges(partitioned_graph(ptn), [NamedGraphs.parent(src(edge))]; dir=:in), [reverse(NamedGraphs.parent(edge))]) + mts[PartitionEdge(e_in)] for e_in in setdiff( + boundary_edges(partitioned_graph(ptn), [NamedGraphs.parent(src(edge))]; dir=:in), + [reverse(NamedGraphs.parent(edge))], + ) ] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) - contract_list = ITensor[incoming_messages; ITensor(unpartitioned_graph(subgraph(ptn, [src(edge)])))] + contract_list = ITensor[ + incoming_messages + ITensor(unpartitioned_graph(subgraph(ptn, [src(edge)]))) + ] if contract_kwargs.alg != "exact" mt, _ = contract(ITensorNetwork(contract_list); contract_kwargs...) else - mt = contract(contract_list; sequence=contraction_sequence(contract_list; alg="optimal")) + mt = contract( + contract_list; sequence=contraction_sequence(contract_list; alg="optimal") + ) end - if isa(mt, ITensor) + if isa(mt, ITensor) mt = ITensor[mt] elseif isa(mt, ITensorNetwork) mt = ITensor(mt) @@ -61,8 +68,7 @@ function belief_propagation_iteration( new_mts[e] = update_message_tensor(ptn, e, new_mts; contract_kwargs) if compute_norm - LHS, RHS = ITensors.contract(mts[e]), - ITensors.contract(new_mts[e]) + LHS, RHS = ITensors.contract(mts[e]), ITensors.contract(new_mts[e]) #This line only makes sense if the message tensors are rank 2??? Should fix this. LHS /= sum(diag(LHS)) RHS /= sum(diag(RHS)) @@ -133,7 +139,11 @@ function belief_propagation( return mts end -function belief_propagation(ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))], kwargs...) +function belief_propagation( + ptn::PartitionedGraph; + itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))], + kwargs..., +) mts = message_tensors(ptn; itensor_constructor) return belief_propagation(ptn, mts; kwargs...) end @@ -148,10 +158,15 @@ function get_environment(ptn::PartitionedGraph, mts, verts::Vector; dir=:in) return get_environment(ptn, mts, setdiff(vertices(ptn), verts)) end - env_tensors = [mts[PartitionEdge(e)] for e in boundary_edges(partitioned_graph(ptn), NamedGraphs.parent.(partition_vertices); dir=:in)] - env_tensors = reduce(vcat, env_tensors; init = ITensor[]) - central_tensors = ITensor[(unpartitioned_graph(ptn))[v] for v in setdiff(vertices(ptn, partition_vertices), verts)] - + env_tensors = [ + mts[PartitionEdge(e)] for e in + boundary_edges(partitioned_graph(ptn), NamedGraphs.parent.(partition_vertices); dir=:in) + ] + env_tensors = reduce(vcat, env_tensors; init=ITensor[]) + central_tensors = ITensor[ + (unpartitioned_graph(ptn))[v] for v in setdiff(vertices(ptn, partition_vertices), verts) + ] + return vcat(env_tensors, central_tensors) end diff --git a/src/beliefpropagation/sqrt_beliefpropagation.jl b/src/beliefpropagation/sqrt_beliefpropagation.jl index 1e20698a..9fe9253b 100644 --- a/src/beliefpropagation/sqrt_beliefpropagation.jl +++ b/src/beliefpropagation/sqrt_beliefpropagation.jl @@ -72,20 +72,24 @@ function sqrt_belief_propagation(ptn::PartitionedGraph; kwargs...) return sqrt_belief_propagation(ptn, mts; kwargs...) end -function update_sqrt_message_tensor( - pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts; -) +function update_sqrt_message_tensor(pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts;) v = only(filter(v -> v[2] == 1, vertices(pψψ, src(edge)))) site_itensor = unpartitioned_graph(pψψ)[v] incoming_messages = [ - sqrt_mts[PartitionEdge(e_in)] for e_in in setdiff(boundary_edges(partitioned_graph(pψψ), [NamedGraphs.parent(src(edge))]; dir=:in), [reverse(NamedGraphs.parent(edge))]) + sqrt_mts[PartitionEdge(e_in)] for e_in in setdiff( + boundary_edges(partitioned_graph(pψψ), [NamedGraphs.parent(src(edge))]; dir=:in), + [reverse(NamedGraphs.parent(edge))], + ) ] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) contract_list = ITensor[incoming_messages; site_itensor] contract_output = contract( contract_list; sequence=contraction_sequence(contract_list; alg="optimal") ) - left_inds = [uniqueinds(contract_output, site_itensor); siteinds(unpartitioned_graph(pψψ), v)] + left_inds = [ + uniqueinds(contract_output, site_itensor) + siteinds(unpartitioned_graph(pψψ), v) + ] Q, R = qr(contract_output, left_inds) normalize!(R) return R @@ -99,17 +103,19 @@ function sqrt_message_tensors( ) sqrt_mts = copy(mts) for e in PartitionEdge.(edges(partitioned_graph(pψψ))) - vsrc, vdst = filter(v -> v[2] == 1, vertices(pψψ, src(e))), filter(v -> v[2] == 1, vertices(pψψ, dst(e))) - ψvsrc, ψvdst = unpartitioned_graph(pψψ)[only(vsrc)],unpartitioned_graph(pψψ)[only(vdst)] + vsrc, vdst = filter(v -> v[2] == 1, vertices(pψψ, src(e))), + filter(v -> v[2] == 1, vertices(pψψ, dst(e))) + ψvsrc, ψvdst = unpartitioned_graph(pψψ)[only(vsrc)], + unpartitioned_graph(pψψ)[only(vdst)] edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) - X_D, X_U = eigen( - only(mts[e]); ishermitian=true, cutoff=eigen_message_tensor_cutoff - ) + X_D, X_U = eigen(only(mts[e]); ishermitian=true, cutoff=eigen_message_tensor_cutoff) Y_D, Y_U = eigen( - only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]); + ishermitian=true, + cutoff=eigen_message_tensor_cutoff, ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) diff --git a/src/gauging.jl b/src/gauging.jl index 7965e30e..ae1e3c58 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -31,11 +31,11 @@ function vidal_gauge( edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) - X_D, X_U = eigen( - only(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff - ) + X_D, X_U = eigen(only(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff) Y_D, Y_U = eigen( - only(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); ishermitian=true, cutoff=eigen_message_tensor_cutoff + only(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); + ishermitian=true, + cutoff=eigen_message_tensor_cutoff, ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -87,7 +87,14 @@ function vidal_gauge( ) bond_tensors = initialize_bond_tensors(ψ) return vidal_gauge( - ψ, pψψ, mts, bond_tensors; eigen_message_tensor_cutoff, regularization, edges, svd_kwargs... + ψ, + pψψ, + mts, + bond_tensors; + eigen_message_tensor_cutoff, + regularization, + edges, + svd_kwargs..., ) end @@ -113,7 +120,9 @@ function vidal_gauge( target_precision=target_canonicalness, verbose, ) - return vidal_gauge(ψ, pψψ, mts; eigen_message_tensor_cutoff, regularization, svd_kwargs...) + return vidal_gauge( + ψ, pψψ, mts; eigen_message_tensor_cutoff, regularization, svd_kwargs... + ) end """Transform from an ITensor in the Vidal Gauge (bond tensors) to the Symmetric Gauge (partitionedgraph, message tensors)""" @@ -130,7 +139,10 @@ function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vsrc]), vsrc) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vdst]), vdst) - ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy(ITensor[dense(bond_tensors[e])]), copy(ITensor[dense(bond_tensors[e])]) + ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy( + ITensor[dense(bond_tensors[e])] + ), + copy(ITensor[dense(bond_tensors[e])]) end ψψsymm = norm_network(ψsymm) @@ -162,7 +174,10 @@ end """Transform from the Symmetric Gauge (message tensors) to the Vidal Gauge (bond tensors)""" function symmetric_to_vidal_gauge( - ψ::ITensorNetwork, pψψ::PartitionedGraph, mts; regularization=10 * eps(real(scalartype(ψ))) + ψ::ITensorNetwork, + pψψ::PartitionedGraph, + mts; + regularization=10 * eps(real(scalartype(ψ))), ) bond_tensors = DataGraph{vertextype(ψ),ITensor,ITensor}(underlying_graph(ψ)) diff --git a/src/itensornetwork.jl b/src/itensornetwork.jl index 85ae4b41..ffc63836 100644 --- a/src/itensornetwork.jl +++ b/src/itensornetwork.jl @@ -273,4 +273,4 @@ function ITensors.ITensor(ψ::ITensorNetwork) end end -NamedGraphs.parent_graph(ψ::ITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(ψ)) \ No newline at end of file +NamedGraphs.parent_graph(ψ::ITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(ψ)) diff --git a/test/test_apply.jl b/test/test_apply.jl index bc0067ad..4cdc6d8b 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -29,14 +29,14 @@ using SplitApplyCombine ψψ = norm_network(ψ) #Simple Belief Propagation Grouping - pψψ_SBP =PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ_SBP = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) mtsSBP = belief_propagation(pψψ_SBP; contract_kwargs=(; alg="exact"), niters=50) envsSBP = get_environment(pψψ_SBP, mtsSBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) ψ_vidal, bond_tensors = vidal_gauge(ψ, pψψ_SBP, mtsSBP) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) - pψψ_GBP =PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) + pψψ_GBP = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) mtsGBP = belief_propagation(pψψ_GBP; contract_kwargs=(; alg="exact"), niters=50) envsGBP = get_environment(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index 6cc2f4a7..8109c990 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -38,9 +38,12 @@ ITensors.disable_warn_order() exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation( + pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 + ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + ) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -62,9 +65,12 @@ ITensors.disable_warn_order() exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), verbose = true, niters = 10, target_precision = 1e-3) + mts = belief_propagation( + pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 + ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])]) + pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + ) denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @@ -85,11 +91,14 @@ ITensors.disable_warn_order() ITensors.contract(ψψ; sequence=contract_seq)[] nsites = 2 - p_vertices = NamedGraphs.partition_vertices(underlying_graph(ψψ); nvertices_per_partition = nsites) + p_vertices = NamedGraphs.partition_vertices( + underlying_graph(ψψ); nvertices_per_partition=nsites + ) pψψ = PartitionedGraph(ψψ, p_vertices) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters = 20) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters=20) numerator_network = approx_network_region( - pψψ, mts, vs; verts_tensors=ITensor[ψOψ[v] for v in vs]) + pψψ, mts, vs; verts_tensors=ITensor[ψOψ[v] for v in vs] + ) denominator_network = approx_network_region(pψψ, mts, vs) bp_szsz = @@ -107,7 +116,7 @@ ITensors.disable_warn_order() ψψ = ψ ⊗ prime(dag(ψ); sites=[]) pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters = 20) + mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters=20) ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) rdm = ITensors.contract( diff --git a/test/test_examples/test_apply_bp.jl b/test/test_examples/test_apply_bp.jl index 6558fbe5..5e9cabb8 100644 --- a/test/test_examples/test_apply_bp.jl +++ b/test/test_examples/test_apply_bp.jl @@ -9,7 +9,7 @@ include(joinpath(pkgdir(ITensorNetworks), "examples", "apply", "apply_bp", "appl graphs = (named_comb_tree, named_grid) dims = (6, 6) @testset "$opname, $graph" for opname in opnames, graph in graphs - ψ_bp, mts_bp, ψ_vidal, mts_vidal = @suppress main(; + ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal = @suppress main(; seed=1234, opname, graph, @@ -21,8 +21,8 @@ include(joinpath(pkgdir(ITensorNetworks), "examples", "apply", "apply_bp", "appl reduced=true, ) v = dims .÷ 2 - sz_bp = expect_bp("Sz", v, ψ_bp, mts_bp) - sz_vidal = expect_bp("Sz", v, ψ_vidal, mts_vidal) + sz_bp = expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) + sz_vidal = expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) @test sz_bp ≈ sz_vidal rtol = 1e-5 end end diff --git a/test/test_examples/test_examples.jl b/test/test_examples/test_examples.jl index e348b2c6..124bffc6 100644 --- a/test/test_examples/test_examples.jl +++ b/test/test_examples/test_examples.jl @@ -25,9 +25,7 @@ using Test @suppress include(joinpath(pkgdir(ITensorNetworks), "examples", example_file)) end if !Sys.iswindows() - example_files = [ - joinpath("contraction_sequence", "contraction_sequence.jl"), - ] + example_files = [joinpath("contraction_sequence", "contraction_sequence.jl")] @testset "Test $example_file (using KaHyPar, so no Windows support)" for example_file in example_files @suppress include(joinpath(pkgdir(ITensorNetworks), "examples", example_file)) diff --git a/test/test_indsnetwork.jl b/test/test_indsnetwork.jl index 1a9bc27f..29a5d858 100644 --- a/test/test_indsnetwork.jl +++ b/test/test_indsnetwork.jl @@ -1,6 +1,7 @@ using Dictionaries using ITensors using ITensorNetworks +using ITensorNetworks: dim using Random using Test From b25a3da9b246ed18f418062bf164c094e7e7ad61 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 22 Jan 2024 15:20:27 -0500 Subject: [PATCH 07/12] Updated to work with latest builds of NamedGraphs and DataGraphs --- examples/apply/apply_bp/apply_bp.jl | 6 +- examples/boundary.jl | 2 +- examples/distances.jl | 2 +- examples/gauging/gauging_itns.jl | 6 +- src/ITensorNetworks.jl | 3 +- .../approx_itensornetwork.jl | 2 +- .../binary_tree_partition.jl | 8 +- src/approx_itensornetwork/partition.jl | 190 +++++------------- src/beliefpropagation/beliefpropagation.jl | 34 ++-- .../sqrt_beliefpropagation.jl | 20 +- src/gauging.jl | 14 +- src/itensornetwork.jl | 2 - test/test_belief_propagation.jl | 4 +- test/test_binary_tree_partition.jl | 9 +- test/test_contract_deltas.jl | 2 +- test/test_examples/test_sqrt_bp.jl | 2 +- 16 files changed, 94 insertions(+), 212 deletions(-) diff --git a/examples/apply/apply_bp/apply_bp.jl b/examples/apply/apply_bp/apply_bp.jl index c8ed449d..4fd6159c 100644 --- a/examples/apply/apply_bp/apply_bp.jl +++ b/examples/apply/apply_bp/apply_bp.jl @@ -61,13 +61,13 @@ function simple_update_bp( v⃗ = neighbor_vertices(ψ, o) for e in edges @assert order(only(mts[e])) == 2 - @assert order(only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))])) == 2 + @assert order(only(mts[reverse(e)])) == 2 end @assert length(v⃗) == 2 v1, v2 = v⃗ - pe = NamedGraphs.partition_edge(pψψ, NamedEdge((v1, 1) => (v2, 1))) + pe = partitionedge(pψψ, NamedEdge((v1, 1) => (v2, 1))) envs = get_environment(pψψ, mts, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) obs = observer() # TODO: Make a version of `apply` that accepts message tensors, @@ -91,7 +91,7 @@ function simple_update_bp( ψψ = norm_network(ψ) pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts[pe] = dense.(obs.singular_values) - mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = dense.(obs.singular_values) + mts[reverse(pe)] = dense.(obs.singular_values) end if regauge println("regauge") diff --git a/examples/boundary.jl b/examples/boundary.jl index 5d4f30c5..71071119 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -9,7 +9,7 @@ tn = ITensorNetwork(named_grid((6, 3)); link_space=4) @visualize tn ptn = PartitionedGraph( - tn, NamedGraphs.partition_vertices(underlying_graph(tn); nvertices_per_partition=2) + tn, partitioned_vertices(underlying_graph(tn); nvertices_per_partition=2) ) sub_vs_1, sub_vs_2 = vertices(ptn, PartitionVertex(1)), vertices(ptn, PartitionVertex(2)) diff --git a/examples/distances.jl b/examples/distances.jl index dcd71ab1..8242889c 100644 --- a/examples/distances.jl +++ b/examples/distances.jl @@ -14,4 +14,4 @@ t = dijkstra_tree(ψ, only(center(ψ))) @show a_star(ψ, (2, 1), (2, 5)) @show mincut_partitions(ψ) @show mincut_partitions(ψ, (1, 1), (3, 5)) -@show NamedGraphs.partition_vertices(underlying_graph(ψ); npartitions=2) +@show partitioned_vertices(underlying_graph(ψ); npartitions=2) diff --git a/examples/gauging/gauging_itns.jl b/examples/gauging/gauging_itns.jl index fc5ef175..6c988753 100644 --- a/examples/gauging/gauging_itns.jl +++ b/examples/gauging/gauging_itns.jl @@ -31,12 +31,10 @@ function eager_gauging( mts = copy(mts) for e in edges(ψ) vsrc, vdst = src(e), dst(e) - pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + pe = which_partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) normalize!(isometries[e]) normalize!(isometries[reverse(e)]) - mts[pe], mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = ITensorNetwork( - isometries[e] - ), + mts[pe], mts[reverse(pe)] = ITensorNetwork(isometries[e]), ITensorNetwork(isometries[reverse(e)]) end diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index f1c8acd1..eb10e781 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -53,7 +53,8 @@ using NamedGraphs: parent_graph, vertex_to_parent_vertex, parent_vertices_to_vertices, - not_implemented + not_implemented, + parent include("imports.jl") diff --git a/src/approx_itensornetwork/approx_itensornetwork.jl b/src/approx_itensornetwork/approx_itensornetwork.jl index 5041a8a5..c1cbfb20 100644 --- a/src/approx_itensornetwork/approx_itensornetwork.jl +++ b/src/approx_itensornetwork/approx_itensornetwork.jl @@ -69,7 +69,7 @@ function approx_itensornetwork( contraction_sequence_alg="optimal", contraction_sequence_kwargs=(;), ) - par = partition(tn, inds_btree; alg="mincut_recursive_bisection") + par = _partition(tn, inds_btree; alg="mincut_recursive_bisection") output_tn, log_root_norm = approx_itensornetwork( alg, par; diff --git a/src/approx_itensornetwork/binary_tree_partition.jl b/src/approx_itensornetwork/binary_tree_partition.jl index cff0c31e..4a7b5846 100644 --- a/src/approx_itensornetwork/binary_tree_partition.jl +++ b/src/approx_itensornetwork/binary_tree_partition.jl @@ -59,7 +59,7 @@ Note: for a given binary tree with n indices, the output partition will contain Note: name of vertices in the output partition are the same as the name of vertices in `inds_btree`. """ -function partition( +function _partition( ::Algorithm"mincut_recursive_bisection", tn::ITensorNetwork, inds_btree::DataGraph ) @assert _is_rooted_directed_binary_tree(inds_btree) @@ -115,7 +115,7 @@ function partition( end tn_deltas = ITensorNetwork(vcat(output_deltas_vector...)) tn_scalars = ITensorNetwork(vcat(scalars_vector...)) - par = partition(disjoint_union(out_tn, tn_deltas, tn_scalars), subgraph_vs) + par = _partition(disjoint_union(out_tn, tn_deltas, tn_scalars), subgraph_vs) @assert is_tree(par) name_map = Dict() for (i, v) in enumerate(pre_order_dfs_vertices(inds_btree, root)) @@ -124,6 +124,6 @@ function partition( return rename_vertices(par, name_map) end -function partition(tn::ITensorNetwork, inds_btree::DataGraph; alg::String) - return partition(Algorithm(alg), tn, inds_btree) +function _partition(tn::ITensorNetwork, inds_btree::DataGraph; alg::String) + return _partition(Algorithm(alg), tn, inds_btree) end diff --git a/src/approx_itensornetwork/partition.jl b/src/approx_itensornetwork/partition.jl index d9d60aa6..9f89d063 100644 --- a/src/approx_itensornetwork/partition.jl +++ b/src/approx_itensornetwork/partition.jl @@ -1,49 +1,26 @@ -""" - partition_vertices(g::AbstractGraph, subgraph_vertices::Vector) - -Given a graph (`g`) and groups of vertices defining subgraphs of that -graph (`subgraph_vertices`), return a DataGraph storing the subgraph -vertices on the vertices of the graph and with edges denoting -which subgraphs of the original graph have edges connecting them, along with -edge data storing the original edges that were connecting the subgraphs. -""" -function partition_vertices(g::AbstractGraph, subgraph_vertices) - partitioned_vertices = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), Dictionary(subgraph_vertices) +function _partition(g::AbstractGraph, subgraph_vertices) + partitioned_graph = DataGraph( + NamedGraph(eachindex(subgraph_vertices)), + map(vs -> subgraph(g, vs), Dictionary(subgraph_vertices)), ) for e in edges(g) - s1 = findfirst_on_vertices( - subgraph_vertices -> src(e) ∈ subgraph_vertices, partitioned_vertices - ) - s2 = findfirst_on_vertices( - subgraph_vertices -> dst(e) ∈ subgraph_vertices, partitioned_vertices - ) - if (!has_edge(partitioned_vertices, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_vertices, s1, s2) - partitioned_vertices[s1 => s2] = Vector{edgetype(g)}() + s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) + s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) + if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) + add_edge!(partitioned_graph, s1, s2) + partitioned_graph[s1 => s2] = Dictionary( + [:edges, :edge_data], + [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], + ) end - if has_edge(partitioned_vertices, s1, s2) - push!(partitioned_vertices[s1 => s2], e) + if has_edge(partitioned_graph, s1, s2) + push!(partitioned_graph[s1 => s2][:edges], e) + if isassigned(g, e) + set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) + end end end - return partitioned_vertices -end - -""" - partition_vertices(g::AbstractGraph; npartitions, nvertices_per_partition, kwargs...) - -Given a graph `g`, partition the vertices of `g` into 'npartitions' partitions -or into partitions with `nvertices_per_partition` vertices per partition. -Try to keep all subgraphs the same size and minimise edges cut between them -Returns a datagraph where each vertex contains the list of vertices involved in that subgraph. The edges state which subgraphs are connected. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function partition_vertices( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return partition_vertices( - g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...) - ) + return partitioned_graph end """ @@ -74,109 +51,34 @@ function findfirst_on_edges(f::Function, graph::AbstractDataGraph) return findfirst(f, edge_data(graph)) end -function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) - return subgraphs(NamedGraph(g), subgraph_vertices) -end - -""" - subgraphs(g::AbstractGraph, subgraph_vertices) - -Return a collection of subgraphs of `g` defined by the subgraph -vertices `subgraph_vertices`. -""" -function subgraphs(g::AbstractGraph, subgraph_vertices) - return map(vs -> subgraph(g, vs), subgraph_vertices) -end - -""" - subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition, -returning a list of subgraphs. -Try to keep all subgraphs the same size and minimise edges cut between them. -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. -""" -function subgraphs( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) -end - -function partition(g::AbstractSimpleGraph, subgraph_vertices) - return partition(NamedGraph(g), subgraph_vertices) -end - -function partition(g::AbstractGraph, subgraph_vertices) - partitioned_graph = DataGraph( - NamedGraph(eachindex(subgraph_vertices)), subgraphs(g, Dictionary(subgraph_vertices)) - ) - for e in edges(g) - s1 = findfirst_on_vertices(subgraph -> src(e) ∈ vertices(subgraph), partitioned_graph) - s2 = findfirst_on_vertices(subgraph -> dst(e) ∈ vertices(subgraph), partitioned_graph) - if (!has_edge(partitioned_graph, s1, s2) && s1 ≠ s2) - add_edge!(partitioned_graph, s1, s2) - partitioned_graph[s1 => s2] = Dictionary( - [:edges, :edge_data], - [Vector{edgetype(g)}(), Dictionary{edgetype(g),edge_data_type(g)}()], - ) - end - if has_edge(partitioned_graph, s1, s2) - push!(partitioned_graph[s1 => s2][:edges], e) - if isassigned(g, e) - set!(partitioned_graph[s1 => s2][:edge_data], e, g[e]) - end - end - end - return partitioned_graph -end - -""" - partition(g::AbstractGraph; npartitions::Integer, kwargs...) - partition(g::AbstractGraph, subgraph_vertices) - -Given a graph `g`, partition `g` into `npartitions` partitions -or into partitions with `nvertices_per_partition` vertices per partition. -The partitioning tries to keep all subgraphs the same size and minimize -edges cut between them. - -Alternatively, specify a desired partitioning with a collection of sugraph -vertices. - -Returns a data graph where each vertex contains the corresponding subgraph as vertex data. -The edges indicates which subgraphs are connected, and the edge data stores a dictionary -with two fields. The field `:edges` stores a list of the edges of the original graph -that were connecting the two subgraphs, and `:edge_data` stores a dictionary -mapping edges of the original graph to the data living on the edges of the original -graph, if it existed. - -Therefore, one should be able to extract that data and recreate the original -graph from the results of `partition`. - -A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work -if the subgraph vertices aren't specified explicitly. -""" -function partition( - g::AbstractGraph; - npartitions=nothing, - nvertices_per_partition=nothing, - subgraph_vertices=nothing, - kwargs..., -) - if count(isnothing, (npartitions, nvertices_per_partition, subgraph_vertices)) != 2 - error( - "Error: Cannot give multiple/ no partitioning options. Please specify exactly one." - ) - end - - if isnothing(subgraph_vertices) - subgraph_vertices = NamedGraphs.partition_vertices( - g; npartitions, nvertices_per_partition, kwargs... - ) - end - - return partition(g, subgraph_vertices) -end +# function subgraphs(g::AbstractSimpleGraph, subgraph_vertices) +# return subgraphs(NamedGraph(g), subgraph_vertices) +# end + +# """ +# subgraphs(g::AbstractGraph, subgraph_vertices) + +# Return a collection of subgraphs of `g` defined by the subgraph +# vertices `subgraph_vertices`. +# """ +# function subgraphs(g::AbstractGraph, subgraph_vertices) +# return map(vs -> subgraph(g, vs), subgraph_vertices) +# end + +# """ +# subgraphs(g::AbstractGraph; npartitions::Integer, kwargs...) + +# Given a graph `g`, partition `g` into `npartitions` partitions +# or into partitions with `nvertices_per_partition` vertices per partition, +# returning a list of subgraphs. +# Try to keep all subgraphs the same size and minimise edges cut between them. +# A graph partitioning backend such as Metis or KaHyPar needs to be installed for this function to work. +# """ +# function subgraphs( +# g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +# ) +# return subgraphs(g, subgraph_vertices(g; npartitions, nvertices_per_partition, kwargs...)) +# end """ TODO: do we want to make it a public function? diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 5a30ef2d..5a0b7b79 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -2,12 +2,12 @@ function message_tensors( ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] ) mts = Dict() - for e in PartitionEdge.(edges(partitioned_graph(ptn))) + for e in partitionedges(ptn) src_e_itn = unpartitioned_graph(subgraph(ptn, [src(e)])) dst_e_itn = unpartitioned_graph(subgraph(ptn, [dst(e)])) inds_e = commoninds(src_e_itn, dst_e_itn) mts[e] = itensor_constructor(inds_e) - mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = dag.(mts[e]) + mts[reverse(e)] = dag.(mts[e]) end return mts end @@ -21,12 +21,11 @@ function update_message_tensor( mts; contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), ) - incoming_messages = [ - mts[PartitionEdge(e_in)] for e_in in setdiff( - boundary_edges(partitioned_graph(ptn), [NamedGraphs.parent(src(edge))]; dir=:in), - [reverse(NamedGraphs.parent(edge))], - ) - ] + pedges = setdiff( + partitionedges(ptn, boundary_edges(ptn, vertices(ptn, src(edge)); dir=:in)), + [reverse(edge)], + ) + incoming_messages = [mts[e_in] for e_in in pedges] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) contract_list = ITensor[ @@ -35,18 +34,14 @@ function update_message_tensor( ] if contract_kwargs.alg != "exact" - mt, _ = contract(ITensorNetwork(contract_list); contract_kwargs...) + mt = first(contract(ITensorNetwork(contract_list); contract_kwargs...)) else mt = contract( contract_list; sequence=contraction_sequence(contract_list; alg="optimal") ) end - if isa(mt, ITensor) - mt = ITensor[mt] - elseif isa(mt, ITensorNetwork) - mt = ITensor(mt) - end + mt = isa(mt, ITensor) ? ITensor[mt] : ITensor(mt) normalize!.(mt) return mt @@ -152,19 +147,18 @@ Given a subet of vertices of a given Tensor Network and the Message Tensors for Specifically, the contraction of the environment tensors and tn[vertices] will be a scalar. """ function get_environment(ptn::PartitionedGraph, mts, verts::Vector; dir=:in) - partition_vertices = unique([NamedGraphs.which_partition(ptn, v) for v in verts]) + partition_verts = partitionvertices(ptn, verts) + central_verts = vertices(ptn, partition_verts) if dir == :out return get_environment(ptn, mts, setdiff(vertices(ptn), verts)) end - env_tensors = [ - mts[PartitionEdge(e)] for e in - boundary_edges(partitioned_graph(ptn), NamedGraphs.parent.(partition_vertices); dir=:in) - ] + pedges = partitionedges(ptn, boundary_edges(ptn, central_verts; dir=:in)) + env_tensors = [mts[e] for e in pedges] env_tensors = reduce(vcat, env_tensors; init=ITensor[]) central_tensors = ITensor[ - (unpartitioned_graph(ptn))[v] for v in setdiff(vertices(ptn, partition_vertices), verts) + (unpartitioned_graph(ptn))[v] for v in setdiff(central_verts, verts) ] return vcat(env_tensors, central_tensors) diff --git a/src/beliefpropagation/sqrt_beliefpropagation.jl b/src/beliefpropagation/sqrt_beliefpropagation.jl index 9fe9253b..dc13713e 100644 --- a/src/beliefpropagation/sqrt_beliefpropagation.jl +++ b/src/beliefpropagation/sqrt_beliefpropagation.jl @@ -75,12 +75,8 @@ end function update_sqrt_message_tensor(pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts;) v = only(filter(v -> v[2] == 1, vertices(pψψ, src(edge)))) site_itensor = unpartitioned_graph(pψψ)[v] - incoming_messages = [ - sqrt_mts[PartitionEdge(e_in)] for e_in in setdiff( - boundary_edges(partitioned_graph(pψψ), [NamedGraphs.parent(src(edge))]; dir=:in), - [reverse(NamedGraphs.parent(edge))], - ) - ] + p_edges = setdiff(partitionedges(pψψ, boundary_edges(pψψ, [v]; dir=:in)), [reverse(edge)]) + incoming_messages = [sqrt_mts[e_in] for e_in in p_edges] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) contract_list = ITensor[incoming_messages; site_itensor] contract_output = contract( @@ -102,7 +98,7 @@ function sqrt_message_tensors( regularization=10 * eps(real(scalartype(unpartitioned_graph(pψψ)))), ) sqrt_mts = copy(mts) - for e in PartitionEdge.(edges(partitioned_graph(pψψ))) + for e in partitionedges(pψψ) vsrc, vdst = filter(v -> v[2] == 1, vertices(pψψ, src(e))), filter(v -> v[2] == 1, vertices(pψψ, dst(e))) ψvsrc, ψvdst = unpartitioned_graph(pψψ)[only(vsrc)], @@ -113,9 +109,7 @@ function sqrt_message_tensors( X_D, X_U = eigen(only(mts[e]); ishermitian=true, cutoff=eigen_message_tensor_cutoff) Y_D, Y_U = eigen( - only(mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]); - ishermitian=true, - cutoff=eigen_message_tensor_cutoff, + only(mts[reverse(e)]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -125,7 +119,7 @@ function sqrt_message_tensors( rootY = Y_U * rootY_D * prime(dag(Y_U)) sqrt_mts[e] = ITensor[rootX] - sqrt_mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = ITensor[rootY] + sqrt_mts[reverse(e)] = ITensor[rootY] end return sqrt_mts end @@ -134,14 +128,14 @@ function sqr_message_tensors(sqrt_mts) mts = copy(sqrt_mts) for e in keys(sqrt_mts) sqrt_mt = only(sqrt_mts[e]) - sqrt_mt_rev = only(sqrt_mts[PartitionEdge(reverse(NamedGraphs.parent(e)))]) + sqrt_mt_rev = only(sqrt_mts[reverse(e)]) l = commoninds(sqrt_mt, sqrt_mt_rev) mt = dag(prime(sqrt_mt, l)) * sqrt_mt normalize!(mt) mt_rev = dag(prime(sqrt_mt_rev, l)) * sqrt_mt_rev normalize!(mt_rev) mts[e] = ITensor[mt] - mts[PartitionEdge(reverse(NamedGraphs.parent(e)))] = ITensor[mt_rev] + mts[reverse(e)] = ITensor[mt_rev] end return mts end diff --git a/src/gauging.jl b/src/gauging.jl index ae1e3c58..25fce5d0 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -27,15 +27,13 @@ function vidal_gauge( vsrc, vdst = src(e), dst(e) ψvsrc, ψvdst = ψ_vidal[vsrc], ψ_vidal[vdst] - pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + pe = partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) X_D, X_U = eigen(only(mts[pe]); ishermitian=true, cutoff=eigen_message_tensor_cutoff) Y_D, Y_U = eigen( - only(mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))]); - ishermitian=true, - cutoff=eigen_message_tensor_cutoff, + only(mts[reverse(pe)]); ishermitian=true, cutoff=eigen_message_tensor_cutoff ) X_D, Y_D = map_diag(x -> x + regularization, X_D), map_diag(x -> x + regularization, Y_D) @@ -134,14 +132,12 @@ function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) for e in edges(ψsymm) vsrc, vdst = src(e), dst(e) - pe = NamedGraphs.partition_edge(pψψsymm, NamedEdge((vsrc, 1) => (vdst, 1))) + pe = partitionedge(pψψsymm, NamedEdge((vsrc, 1) => (vdst, 1))) root_S = sqrt_diag(bond_tensors[e]) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vsrc]), vsrc) setindex_preserve_graph!(ψsymm, noprime(root_S * ψsymm[vdst]), vdst) - ψsymm_mts[pe], ψsymm_mts[PartitionEdge(reverse(NamedGraphs.parent(pe)))] = copy( - ITensor[dense(bond_tensors[e])] - ), + ψsymm_mts[pe], ψsymm_mts[reverse(pe)] = copy(ITensor[dense(bond_tensors[e])]), copy(ITensor[dense(bond_tensors[e])]) end @@ -185,7 +181,7 @@ function symmetric_to_vidal_gauge( for e in edges(ψ) vsrc, vdst = src(e), dst(e) - pe = NamedGraphs.partition_edge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + pe = partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) bond_tensors[e] = only(mts[pe]) invroot_S = invsqrt_diag(map_diag(x -> x + regularization, bond_tensors[e])) setindex_preserve_graph!(ψ_vidal, noprime(invroot_S * ψ_vidal[vsrc]), vsrc) diff --git a/src/itensornetwork.jl b/src/itensornetwork.jl index ffc63836..0b8178c3 100644 --- a/src/itensornetwork.jl +++ b/src/itensornetwork.jl @@ -272,5 +272,3 @@ function ITensors.ITensor(ψ::ITensorNetwork) return ITensor[ψ[v] for v in vertices(ψ)] end end - -NamedGraphs.parent_graph(ψ::ITensorNetwork) = NamedGraphs.parent_graph(underlying_graph(ψ)) diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index 8109c990..f2e25f4b 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -91,9 +91,7 @@ ITensors.disable_warn_order() ITensors.contract(ψψ; sequence=contract_seq)[] nsites = 2 - p_vertices = NamedGraphs.partition_vertices( - underlying_graph(ψψ); nvertices_per_partition=nsites - ) + p_vertices = partitioned_vertices(underlying_graph(ψψ); nvertices_per_partition=nsites) pψψ = PartitionedGraph(ψψ, p_vertices) mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters=20) numerator_network = approx_network_region( diff --git a/test/test_binary_tree_partition.jl b/test/test_binary_tree_partition.jl index c558bcef..4fe55250 100644 --- a/test/test_binary_tree_partition.jl +++ b/test/test_binary_tree_partition.jl @@ -9,7 +9,8 @@ using ITensorNetworks: _is_rooted_directed_binary_tree, _contract_deltas_ignore_leaf_partitions, _rem_vertex!, - _DensityMartrixAlgGraph + _DensityMartrixAlgGraph, + _partition using Test @testset "test mincut functions on top of MPS" begin @@ -63,7 +64,7 @@ end @testset "test partition with mincut_recursive_bisection alg of disconnected tn" begin inds = [Index(2, "$i") for i in 1:5] tn = ITensorNetwork([randomITensor(i) for i in inds]) - par = partition(tn, binary_tree_structure(tn); alg="mincut_recursive_bisection") + par = _partition(tn, binary_tree_structure(tn); alg="mincut_recursive_bisection") networks = [Vector{ITensor}(par[v]) for v in vertices(par)] network = vcat(networks...) @test isapprox(contract(Vector{ITensor}(tn)), contract(network...)) @@ -82,7 +83,7 @@ end out1 = contract(network...) tn = ITensorNetwork(network) inds_btree = binary_tree_structure(tn) - par = partition(tn, inds_btree; alg="mincut_recursive_bisection") + par = _partition(tn, inds_btree; alg="mincut_recursive_bisection") par = _contract_deltas_ignore_leaf_partitions(par; root=_root(inds_btree)) networks = [Vector{ITensor}(par[v]) for v in vertices(par)] network2 = vcat(networks...) @@ -115,7 +116,7 @@ end M = MPS(T, (i, j, k, l, m); cutoff=1e-5, maxdim=5) tn = ITensorNetwork(M[:]) out_tree = path_graph_structure(tn) - input_partition = partition(tn, out_tree; alg="mincut_recursive_bisection") + input_partition = _partition(tn, out_tree; alg="mincut_recursive_bisection") underlying_tree = underlying_graph(input_partition) # Change type of each partition[v] since they will be updated # with potential data type chage. diff --git a/test/test_contract_deltas.jl b/test/test_contract_deltas.jl index d48eb2d5..151aa6c3 100644 --- a/test/test_contract_deltas.jl +++ b/test/test_contract_deltas.jl @@ -32,7 +32,7 @@ end end tn = ITensorNetwork(vec(tn[:, :, 1])) for inds_tree in [binary_tree_structure(tn), path_graph_structure(tn)] - par = partition(tn, inds_tree; alg="mincut_recursive_bisection") + par = _partition(tn, inds_tree; alg="mincut_recursive_bisection") root = _root(inds_tree) par_contract_deltas = _contract_deltas_ignore_leaf_partitions(par; root=root) @test Set(_noncommoninds(par)) == Set(_noncommoninds(par_contract_deltas)) diff --git a/test/test_examples/test_sqrt_bp.jl b/test/test_examples/test_sqrt_bp.jl index 19d90aa5..127eda83 100644 --- a/test/test_examples/test_sqrt_bp.jl +++ b/test/test_examples/test_sqrt_bp.jl @@ -5,6 +5,6 @@ using Test include(joinpath(pkgdir(ITensorNetworks), "examples", "belief_propagation", "sqrt_bp.jl")) @testset "Test sqrt_bp example" begin - (; sz_bp, sz_sqrt_bp) = @suppress main(; n=8, niters=10, β=0.28, h=0.5) + (; sz_bp, sz_sqrt_bp) = main(; n=8, niters=10, β=0.28, h=0.5) @test sz_bp ≈ sz_sqrt_bp end From 64ddf7cb09b04c5672f7ec05c1fb73dee427b4c2 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 23 Jan 2024 15:33:42 -0500 Subject: [PATCH 08/12] Improved functionality for BP. Removed BP example files for archiving --- examples/apply/apply_bp/apply_bp.jl | 169 ------------------ examples/apply/apply_bp/apply_bp_run.jl | 74 -------- examples/belief_propagation/bpexample.jl | 97 ---------- examples/belief_propagation/bpsequences.jl | 80 --------- examples/belief_propagation/sqrt_bp.jl | 71 -------- examples/boundary.jl | 8 +- .../dynamics/heavy_hex_ising_real_tebd.jl | 131 -------------- examples/gauging/gauging_itns.jl | 148 --------------- src/ITensorNetworks.jl | 1 - src/apply.jl | 14 +- src/beliefpropagation/beliefpropagation.jl | 47 +++-- .../sqrt_beliefpropagation.jl | 141 --------------- src/boundarymps.jl | 2 +- src/exports.jl | 1 - src/gauging.jl | 31 ++-- test/test_apply.jl | 15 +- test/test_belief_propagation.jl | 48 +++-- test/test_examples/test_apply_bp.jl | 28 --- test/test_examples/test_examples.jl | 3 - test/test_examples/test_sqrt_bp.jl | 10 -- test/test_gauging.jl | 4 +- test/test_indsnetwork.jl | 1 - 22 files changed, 86 insertions(+), 1038 deletions(-) delete mode 100644 examples/apply/apply_bp/apply_bp.jl delete mode 100644 examples/apply/apply_bp/apply_bp_run.jl delete mode 100644 examples/belief_propagation/bpexample.jl delete mode 100644 examples/belief_propagation/bpsequences.jl delete mode 100644 examples/belief_propagation/sqrt_bp.jl delete mode 100644 examples/dynamics/heavy_hex_ising_real_tebd.jl delete mode 100644 examples/gauging/gauging_itns.jl delete mode 100644 src/beliefpropagation/sqrt_beliefpropagation.jl delete mode 100644 test/test_examples/test_apply_bp.jl delete mode 100644 test/test_examples/test_sqrt_bp.jl diff --git a/examples/apply/apply_bp/apply_bp.jl b/examples/apply/apply_bp/apply_bp.jl deleted file mode 100644 index 4fd6159c..00000000 --- a/examples/apply/apply_bp/apply_bp.jl +++ /dev/null @@ -1,169 +0,0 @@ -using ITensorNetworks -using ITensorNetworks: - approx_network_region, - belief_propagation, - get_environment, - contract_inner, - message_tensors, - neighbor_vertices, - symmetric_gauge, - vidal_gauge, - vidal_to_symmetric_gauge, - norm_network -using Test -using Compat -using Dictionaries -using ITensors -using Metis -using NamedGraphs -using Observers -using Random -using LinearAlgebra -using SplitApplyCombine -using OMEinsumContractionOrders - -function expect_bp(opname, v, ψ, pψψ, mts) - s = siteinds(ψ) - numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op(opname, s[v]), ψ[v])] - ) - denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) - return contract(numerator_tensors)[] / contract(denominator_tensors)[] -end - -function vertex_array(ψ, v, v⃗ⱼ) - indsᵥ = unioninds((linkinds(ψ, v => vⱼ) for vⱼ in v⃗ⱼ)...) - indsᵥ = unioninds(siteinds(ψ, v), indsᵥ) - ψᵥ = ψ[v] - ψᵥ /= norm(ψᵥ) - return array(permute(ψᵥ, indsᵥ)) -end - -function simple_update_bp( - os, - ψ::ITensorNetwork; - maxdim, - variational_optimization_only=false, - regauge=false, - reduced=true, -) - println("Simple update, BP") - ψψ = norm_network(ψ) - pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 - ) - edges = PartitionEdge.(NamedGraphs.edges(partitioned_graph(pψψ))) - for layer in eachindex(os) - @show layer - o⃗ = os[layer] - for o in o⃗ - v⃗ = neighbor_vertices(ψ, o) - for e in edges - @assert order(only(mts[e])) == 2 - @assert order(only(mts[reverse(e)])) == 2 - end - - @assert length(v⃗) == 2 - v1, v2 = v⃗ - - pe = partitionedge(pψψ, NamedEdge((v1, 1) => (v2, 1))) - envs = get_environment(pψψ, mts, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) - obs = observer() - # TODO: Make a version of `apply` that accepts message tensors, - # and computes the environment and does the message tensor update of the bond internally. - ψ = apply( - o, - ψ; - envs, - (observer!)=obs, - maxdim, - normalize=true, - variational_optimization_only, - nfullupdatesweeps=20, - symmetrize=true, - reduced, - ) - S = only(obs.singular_values) - S /= norm(S) - - # Update message tensor - ψψ = norm_network(ψ) - pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mts[pe] = dense.(obs.singular_values) - mts[reverse(pe)] = dense.(obs.singular_values) - end - if regauge - println("regauge") - mts = belief_propagation( - pψψ, mts; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 - ) - end - end - return ψ, pψψ, mts -end - -function simple_update_vidal(os, ψ::ITensorNetwork; maxdim, regauge=false) - println("Simple update, Vidal gauge") - ψψ = norm_network(ψ) - pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), niters=50, target_precision=1e-5 - ) - ψ, bond_tensors = vidal_gauge(ψ, pψψ, mts) - for layer in eachindex(os) - @show layer - o⃗ = os[layer] - for o in o⃗ - v⃗ = neighbor_vertices(ψ, o) - ψ, bond_tensors = apply(o, ψ, bond_tensors; maxdim, normalize=true) - end - if regauge - println("regauge") - ψ_symmetric, pψψ_symmetric, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) - mts = belief_propagation( - pψψ_symmetric, - mts; - contract_kwargs=(; alg="exact"), - niters=50, - target_precision=1e-5, - ) - ψ, bond_tensors = vidal_gauge(ψ_symmetric, pψψ_symmetric, mts) - end - end - return ψ, bond_tensors -end - -function main(; - seed=5623, - graph, - opname, - dims, - χ, - nlayers, - variational_optimization_only=false, - regauge=false, - reduced=true, -) - Random.seed!(seed) - n = prod(dims) - g = graph(dims) - s = siteinds("S=1/2", g) - ψ = randomITensorNetwork(s; link_space=χ) - es = edges(g) - os = [ - [op(opname, s[src(e)]..., s[dst(e)]...; eltype=Float64) for e in es] for _ in 1:nlayers - ] - - # BP SU - ψ_bp, pψψ_bp, mts_bp = simple_update_bp( - os, ψ; maxdim=χ, variational_optimization_only, regauge, reduced - ) - # ψ_bp, mts = vidal_to_symmetric_gauge(vidal_gauge(ψ_bp, mts)...) - - # Vidal SU - ψ_vidal, bond_tensors = simple_update_vidal(os, ψ; maxdim=χ, regauge) - ψ_vidal, pψψ_vidal, mts_vidal = vidal_to_symmetric_gauge(ψ_vidal, bond_tensors) - - return ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal -end diff --git a/examples/apply/apply_bp/apply_bp_run.jl b/examples/apply/apply_bp/apply_bp_run.jl deleted file mode 100644 index 7c6ff8df..00000000 --- a/examples/apply/apply_bp/apply_bp_run.jl +++ /dev/null @@ -1,74 +0,0 @@ -include("apply_bp.jl") - -# opname = "Id" -opname = "RandomUnitary" - -@show opname - -# graph = named_comb_tree -graph = named_grid - -dims = (6, 6) - -ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal = main(; - seed=1234, - opname, - graph, - dims, - χ=2, - nlayers=2, - variational_optimization_only=false, - regauge=false, - reduced=true, -) - -v = dims .÷ 2 - -sz_bp = @show expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) -sz_vidal = @show expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) -@show abs(sz_bp - sz_vidal) / abs(sz_vidal) - -# Run BP again -mts_bp = belief_propagation( - pψψ_bp, - mts_bp; - contract_kwargs=(; alg="exact"), - niters=50, - target_precision=1e-7, - verbose=true, -) -mts_vidal = belief_propagation( - pψψ_vidal, - mts_vidal; - contract_kwargs=(; alg="exact"), - niters=50, - target_precision=1e-7, - verbose=true, -) - -sz_bp = @show expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) -sz_vidal = @show expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) -@show abs(sz_bp - sz_vidal) / abs(sz_vidal) - -ψ_symmetric, _, _ = symmetric_gauge(ψ_bp) - -v⃗ⱼ = [v .+ (1, 0), v .- (1, 0), v .+ (0, 1), v .- (0, 1)] -ψ_bp_v = vertex_array(ψ_bp, v, v⃗ⱼ) -ψ_vidal_v = vertex_array(ψ_vidal, v, v⃗ⱼ) -ψ_symmetric_v = vertex_array(ψ_symmetric, v, v⃗ⱼ) - -@show norm(abs.(ψ_bp_v) - abs.(ψ_vidal_v)) -@show norm(abs.(ψ_bp_v) - abs.(ψ_symmetric_v)) -@show norm(abs.(ψ_vidal_v) - abs.(ψ_symmetric_v)) - -# inner_ψ_net = inner_network(ψ_bp, ψ_vidal) -# norm_ψ_bp_net = norm_network(ψ_bp) -# norm_ψ_vidal_net = norm_network(ψ_vidal) -# seq = contraction_sequence(inner_ψ_net; alg="sa_bipartite") -# @disable_warn_order begin -# inner_ψ = contract(inner_ψ_net; sequence=seq)[] -# norm_sqr_ψ_bp = contract(norm_ψ_bp_net; sequence=seq)[] -# norm_sqr_ψ_vidal = contract(norm_ψ_vidal_net; sequence=seq)[] -# end -# @show 1 - inner_ψ / (sqrt(norm_sqr_ψ_bp) * sqrt(norm_sqr_ψ_vidal)) -# @show log(inner_ψ) - (log(norm_sqr_ψ_bp) / 2 + log(norm_sqr_ψ_vidal) / 2) diff --git a/examples/belief_propagation/bpexample.jl b/examples/belief_propagation/bpexample.jl deleted file mode 100644 index 62eba8fa..00000000 --- a/examples/belief_propagation/bpexample.jl +++ /dev/null @@ -1,97 +0,0 @@ -using Compat -using ITensors -using Metis -using ITensorNetworks -using Random -using SplitApplyCombine -using NamedGraphs - -using ITensorNetworks: - belief_propagation, approx_network_region, contract_inner, message_tensors - -function main() - n = 4 - g_dims = (n, n) - g = named_grid(g_dims) - s = siteinds("S=1/2", g) - chi = 2 - - Random.seed!(5467) - - #bra - ψ = randomITensorNetwork(s; link_space=chi) - #bra-ket (but not actually contracted) - ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - - #Site to take expectation value on - v = (1, 1) - - #Now do Simple Belief Propagation to Measure Sz on Site v - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 - ) - numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] - ) - denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) - sz_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] - - println( - "Simple Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_bp) - ) - - #Now do Column-wise General Belief Propagation to Measure Sz on Site v - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 - ) - numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] - ) - denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) - sz_gen_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] - - println( - "General Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_gen_bp) - ) - - #Now do General Belief Propagation with Matrix Product State Message Tensors Measure Sz on Site v - ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) - Oψ = copy(ψ) - Oψ[v] = apply(op("Sz", s[v]), ψ[v]) - ψOψ = flatten_networks(ψ, dag(Oψ); combine_linkinds=false, map_bra_linkinds=prime) - - combiners = linkinds_combiners(ψψ) - ψψ = combine_linkinds(ψψ, combiners) - ψOψ = combine_linkinds(ψOψ, combiners) - - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation( - pψψ; - itensor_constructor=inds_e -> ITensor[dense(delta(i)) for i in inds_e], - contract_kwargs=(; - alg="density_matrix", - output_structure=path_graph_structure, - maxdim=8, - contraction_sequence_alg="optimal", - ), - ) - numerator_tensors = approx_network_region(pψψ, mts, [v]; verts_tensors=[ψOψ[v]]) - denominator_tensors = approx_network_region(pψψ, mts, [v]) - sz_MPS_bp = contract(numerator_tensors)[] / contract(denominator_tensors)[] - - println( - "Column-Wise MPS Belief Propagation Gives Sz on Site " * - string(v) * - " as " * - string(sz_gen_bp), - ) - - #Now do it exactly - sz_exact = contract(ψOψ)[] / contract(ψψ)[] - - return println("The exact value of Sz on Site " * string(v) * " is " * string(sz_exact)) -end - -main() diff --git a/examples/belief_propagation/bpsequences.jl b/examples/belief_propagation/bpsequences.jl deleted file mode 100644 index 8f3b1438..00000000 --- a/examples/belief_propagation/bpsequences.jl +++ /dev/null @@ -1,80 +0,0 @@ -using Compat -using ITensors -using Metis -using ITensorNetworks -using Random -using SplitApplyCombine -using Graphs -using NamedGraphs - -using ITensorNetworks: - belief_propagation, approx_network_region, contract_inner, message_tensors, edge_sequence - -function main() - g_labels = [ - "Comb Tree", - "100 Site Random Regular Graph z = 3", - "6x6 Square Grid", - "4x4 Hexagonal Lattice", - ] - gs = [ - named_comb_tree((6, 6)), - NamedGraph(Graphs.random_regular_graph(100, 3)), - named_grid((6, 6)), - NamedGraphs.hexagonal_lattice_graph(4, 4), - ] - χs = [4, 4, 2, 3] - - for (i, g) in enumerate(gs) - Random.seed!(5467) - g_label = g_labels[i] - χ = χs[i] - s = siteinds("S=1/2", g) - ψ = randomITensorNetwork(s; link_space=χ) - ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - - #Initial message tensors for BP - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts_init = message_tensors(pψψ) - - println("\nFirst testing out a $g_label. Random network with bond dim $χ") - - #Now test out various sequences - print("Parallel updates (sequence is irrelevant): ") - belief_propagation( - pψψ, - mts_init; - contract_kwargs=(; alg="exact"), - target_precision=1e-10, - niters=100, - edges=[ - PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel") - ], - verbose=true, - ) - print("Sequential updates (sequence is default edge list of the message tensors): ") - belief_propagation( - pψψ, - mts_init; - contract_kwargs=(; alg="exact"), - target_precision=1e-10, - niters=100, - edges=PartitionEdge.([ - e for - e in vcat(edges(partitioned_graph(pψψ)), reverse.(edges(partitioned_graph(pψψ)))) - ]), - verbose=true, - ) - print("Sequential updates (sequence is our custom sequence finder): ") - belief_propagation( - pψψ, - mts_init; - contract_kwargs=(; alg="exact"), - target_precision=1e-10, - niters=100, - verbose=true, - ) - end -end - -main() diff --git a/examples/belief_propagation/sqrt_bp.jl b/examples/belief_propagation/sqrt_bp.jl deleted file mode 100644 index 512e5981..00000000 --- a/examples/belief_propagation/sqrt_bp.jl +++ /dev/null @@ -1,71 +0,0 @@ -using ITensors -using ITensorNetworks -using Random -using SplitApplyCombine -using NamedGraphs - -using ITensorNetworks: - approx_network_region, - belief_propagation, - sqrt_belief_propagation, - ising_network_state, - message_tensors - -function main(; n, niters, network="ising", β=nothing, h=nothing, χ=nothing) - g_dims = (n, n) - g = named_grid(g_dims) - s = siteinds("S=1/2", g) - - Random.seed!(5467) - - ψ = if network == "ising" - ising_network_state(s, β; h) - elseif network == "random" - randomITensorNetwork(s; link_space=χ) - else - error("Network type $network not supported.") - end - - ψψ = norm_network(ψ) - - # Site to take expectation value on - v = (n ÷ 2, n ÷ 2) - - #Now do Simple Belief Propagation to Measure Sz on Site v - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - - mts = @time belief_propagation(pψψ; niters, contract_kwargs=(; alg="exact")) - numerator_network = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])] - ) - denominator_network = approx_network_region(pψψ, mts, [(v, 1)]) - sz_bp = - ITensors.contract( - numerator_network; sequence=contraction_sequence(numerator_network; alg="optimal") - )[] / ITensors.contract( - denominator_network; sequence=contraction_sequence(denominator_network; alg="optimal") - )[] - - println( - "Simple Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_bp) - ) - - mts_sqrt = @time sqrt_belief_propagation(pψψ; niters) - - numerator_network = approx_network_region( - pψψ, mts_sqrt, [(v, 1)]; verts_tensors=ITensor[apply(op("Sz", s[v]), ψ[v])] - ) - denominator_network = approx_network_region(pψψ, mts_sqrt, [(v, 1)]) - sz_sqrt_bp = - contract( - numerator_network; sequence=contraction_sequence(numerator_network; alg="optimal") - )[] / contract( - denominator_network; sequence=contraction_sequence(denominator_network; alg="optimal") - )[] - - println( - "Sqrt Belief Propagation Gives Sz on Site " * string(v) * " as " * string(sz_sqrt_bp) - ) - - return (; sz_bp, sz_sqrt_bp) -end diff --git a/examples/boundary.jl b/examples/boundary.jl index 71071119..6305744d 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -8,10 +8,10 @@ tn = ITensorNetwork(named_grid((6, 3)); link_space=4) @visualize tn -ptn = PartitionedGraph( - tn, partitioned_vertices(underlying_graph(tn); nvertices_per_partition=2) -) -sub_vs_1, sub_vs_2 = vertices(ptn, PartitionVertex(1)), vertices(ptn, PartitionVertex(2)) +pvertices = partitioned_vertices(underlying_graph(tn); nvertices_per_partition=2) +ptn = PartitionedGraph(tn, pvertices) +sub_vs_1 = vertices(ptn, PartitionVertex(1)) +sub_vs_2 = vertices(ptn, PartitionVertex(2)) @show (1, 1) ∈ sub_vs_1 @show (6, 3) ∈ sub_vs_2 diff --git a/examples/dynamics/heavy_hex_ising_real_tebd.jl b/examples/dynamics/heavy_hex_ising_real_tebd.jl deleted file mode 100644 index d17084a9..00000000 --- a/examples/dynamics/heavy_hex_ising_real_tebd.jl +++ /dev/null @@ -1,131 +0,0 @@ -using ITensors -using ITensorNetworks -using NamedGraphs -using NamedGraphs: rem_edges!, add_edge!, decorate_graph_edges, hexagonal_lattice_graph -using Graphs - -using ITensorNetworks: - initialize_bond_tensors, - vidal_itn_canonicalness, - vidal_to_symmetric_gauge, - vidal_gauge, - approx_network_region, - optimal_contraction_sequence, - contract, - symmetric_to_vidal_gauge, - norm_network - -function heavy_hex_lattice_graph(n::Int64, m::Int64) - g = hexagonal_lattice_graph(n, m) - g = decorate_graph_edges(g) - return g -end - -function ibm_processor_graph(n::Int64, m::Int64) - g = heavy_hex_lattice_graph(n, m) - dims = maximum(vertices(hexagonal_lattice_graph(n, m))) - v1, v2 = (dims[1], 1), (1, dims[2]) - add_vertices!(g, [v1, v2]) - add_edge!(g, v1 => v1 .- (1, 0)) - add_edge!(g, v2 => v2 .+ (1, 0)) - - return g -end - -eagle_processor_graph() = ibm_processor_graph(3, 6) -hummingbird_processor_graph() = ibm_processor_graph(2, 4) -osprey_processor_graph() = ibm_processor_graph(6, 12) - -"""Take the expectation value of o on an ITN using belief propagation""" -function expect_state_SBP(o::ITensor, ψ::AbstractITensorNetwork, pψψ::PartitionedGraph, mts) - Oψ = apply(o, ψ; cutoff=1e-16) - ψ = copy(ψ) - s = siteinds(ψ) - vs = vertices(s)[findall(i -> (length(commoninds(s[i], inds(o))) != 0), vertices(s))] - vs_braket = [(v, 1) for v in vs] - - numerator_tensors = approx_network_region( - pψψ, mts, vs_braket; verts_tensors=ITensor[Oψ[v] for v in vs] - ) - denominator_tensors = approx_network_region(pψψ, mts, vs_braket) - num_seq = contraction_sequence(numerator_tensors; alg="optimal") - den_seq = contraction_sequence(denominator_tensors; alg="optimal") - return contract(numerator_tensors; sequence=num_seq)[] / - contract(denominator_tensors; sequence=den_seq)[] -end - -function main(θh::Float64, no_trotter_steps::Int64; apply_kwargs...) - - #Build the graph - g = eagle_processor_graph() - - #Do this if measuring a Z based expectation value (i.e. ignore ZZ_gates in final layer as they are irrelevant) - shortened_final_layer = true - - s = siteinds("S=1/2", g) - - #Gauging parameters - target_c = 1e-3 - gauge_freq = 1 - - #State initialisation - ψ = ITensorNetwork(s, v -> "↑") - bond_tensors = initialize_bond_tensors(ψ) - - #Build gates - HX, HZZ = ising(g; h=θh / 2, J1=0), ising(g; h=0, J1=-pi / 4) - RX, RZZ = exp(-im * HX; alg=Trotter{1}()), exp(-im * HZZ; alg=Trotter{1}()) - RX_gates, RZZ_gates = Vector{ITensor}(RX, s), Vector{ITensor}(RZZ, s) - - for i in 1:no_trotter_steps - println("On Trotter Step $i") - gates = if (shortened_final_layer && i == no_trotter_steps) - RX_gates - else - vcat(RX_gates, RZZ_gates) - end - - for gate in gates - ψ, bond_tensors = apply(gate, ψ, bond_tensors; normalize=true, apply_kwargs...) - end - - cur_C = vidal_itn_canonicalness(ψ, bond_tensors) - if ((i + 1) % gauge_freq) == 0 && (cur_C >= target_c) - println("Too far from the Vidal gauge. Regauging the state!") - ψ, _, _ = vidal_to_symmetric_gauge(ψ, bond_tensors) - ψ, bond_tensors = vidal_gauge( - ψ; target_canonicalness=target_c, niters=20, cutoff=1e-14 - ) - end - end - - #Calculate on-site magnetisations - ψ, pψψ, mts = vidal_to_symmetric_gauge(ψ, bond_tensors) - mag_dict = Dict( - zip( - [v for v in vertices(ψ)], - [expect_state_SBP(op("Z", s[v]), ψ, pψψ, mts) for v in vertices(ψ)], - ), - ) - - return mag_dict -end - -ibm_processor_graph(3, 6) - -θh = pi / 4 -no_trotter_steps = 5 -χ = 32 -apply_kwargs = (; maxdim=χ, cutoff=1e-12) - -println( - "Simulating $no_trotter_steps Steps of the Kicked Ising Model on the Heavy Hex Layout." -) -println("θh is $θh. Maxdim is $χ.") - -mags = main(θh, no_trotter_steps; apply_kwargs...) - -println( - "After $no_trotter_steps steps. Average magnetisation is " * - string(sum(values(mags)) / length(values(mags))), -) diff --git a/examples/gauging/gauging_itns.jl b/examples/gauging/gauging_itns.jl deleted file mode 100644 index 6c988753..00000000 --- a/examples/gauging/gauging_itns.jl +++ /dev/null @@ -1,148 +0,0 @@ -using Compat -using ITensors -using Metis -using ITensorNetworks -using Random -using SplitApplyCombine - -using ITensorNetworks: - message_tensors, - belief_propagation, - vidal_gauge, - symmetric_gauge, - vidal_itn_canonicalness, - initialize_bond_tensors, - vidal_itn_isometries, - norm_network, - edge_sequence, - belief_propagation_iteration - -using NamedGraphs -using NamedGraphs: add_edges!, rem_vertex!, hexagonal_lattice_graph -using Graphs - -"""Eager Gauging""" -function eager_gauging( - ψ::ITensorNetwork, pψψ::PartitionedGraph, bond_tensors::DataGraph, mts -) - isometries = vidal_itn_isometries(ψ, bond_tensors) - - ψ = copy(ψ) - mts = copy(mts) - for e in edges(ψ) - vsrc, vdst = src(e), dst(e) - pe = which_partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) - normalize!(isometries[e]) - normalize!(isometries[reverse(e)]) - mts[pe], mts[reverse(pe)] = ITensorNetwork(isometries[e]), - ITensorNetwork(isometries[reverse(e)]) - end - - ψ, bond_tensors = vidal_gauge(ψ, pψψ, mts, bond_tensors) - - return ψ, bond_tensors, mts -end - -"""Bring an ITN into the Vidal gauge, various methods possible. Result is timed""" -function benchmark_state_gauging( - ψ::ITensorNetwork; - mode="belief_propagation", - no_iterations=50, - BP_update_order::String="sequential", -) - s = siteinds(ψ) - - C = zeros((no_iterations)) - times_iters = zeros((no_iterations)) - times_gauging = zeros((no_iterations)) - - ψψ = norm_network(ψ) - ψinit = copy(ψ) - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = message_tensors(pψψ) - bond_tensors = initialize_bond_tensors(ψ) - - for i in 1:no_iterations - println("On Iteration " * string(i)) - - if mode == "belief_propagation" - if BP_update_order != "parallel" - times_iters[i] = @elapsed mts, _ = belief_propagation_iteration( - pψψ, mts; contract_kwargs=(; alg="exact") - ) - else - times_iters[i] = @elapsed mts, _ = belief_propagation_iteration( - pψψ, - mts; - contract_kwargs=(; alg="exact"), - edges=[ - PartitionEdge.(e) for e in edge_sequence(partitioned_graph(pψψ); alg="parallel") - ], - ) - end - - times_gauging[i] = @elapsed ψ, bond_tensors = vidal_gauge(ψinit, pψψ, mts) - elseif mode == "eager" - times_iters[i] = @elapsed ψ, bond_tensors, mts = eager_gauging( - ψ, pψψ, bond_tensors, mts - ) - else - times_iters[i] = @elapsed begin - for e in edges(ψ) - ψ, bond_tensors = apply(e, ψ, bond_tensors; normalize=true, cutoff=1e-16) - end - end - end - - C[i] = vidal_itn_canonicalness(ψ, bond_tensors) - end - @show times_iters, time - simulation_times = cumsum(times_iters) + times_gauging - - return simulation_times, C -end - -L, χ = 10, 10 -g = named_grid((L, L)) -s = siteinds("S=1/2", g) -ψ = randomITensorNetwork(s; link_space=χ) -no_iterations = 30 - -BPG_simulation_times, BPG_Cs = benchmark_state_gauging( - ψ; no_iterations, BP_update_order="parallel" -) -BPG_sequential_simulation_times, BPG_sequential_Cs = benchmark_state_gauging( - ψ; no_iterations -) -Eager_simulation_times, Eager_Cs = benchmark_state_gauging(ψ; mode="eager", no_iterations) -SU_simulation_times, SU_Cs = benchmark_state_gauging(ψ; mode="SU", no_iterations) - -epsilon = 1e-10 - -println( - "Time for BPG (with parallel updates) to reach C < epsilon was " * - string(BPG_simulation_times[findfirst(x -> x < 0, BPG_Cs .- epsilon)]) * - " seconds. No iters was " * - string(findfirst(x -> x < 0, BPG_Cs .- epsilon)), -) -println( - "Time for BPG (with sequential updates) to reach C < epsilon was " * - string( - BPG_sequential_simulation_times[findfirst(x -> x < 0, BPG_sequential_Cs .- epsilon)] - ) * - " seconds. No iters was " * - string(findfirst(x -> x < 0, BPG_sequential_Cs .- epsilon)), -) - -println( - "Time for Eager Gauging to reach C < epsilon was " * - string(Eager_simulation_times[findfirst(x -> x < 0, Eager_Cs .- epsilon)]) * - " seconds. No iters was " * - string(findfirst(x -> x < 0, Eager_Cs .- epsilon)), -) -println( - "Time for SU Gauging (with sequential updates) to reach C < epsilon was " * - string(SU_simulation_times[findfirst(x -> x < 0, SU_Cs .- epsilon)]) * - " seconds. No iters was " * - string(findfirst(x -> x < 0, SU_Cs .- epsilon)), -) diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index 47f14b4e..b457864f 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -99,7 +99,6 @@ include("renameitensornetwork.jl") include("boundarymps.jl") include(joinpath("beliefpropagation", "beliefpropagation.jl")) include(joinpath("beliefpropagation", "beliefpropagation_schedule.jl")) -include(joinpath("beliefpropagation", "sqrt_beliefpropagation.jl")) include("contraction_tree_to_graph.jl") include("gauging.jl") include("utils.jl") diff --git a/src/apply.jl b/src/apply.jl index 676f5867..0923229a 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -297,10 +297,10 @@ end #In the future we will try to unify this into apply() above but currently leave it mostly as a separate function """Apply() function for an ITN in the Vidal Gauge. Hence the bond tensors are required. Gate does not necessarily need to be passed. Can supply an edge to do an identity update instead. Uses Simple Update procedure assuming gate is two-site""" -function ITensors.apply( +function vidal_apply( o::Union{ITensor,NamedEdge}, ψ::AbstractITensorNetwork, - bond_tensors::DataGraph; + bond_tensors; normalize=false, apply_kwargs..., ) @@ -314,13 +314,13 @@ function ITensors.apply( for vn in neighbors(ψ, src(e)) if (vn != dst(e)) - ψv1 = noprime(ψv1 * bond_tensors[vn => src(e)]) + ψv1 = noprime(ψv1 * bond_tensors[NamedEdge(vn => src(e))]) end end for vn in neighbors(ψ, dst(e)) if (vn != src(e)) - ψv2 = noprime(ψv2 * bond_tensors[vn => dst(e)]) + ψv2 = noprime(ψv2 * bond_tensors[NamedEdge(vn => dst(e))]) end end @@ -339,17 +339,17 @@ function ITensors.apply( replaceind!(S, ind_to_replace, ind_to_replace_with') replaceind!(V, ind_to_replace, ind_to_replace_with) - ψv1, bond_tensors[e], ψv2 = U * Qᵥ₁, S, V * Qᵥ₂ + ψv1, bond_tensors[e], bond_tensors[reverse(e)], ψv2 = U * Qᵥ₁, S, S, V * Qᵥ₂ for vn in neighbors(ψ, src(e)) if (vn != dst(e)) - ψv1 = noprime(ψv1 * inv_diag(bond_tensors[vn => src(e)])) + ψv1 = noprime(ψv1 * inv_diag(bond_tensors[NamedEdge(vn => src(e))])) end end for vn in neighbors(ψ, dst(e)) if (vn != src(e)) - ψv2 = noprime(ψv2 * inv_diag(bond_tensors[vn => dst(e)])) + ψv2 = noprime(ψv2 * inv_diag(bond_tensors[NamedEdge(vn => dst(e))])) end end diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 5a0b7b79..cfbafdf6 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -3,8 +3,8 @@ function message_tensors( ) mts = Dict() for e in partitionedges(ptn) - src_e_itn = unpartitioned_graph(subgraph(ptn, [src(e)])) - dst_e_itn = unpartitioned_graph(subgraph(ptn, [dst(e)])) + src_e_itn = subgraph(ptn, src(e)) + dst_e_itn = subgraph(ptn, dst(e)) inds_e = commoninds(src_e_itn, dst_e_itn) mts[e] = itensor_constructor(inds_e) mts[reverse(e)] = dag.(mts[e]) @@ -16,10 +16,7 @@ end Do a single update of a message tensor using the current subgraph and the incoming mts """ function update_message_tensor( - ptn::PartitionedGraph, - edge::PartitionEdge, - mts; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), + ptn::PartitionedGraph, edge::PartitionEdge, mts; contract_kwargs=(; alg="exact") ) pedges = setdiff( partitionedges(ptn, boundary_edges(ptn, vertices(ptn, src(edge)); dir=:in)), @@ -30,7 +27,7 @@ function update_message_tensor( contract_list = ITensor[ incoming_messages - ITensor(unpartitioned_graph(subgraph(ptn, [src(edge)]))) + ITensor(subgraph(ptn, src(edge))) ] if contract_kwargs.alg != "exact" @@ -54,7 +51,7 @@ function belief_propagation_iteration( ptn::PartitionedGraph, mts, edges::Vector{<:PartitionEdge}; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), + contract_kwargs=(; alg="exact"), compute_norm=false, ) new_mts = copy(mts) @@ -82,7 +79,7 @@ function belief_propagation_iteration( ptn::PartitionedGraph, mts, edge_groups::Vector{<:Vector{<:PartitionEdge}}; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), + contract_kwargs=(; alg="exact"), compute_norm=false, ) new_mts = copy(mts) @@ -102,7 +99,7 @@ end function belief_propagation_iteration( ptn::PartitionedGraph, mts; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), + contract_kwargs=(; alg="exact"), compute_norm=false, edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), ) @@ -112,7 +109,7 @@ end function belief_propagation( ptn::PartitionedGraph, mts; - contract_kwargs=(; alg="density_matrix", output_structure=path_graph_structure, maxdim=1), + contract_kwargs=(; alg="exact"), niters=default_bp_niters(partitioned_graph(ptn)), target_precision=nothing, edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), @@ -143,17 +140,12 @@ function belief_propagation( return belief_propagation(ptn, mts; kwargs...) end """ -Given a subet of vertices of a given Tensor Network and the Message Tensors for that network, return a Dictionary with the involved subgraphs as keys and the vector of tensors associated with that subgraph as values -Specifically, the contraction of the environment tensors and tn[vertices] will be a scalar. +Given a subet of partitionvertices of a ptn get the incoming message tensors to that region """ -function get_environment(ptn::PartitionedGraph, mts, verts::Vector; dir=:in) +function get_environment_tensors(ptn::PartitionedGraph, mts, verts::Vector) partition_verts = partitionvertices(ptn, verts) central_verts = vertices(ptn, partition_verts) - if dir == :out - return get_environment(ptn, mts, setdiff(vertices(ptn), verts)) - end - pedges = partitionedges(ptn, boundary_edges(ptn, central_verts; dir=:in)) env_tensors = [mts[e] for e in pedges] env_tensors = reduce(vcat, env_tensors; init=ITensor[]) @@ -164,6 +156,12 @@ function get_environment(ptn::PartitionedGraph, mts, verts::Vector; dir=:in) return vcat(env_tensors, central_tensors) end +function get_environment_tensors( + ptn::PartitionedGraph, mts, partition_verts::Vector{<:PartitionVertex} +) + return get_environment_tensors(ptn, mts, vertices(ptn, partition_verts)) +end + """ Calculate the contraction of a tensor network centred on the vertices verts. Using message tensors. Defaults to using tn[verts] as the local network but can be overriden @@ -174,7 +172,18 @@ function approx_network_region( verts::Vector; verts_tensors=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], ) - environment_tensors = get_environment(ptn, mts, verts) + environment_tensors = get_environment_tensors(ptn, mts, verts) return vcat(environment_tensors, verts_tensors) end + +function approx_network_region( + ptn::PartitionedGraph, + mts, + partition_verts::Vector{<:PartitionVertex}; + verts_tensors=ITensor[ + (unpartitioned_graph(ptn))[v] for v in vertices(ptn, partition_verts) + ], +) + return approx_network_region(ptn, mts, vertices(ptn, partition_verts); verts_tensors) +end diff --git a/src/beliefpropagation/sqrt_beliefpropagation.jl b/src/beliefpropagation/sqrt_beliefpropagation.jl deleted file mode 100644 index dc13713e..00000000 --- a/src/beliefpropagation/sqrt_beliefpropagation.jl +++ /dev/null @@ -1,141 +0,0 @@ -# using ITensors: scalartype -# using ITensorNetworks: find_subgraph, map_diag, sqrt_diag, boundary_edges - -function sqrt_belief_propagation_iteration( - pψψ::PartitionedGraph, sqrt_mts, edges::Vector{<:PartitionEdge} -) - new_sqrt_mts = copy(sqrt_mts) - c = 0.0 - for e in edges - new_sqrt_mts[e] = ITensor[update_sqrt_message_tensor(pψψ, e, new_sqrt_mts;)] - - # if compute_norm - # LHS, RHS = ITensors.contract(ITensor(sqrt_mts[src(e) => dst(e)])), - # ITensors.contract(ITensor(new_sqrt_mts[src(e) => dst(e)])) - # LHS /= sum(diag(LHS)) - # RHS /= sum(diag(RHS)) - # c += 0.5 * norm(LHS - RHS) - # end - end - return new_sqrt_mts, c / (length(edges)) -end - -function sqrt_belief_propagation_iteration( - pψψ::PartitionedGraph, sqrt_mts, edges::Vector{<:Vector{<:PartitionEdge}} -) - new_sqrt_mts = copy(sqrt_mts) - c = 0.0 - for e_group in edges - updated_sqrt_mts, ct = sqrt_belief_propagation_iteration(pψψ, sqr_mts, e_group) - for e in e_group - new_sqrt_mts[e] = updated_sqrt_mts[e] - end - c += ct - end - return new_sqrt_mts, c / (length(edges)) -end - -function sqrt_belief_propagation_iteration( - pψψ::PartitionedGraph, sqrt_mts; edges=edge_sequence(partitioned_graph(pψψ)) -) - return sqrt_belief_propagation_iteration(pψψ, sqrt_mts, edges) -end - -function sqrt_belief_propagation( - pψψ::PartitionedGraph, - mts; - niters=default_bp_niters(partitioned_graph(pψψ)), - edges=PartitionEdge.(edge_sequence(partitioned_graph(pψψ))), - # target_precision::Union{Float64,Nothing}=nothing, -) - # compute_norm = target_precision == nothing ? false : true - sqrt_mts = sqrt_message_tensors(pψψ, mts) - if isnothing(niters) - error("You need to specify a number of iterations for BP!") - end - for i in 1:niters - sqrt_mts, c = sqrt_belief_propagation_iteration(pψψ, sqrt_mts, edges) #; compute_norm) - # if compute_norm && c <= target_precision - # println( - # "Belief Propagation finished. Reached a canonicalness of " * - # string(c) * - # " after $i iterations. ", - # ) - # break - # end - end - return sqr_message_tensors(sqrt_mts) -end - -function sqrt_belief_propagation(ptn::PartitionedGraph; kwargs...) - mts = message_tensors(ptn) - return sqrt_belief_propagation(ptn, mts; kwargs...) -end - -function update_sqrt_message_tensor(pψψ::PartitionedGraph, edge::PartitionEdge, sqrt_mts;) - v = only(filter(v -> v[2] == 1, vertices(pψψ, src(edge)))) - site_itensor = unpartitioned_graph(pψψ)[v] - p_edges = setdiff(partitionedges(pψψ, boundary_edges(pψψ, [v]; dir=:in)), [reverse(edge)]) - incoming_messages = [sqrt_mts[e_in] for e_in in p_edges] - incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) - contract_list = ITensor[incoming_messages; site_itensor] - contract_output = contract( - contract_list; sequence=contraction_sequence(contract_list; alg="optimal") - ) - left_inds = [ - uniqueinds(contract_output, site_itensor) - siteinds(unpartitioned_graph(pψψ), v) - ] - Q, R = qr(contract_output, left_inds) - normalize!(R) - return R -end - -function sqrt_message_tensors( - pψψ::PartitionedGraph, - mts; - eigen_message_tensor_cutoff=10 * eps(real(scalartype(unpartitioned_graph(pψψ)))), - regularization=10 * eps(real(scalartype(unpartitioned_graph(pψψ)))), -) - sqrt_mts = copy(mts) - for e in partitionedges(pψψ) - vsrc, vdst = filter(v -> v[2] == 1, vertices(pψψ, src(e))), - filter(v -> v[2] == 1, vertices(pψψ, dst(e))) - ψvsrc, ψvdst = unpartitioned_graph(pψψ)[only(vsrc)], - unpartitioned_graph(pψψ)[only(vdst)] - - edge_ind = commoninds(ψvsrc, ψvdst) - edge_ind_sim = sim(edge_ind) - - X_D, X_U = eigen(only(mts[e]); ishermitian=true, cutoff=eigen_message_tensor_cutoff) - Y_D, Y_U = eigen( - only(mts[reverse(e)]); ishermitian=true, cutoff=eigen_message_tensor_cutoff - ) - X_D, Y_D = map_diag(x -> x + regularization, X_D), - map_diag(x -> x + regularization, Y_D) - - rootX_D, rootY_D = sqrt_diag(X_D), sqrt_diag(Y_D) - rootX = X_U * rootX_D * prime(dag(X_U)) - rootY = Y_U * rootY_D * prime(dag(Y_U)) - - sqrt_mts[e] = ITensor[rootX] - sqrt_mts[reverse(e)] = ITensor[rootY] - end - return sqrt_mts -end - -function sqr_message_tensors(sqrt_mts) - mts = copy(sqrt_mts) - for e in keys(sqrt_mts) - sqrt_mt = only(sqrt_mts[e]) - sqrt_mt_rev = only(sqrt_mts[reverse(e)]) - l = commoninds(sqrt_mt, sqrt_mt_rev) - mt = dag(prime(sqrt_mt, l)) * sqrt_mt - normalize!(mt) - mt_rev = dag(prime(sqrt_mt_rev, l)) * sqrt_mt_rev - normalize!(mt_rev) - mts[e] = ITensor[mt] - mts[reverse(e)] = ITensor[mt_rev] - end - return mts -end diff --git a/src/boundarymps.jl b/src/boundarymps.jl index 449442d6..8e27868e 100644 --- a/src/boundarymps.jl +++ b/src/boundarymps.jl @@ -1,4 +1,4 @@ -#GIVEN AN ITENSOR NETWORK CORRESPONDING to an Lx*Ly grid with sites indexed as (i,j) then perform contraction using a sequence of mps-mpo contractions +#Given an ITensorNetwork on an Lx*Ly grid with sites indexed as (i,j) then perform contraction using a sequence of mps-mpo contractions function contract_boundary_mps(tn::ITensorNetwork; kwargs...) dims = maximum(vertices(tn)) d1, d2 = dims diff --git a/src/exports.jl b/src/exports.jl index 24623f1c..f298994d 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -73,7 +73,6 @@ export AbstractITensorNetwork, TreeTensorNetwork, TTN, random_ttn, - PartitionedITensorNetwork, ProjTTN, ProjTTNSum, ProjTTNApply, diff --git a/src/gauging.jl b/src/gauging.jl index 25fce5d0..4b36badf 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -1,10 +1,11 @@ """initialize bond tensors of an ITN to identity matrices""" function initialize_bond_tensors(ψ::ITensorNetwork; index_map=prime) - bond_tensors = DataGraph{vertextype(ψ),ITensor,ITensor}(underlying_graph(ψ)) + bond_tensors = Dict() for e in edges(ψ) index = commoninds(ψ[src(e)], ψ[dst(e)]) bond_tensors[e] = dense(delta(index, index_map(index))) + bond_tensors[reverse(e)] = bond_tensors[e] end return bond_tensors @@ -15,7 +16,7 @@ function vidal_gauge( ψ::ITensorNetwork, pψψ::PartitionedGraph, mts, - bond_tensors::DataGraph; + bond_tensors; eigen_message_tensor_cutoff=10 * eps(real(scalartype(ψ))), regularization=10 * eps(real(scalartype(ψ))), edges=NamedGraphs.edges(ψ), @@ -67,7 +68,7 @@ function vidal_gauge( [commoninds(S, U)..., commoninds(S, V)...] => [new_edge_ind..., prime(new_edge_ind)...], ) - bond_tensors[e] = S + bond_tensors[e], bond_tensors[reverse(e)] = S, S end return ψ_vidal, bond_tensors @@ -107,7 +108,7 @@ function vidal_gauge( svd_kwargs..., ) ψψ = norm_network(ψ) - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = message_tensors(pψψ) mts = belief_propagation( @@ -124,10 +125,10 @@ function vidal_gauge( end """Transform from an ITensor in the Vidal Gauge (bond tensors) to the Symmetric Gauge (partitionedgraph, message tensors)""" -function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) +function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors) ψsymm = copy(ψ) ψψsymm = norm_network(ψsymm) - pψψsymm = PartitionedGraph(ψψsymm, collect(values(group(v -> v[1], vertices(ψψsymm))))) + pψψsymm = PartitionedGraph(ψψsymm, group(v -> v[1], vertices(ψψsymm))) ψsymm_mts = message_tensors(pψψsymm) for e in edges(ψsymm) @@ -142,7 +143,7 @@ function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) end ψψsymm = norm_network(ψsymm) - pψψsymm = PartitionedGraph(ψψsymm, collect(values(group(v -> v[1], vertices(ψψsymm))))) + pψψsymm = PartitionedGraph(ψψsymm, group(v -> v[1], vertices(ψψsymm))) return ψsymm, pψψsymm, ψsymm_mts end @@ -175,14 +176,14 @@ function symmetric_to_vidal_gauge( mts; regularization=10 * eps(real(scalartype(ψ))), ) - bond_tensors = DataGraph{vertextype(ψ),ITensor,ITensor}(underlying_graph(ψ)) + bond_tensors = Dict() ψ_vidal = copy(ψ) for e in edges(ψ) vsrc, vdst = src(e), dst(e) pe = partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) - bond_tensors[e] = only(mts[pe]) + bond_tensors[e], bond_tensors[reverse(e)] = only(mts[pe]), only(mts[pe]) invroot_S = invsqrt_diag(map_diag(x -> x + regularization, bond_tensors[e])) setindex_preserve_graph!(ψ_vidal, noprime(invroot_S * ψ_vidal[vsrc]), vsrc) setindex_preserve_graph!(ψ_vidal, noprime(invroot_S * ψ_vidal[vdst]), vdst) @@ -194,16 +195,16 @@ end """Function to measure the 'isometries' of a state in the Vidal Gauge""" function vidal_itn_isometries( ψ::ITensorNetwork, - bond_tensors::DataGraph; + bond_tensors; edges=vcat(NamedGraphs.edges(ψ), reverse.(NamedGraphs.edges(ψ))), ) - isometries = DataGraph{vertextype(ψ),ITensor,ITensor}(directed_graph(underlying_graph(ψ))) + isometries = Dict() for e in edges vsrc, vdst = src(e), dst(e) ψv = copy(ψ[vsrc]) for vn in setdiff(neighbors(ψ, vsrc), [vdst]) - ψv = noprime(ψv * bond_tensors[vn => vsrc]) + ψv = noprime(ψv * bond_tensors[NamedEdge(vn => vsrc)]) end ψvdag = dag(ψv) @@ -215,19 +216,19 @@ function vidal_itn_isometries( end """Function to measure the 'canonicalness' of a state in the Vidal Gauge""" -function vidal_itn_canonicalness(ψ::ITensorNetwork, bond_tensors::DataGraph) +function vidal_itn_canonicalness(ψ::ITensorNetwork, bond_tensors) f = 0 isometries = vidal_itn_isometries(ψ, bond_tensors) - for e in edges(isometries) + for e in keys(isometries) LHS = isometries[e] / sum(diag(isometries[e])) id = dense(delta(inds(LHS))) id /= sum(diag(id)) f += 0.5 * norm(id - LHS) end - return f / (length(edges(isometries))) + return f / (length(keys(isometries))) end """Function to measure the 'canonicalness' of a state in the Symmetric Gauge""" diff --git a/test/test_apply.jl b/test/test_apply.jl index 4cdc6d8b..55bca9aa 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -1,10 +1,11 @@ using ITensorNetworks using ITensorNetworks: belief_propagation, - get_environment, + get_environment_tensors, contract_inner, message_tensors, vidal_gauge, + vidal_apply, vidal_to_symmetric_gauge, norm_network using Test @@ -29,16 +30,16 @@ using SplitApplyCombine ψψ = norm_network(ψ) #Simple Belief Propagation Grouping - pψψ_SBP = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ_SBP = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mtsSBP = belief_propagation(pψψ_SBP; contract_kwargs=(; alg="exact"), niters=50) - envsSBP = get_environment(pψψ_SBP, mtsSBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + envsSBP = get_environment_tensors(pψψ_SBP, mtsSBP, PartitionVertex.([v1, v2])) ψ_vidal, bond_tensors = vidal_gauge(ψ, pψψ_SBP, mtsSBP) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) - pψψ_GBP = PartitionedGraph(ψψ, collect(values(group(v -> v[1][1], vertices(ψψ))))) + pψψ_GBP = PartitionedGraph(ψψ, group(v -> v[1][1], vertices(ψψ))) mtsGBP = belief_propagation(pψψ_GBP; contract_kwargs=(; alg="exact"), niters=50) - envsGBP = get_environment(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + envsGBP = get_environment_tensors(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) ngates = 5 @@ -55,7 +56,9 @@ using SplitApplyCombine print_fidelity_loss=true, envisposdef=true, ) - ψOVidal, bond_tensors_t = apply(o, ψ_vidal, bond_tensors; maxdim=χ, normalize=true) + ψOVidal, bond_tensors_t = vidal_apply( + o, ψ_vidal, bond_tensors; maxdim=χ, normalize=true + ) ψOVidal_symm, _ = vidal_to_symmetric_gauge(ψOVidal, bond_tensors_t) ψOGBP = apply( o, diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index f2e25f4b..ffcd2430 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -10,12 +10,11 @@ using ITensorNetworks: using Test using Compat using ITensors -#ASSUME ONE CAN INSTALL THIS, MIGHT FAIL FOR WINDOWS -using Metis using LinearAlgebra using NamedGraphs using SplitApplyCombine using Random +using Metis ITensors.disable_warn_order() @@ -37,20 +36,18 @@ ITensors.disable_warn_order() Oψ[v] = apply(op("Sz", s[v]), ψ[v]) exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 - ) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) + mts = belief_propagation(pψψ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + pψψ, mts, [PartitionVertex(v)]; verts_tensors=[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))] ) - denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + denominator_tensors = approx_network_region(pψψ, mts, [PartitionVertex(v)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @test abs.(bp_sz - exact_sz) <= 1e-14 #Now test on a tree, should also be exact - g = named_comb_tree((6, 6)) + g = named_comb_tree((4, 4)) s = siteinds("S=1/2", g) χ = 2 Random.seed!(1564) @@ -64,14 +61,12 @@ ITensors.disable_warn_order() Oψ[v] = apply(op("Sz", s[v]), ψ[v]) exact_sz = contract_inner(Oψ, ψ) / contract_inner(ψ, ψ) - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation( - pψψ; contract_kwargs=(; alg="exact"), verbose=true, niters=10, target_precision=1e-3 - ) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) + mts = belief_propagation(pψψ) numerator_tensors = approx_network_region( - pψψ, mts, [(v, 1)]; verts_tensors=[apply(op("Sz", s[v]), ψ[v])] + pψψ, mts, [PartitionVertex(v)]; verts_tensors=[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))] ) - denominator_tensors = approx_network_region(pψψ, mts, [(v, 1)]) + denominator_tensors = approx_network_region(pψψ, mts, [PartitionVertex(v)]) bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] @test abs.(bp_sz - exact_sz) <= 1e-14 @@ -90,10 +85,8 @@ ITensors.disable_warn_order() ITensors.contract(ψOψ; sequence=contract_seq)[] / ITensors.contract(ψψ; sequence=contract_seq)[] - nsites = 2 - p_vertices = partitioned_vertices(underlying_graph(ψψ); nvertices_per_partition=nsites) - pψψ = PartitionedGraph(ψψ, p_vertices) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters=20) + pψψ = PartitionedGraph(ψψ; nvertices_per_partition=2, backend="Metis") + mts = belief_propagation(pψψ; niters=20) numerator_network = approx_network_region( pψψ, mts, vs; verts_tensors=ITensor[ψOψ[v] for v in vs] ) @@ -105,16 +98,16 @@ ITensors.disable_warn_order() @test abs.(bp_szsz - actual_szsz) <= 0.05 #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD - g_dims = (4, 4) + g_dims = (3, 3) g = named_grid(g_dims) s = siteinds("S=1/2", g) - vs = [(2, 2), (2, 3), (2, 4)] + vs = [(2, 2), (2, 3)] χ = 3 ψ = randomITensorNetwork(s; link_space=χ) ψψ = ψ ⊗ prime(dag(ψ); sites=[]) - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) - mts = belief_propagation(pψψ; contract_kwargs=(; alg="exact"), niters=20) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) + mts = belief_propagation(pψψ; niters=20) ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) rdm = ITensors.contract( @@ -134,12 +127,12 @@ ITensors.disable_warn_order() @test all(>=(0), real(eigs)) && all(==(0), imag(eigs)) #Test more advanced block BP with MPS message tensors on a grid - g_dims = (5, 4) + g_dims = (4, 3) g = named_grid(g_dims) s = siteinds("S=1/2", g) χ = 2 ψ = randomITensorNetwork(s; link_space=χ) - maxdim = 16 + maxdim = 8 v = (2, 2) ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) @@ -150,12 +143,11 @@ ITensors.disable_warn_order() combiners = linkinds_combiners(ψψ) ψψ = combine_linkinds(ψψ, combiners) ψOψ = combine_linkinds(ψOψ, combiners) - - pψψ = PartitionedGraph(ψψ, collect(values(group(v -> v[1], vertices(ψψ))))) + pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = belief_propagation( pψψ; contract_kwargs=(; - alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-16, maxdim + alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-12, maxdim ), ) diff --git a/test/test_examples/test_apply_bp.jl b/test/test_examples/test_apply_bp.jl deleted file mode 100644 index 5e9cabb8..00000000 --- a/test/test_examples/test_apply_bp.jl +++ /dev/null @@ -1,28 +0,0 @@ -using ITensorNetworks -using Suppressor -using Test - -include(joinpath(pkgdir(ITensorNetworks), "examples", "apply", "apply_bp", "apply_bp.jl")) - -@testset "Test apply_bp example" begin - opnames = ["Id", "RandomUnitary"] - graphs = (named_comb_tree, named_grid) - dims = (6, 6) - @testset "$opname, $graph" for opname in opnames, graph in graphs - ψ_bp, pψψ_bp, mts_bp, ψ_vidal, pψψ_vidal, mts_vidal = @suppress main(; - seed=1234, - opname, - graph, - dims, - χ=2, - nlayers=2, - variational_optimization_only=false, - regauge=false, - reduced=true, - ) - v = dims .÷ 2 - sz_bp = expect_bp("Sz", v, ψ_bp, pψψ_bp, mts_bp) - sz_vidal = expect_bp("Sz", v, ψ_vidal, pψψ_vidal, mts_vidal) - @test sz_bp ≈ sz_vidal rtol = 1e-5 - end -end diff --git a/test/test_examples/test_examples.jl b/test/test_examples/test_examples.jl index 124bffc6..b805db50 100644 --- a/test/test_examples/test_examples.jl +++ b/test/test_examples/test_examples.jl @@ -12,10 +12,7 @@ using Test "mps.jl", "peps.jl", "steiner_tree.jl", - joinpath("belief_propagation", "bpexample.jl"), - joinpath("belief_propagation", "bpsequences.jl"), joinpath("dynamics", "2d_ising_imag_tebd.jl"), - joinpath("dynamics", "heavy_hex_ising_real_tebd.jl"), joinpath("treetensornetworks", "comb_tree.jl"), joinpath("treetensornetworks", "spanning_tree.jl"), joinpath("treetensornetworks", "ttn_basics.jl"), diff --git a/test/test_examples/test_sqrt_bp.jl b/test/test_examples/test_sqrt_bp.jl deleted file mode 100644 index 127eda83..00000000 --- a/test/test_examples/test_sqrt_bp.jl +++ /dev/null @@ -1,10 +0,0 @@ -using ITensorNetworks -using Suppressor -using Test - -include(joinpath(pkgdir(ITensorNetworks), "examples", "belief_propagation", "sqrt_bp.jl")) - -@testset "Test sqrt_bp example" begin - (; sz_bp, sz_sqrt_bp) = main(; n=8, niters=10, β=0.28, h=0.5) - @test sz_bp ≈ sz_sqrt_bp -end diff --git a/test/test_gauging.jl b/test/test_gauging.jl index 04067b43..fa003896 100644 --- a/test/test_gauging.jl +++ b/test/test_gauging.jl @@ -34,9 +34,7 @@ using SplitApplyCombine sqrt(contract_inner(ψ_symm, ψ_symm) * contract_inner(ψ, ψ)) ≈ 1.0 ψψ_symm_V2 = ψ_symm ⊗ prime(dag(ψ_symm); sites=[]) - pψψ_symm_V2 = PartitionedGraph( - ψψ_symm_V2, collect(values(group(v -> v[1], vertices(ψψ_symm_V2)))) - ) + pψψ_symm_V2 = PartitionedGraph(ψψ_symm_V2, group(v -> v[1], vertices(ψψ_symm_V2))) ψ_symm_mts_V2 = belief_propagation( pψψ_symm_V2; contract_kwargs=(; alg="exact"), niters=50 ) diff --git a/test/test_indsnetwork.jl b/test/test_indsnetwork.jl index 29a5d858..1a9bc27f 100644 --- a/test/test_indsnetwork.jl +++ b/test/test_indsnetwork.jl @@ -1,7 +1,6 @@ using Dictionaries using ITensors using ITensorNetworks -using ITensorNetworks: dim using Random using Test From 504085c31b5d69ac9e08f6009dca7e7044d35f32 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 23 Jan 2024 15:56:29 -0500 Subject: [PATCH 09/12] Removed approx_network_region --- src/beliefpropagation/beliefpropagation.jl | 32 ++------------ test/test_apply.jl | 6 +-- test/test_belief_propagation.jl | 51 +++++++++------------- 3 files changed, 26 insertions(+), 63 deletions(-) diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index cfbafdf6..a7f2f9a6 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -142,7 +142,7 @@ end """ Given a subet of partitionvertices of a ptn get the incoming message tensors to that region """ -function get_environment_tensors(ptn::PartitionedGraph, mts, verts::Vector) +function environment_tensors(ptn::PartitionedGraph, mts, verts::Vector) partition_verts = partitionvertices(ptn, verts) central_verts = vertices(ptn, partition_verts) @@ -156,34 +156,8 @@ function get_environment_tensors(ptn::PartitionedGraph, mts, verts::Vector) return vcat(env_tensors, central_tensors) end -function get_environment_tensors( +function environment_tensors( ptn::PartitionedGraph, mts, partition_verts::Vector{<:PartitionVertex} ) - return get_environment_tensors(ptn, mts, vertices(ptn, partition_verts)) -end - -""" -Calculate the contraction of a tensor network centred on the vertices verts. Using message tensors. -Defaults to using tn[verts] as the local network but can be overriden -""" -function approx_network_region( - ptn::PartitionedGraph, - mts, - verts::Vector; - verts_tensors=ITensor[(unpartitioned_graph(ptn))[v] for v in verts], -) - environment_tensors = get_environment_tensors(ptn, mts, verts) - - return vcat(environment_tensors, verts_tensors) -end - -function approx_network_region( - ptn::PartitionedGraph, - mts, - partition_verts::Vector{<:PartitionVertex}; - verts_tensors=ITensor[ - (unpartitioned_graph(ptn))[v] for v in vertices(ptn, partition_verts) - ], -) - return approx_network_region(ptn, mts, vertices(ptn, partition_verts); verts_tensors) + return environment_tensors(ptn, mts, vertices(ptn, partition_verts)) end diff --git a/test/test_apply.jl b/test/test_apply.jl index 55bca9aa..51587cac 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -1,7 +1,7 @@ using ITensorNetworks using ITensorNetworks: belief_propagation, - get_environment_tensors, + environment_tensors, contract_inner, message_tensors, vidal_gauge, @@ -32,14 +32,14 @@ using SplitApplyCombine #Simple Belief Propagation Grouping pψψ_SBP = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mtsSBP = belief_propagation(pψψ_SBP; contract_kwargs=(; alg="exact"), niters=50) - envsSBP = get_environment_tensors(pψψ_SBP, mtsSBP, PartitionVertex.([v1, v2])) + envsSBP = environment_tensors(pψψ_SBP, mtsSBP, PartitionVertex.([v1, v2])) ψ_vidal, bond_tensors = vidal_gauge(ψ, pψψ_SBP, mtsSBP) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) pψψ_GBP = PartitionedGraph(ψψ, group(v -> v[1][1], vertices(ψψ))) mtsGBP = belief_propagation(pψψ_GBP; contract_kwargs=(; alg="exact"), niters=50) - envsGBP = get_environment_tensors(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) + envsGBP = environment_tensors(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) ngates = 5 diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index ffcd2430..a6976807 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -2,11 +2,11 @@ using ITensorNetworks using ITensorNetworks: ising_network, belief_propagation, - approx_network_region, split_index, contract_inner, contract_boundary_mps, - message_tensors + message_tensors, + environment_tensors using Test using Compat using ITensors @@ -38,13 +38,11 @@ ITensors.disable_warn_order() pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = belief_propagation(pψψ) - numerator_tensors = approx_network_region( - pψψ, mts, [PartitionVertex(v)]; verts_tensors=[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))] - ) - denominator_tensors = approx_network_region(pψψ, mts, [PartitionVertex(v)]) - bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] + env_tensors = environment_tensors(pψψ, mts, [PartitionVertex(v)]) + numerator = contract(vcat(env_tensors, ITensor[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))]))[] + denominator = contract(vcat(env_tensors, ITensor[ψ[v], op("I", s[v]), dag(prime(ψ[v]))]))[] - @test abs.(bp_sz - exact_sz) <= 1e-14 + @test abs.((numerator / denominator) - exact_sz) <= 1e-14 #Now test on a tree, should also be exact g = named_comb_tree((4, 4)) @@ -63,13 +61,11 @@ ITensors.disable_warn_order() pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) mts = belief_propagation(pψψ) - numerator_tensors = approx_network_region( - pψψ, mts, [PartitionVertex(v)]; verts_tensors=[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))] - ) - denominator_tensors = approx_network_region(pψψ, mts, [PartitionVertex(v)]) - bp_sz = contract(numerator_tensors)[] / contract(denominator_tensors)[] + env_tensors = environment_tensors(pψψ, mts, [PartitionVertex(v)]) + numerator = contract(vcat(env_tensors, ITensor[ψ[v], op("Sz", s[v]), dag(prime(ψ[v]))]))[] + denominator = contract(vcat(env_tensors, ITensor[ψ[v], op("I", s[v]), dag(prime(ψ[v]))]))[] - @test abs.(bp_sz - exact_sz) <= 1e-14 + @test abs.((numerator / denominator) - exact_sz) <= 1e-14 #Now test two-site expec taking on the partition function of the Ising model. Not exact, but close g_dims = (3, 4) @@ -87,15 +83,12 @@ ITensors.disable_warn_order() pψψ = PartitionedGraph(ψψ; nvertices_per_partition=2, backend="Metis") mts = belief_propagation(pψψ; niters=20) - numerator_network = approx_network_region( - pψψ, mts, vs; verts_tensors=ITensor[ψOψ[v] for v in vs] - ) - denominator_network = approx_network_region(pψψ, mts, vs) - bp_szsz = - ITensors.contract(numerator_network)[] / ITensors.contract(denominator_network)[] + env_tensors = environment_tensors(pψψ, mts, vs) + numerator = contract(vcat(env_tensors, ITensor[ψOψ[v] for v in vs]))[] + denominator = contract(vcat(env_tensors, ITensor[ψψ[v] for v in vs]))[] - @test abs.(bp_szsz - actual_szsz) <= 0.05 + @test abs.((numerator / denominator) - actual_szsz) <= 0.05 #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD g_dims = (3, 3) @@ -110,13 +103,9 @@ ITensors.disable_warn_order() mts = belief_propagation(pψψ; niters=20) ψψsplit = split_index(ψψ, NamedEdge.([(v, 1) => (v, 2) for v in vs])) + env_tensors = environment_tensors(pψψ, mts, [(v, 2) for v in vs]) rdm = ITensors.contract( - approx_network_region( - pψψ, - mts, - [(v, 2) for v in vs]; - verts_tensors=ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]], - ), + 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)...)) @@ -151,12 +140,12 @@ ITensors.disable_warn_order() ), ) - numerator_tensors = approx_network_region(pψψ, mts, [v]; verts_tensors=ITensor[ψOψ[v]]) - denominator_tensors = approx_network_region(pψψ, mts, [v]) - bp_sz = ITensors.contract(numerator_tensors)[] / ITensors.contract(denominator_tensors)[] + env_tensors = environment_tensors(pψψ, mts, [v]) + numerator = contract(vcat(env_tensors, ITensor[ψOψ[v]]))[] + denominator = contract(vcat(env_tensors, ITensor[ψψ[v]]))[] exact_sz = contract_boundary_mps(ψOψ; cutoff=1e-16) / contract_boundary_mps(ψψ; cutoff=1e-16) - @test abs.(bp_sz - exact_sz) <= 1e-5 + @test abs.((numerator / denominator) - exact_sz) <= 1e-5 end From c32edf514d9da2c61b63858d1c3480a703228554 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 24 Jan 2024 13:46:09 -0500 Subject: [PATCH 10/12] Added callback functions for building message tensors and contracting them --- examples/boundary.jl | 3 +- examples/distances.jl | 2 +- src/ITensorNetworks.jl | 3 +- src/apply.jl | 16 ++-- src/beliefpropagation/beliefpropagation.jl | 89 ++++++++++++---------- src/exports.jl | 1 + src/gauging.jl | 31 +++----- test/test_apply.jl | 5 +- test/test_belief_propagation.jl | 11 +-- test/test_gauging.jl | 8 +- 10 files changed, 79 insertions(+), 90 deletions(-) diff --git a/examples/boundary.jl b/examples/boundary.jl index 6305744d..7c43712a 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -8,8 +8,7 @@ tn = ITensorNetwork(named_grid((6, 3)); link_space=4) @visualize tn -pvertices = partitioned_vertices(underlying_graph(tn); nvertices_per_partition=2) -ptn = PartitionedGraph(tn, pvertices) +ptn = PartitionedGraph(tn; nvertices_per_partition=2) sub_vs_1 = vertices(ptn, PartitionVertex(1)) sub_vs_2 = vertices(ptn, PartitionVertex(2)) diff --git a/examples/distances.jl b/examples/distances.jl index 8242889c..7767651f 100644 --- a/examples/distances.jl +++ b/examples/distances.jl @@ -14,4 +14,4 @@ t = dijkstra_tree(ψ, only(center(ψ))) @show a_star(ψ, (2, 1), (2, 5)) @show mincut_partitions(ψ) @show mincut_partitions(ψ, (1, 1), (3, 5)) -@show partitioned_vertices(underlying_graph(ψ); npartitions=2) +@show partitioned_vertices(ψ; npartitions=2) diff --git a/src/ITensorNetworks.jl b/src/ITensorNetworks.jl index b457864f..24fea311 100644 --- a/src/ITensorNetworks.jl +++ b/src/ITensorNetworks.jl @@ -53,8 +53,7 @@ using NamedGraphs: parent_graph, vertex_to_parent_vertex, parent_vertices_to_vertices, - not_implemented, - parent + not_implemented include("imports.jl") diff --git a/src/apply.jl b/src/apply.jl index 0923229a..c7745973 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -300,7 +300,7 @@ Gate does not necessarily need to be passed. Can supply an edge to do an identit function vidal_apply( o::Union{ITensor,NamedEdge}, ψ::AbstractITensorNetwork, - bond_tensors; + bond_tensors::DataGraph; normalize=false, apply_kwargs..., ) @@ -314,13 +314,13 @@ function vidal_apply( for vn in neighbors(ψ, src(e)) if (vn != dst(e)) - ψv1 = noprime(ψv1 * bond_tensors[NamedEdge(vn => src(e))]) + ψv1 = noprime(ψv1 * bond_tensors[vn => src(e)]) end end for vn in neighbors(ψ, dst(e)) if (vn != src(e)) - ψv2 = noprime(ψv2 * bond_tensors[NamedEdge(vn => dst(e))]) + ψv2 = noprime(ψv2 * bond_tensors[vn => dst(e)]) end end @@ -336,20 +336,20 @@ function vidal_apply( ind_to_replace = commonind(V, S) ind_to_replace_with = commonind(U, S) - replaceind!(S, ind_to_replace, ind_to_replace_with') - replaceind!(V, ind_to_replace, ind_to_replace_with) + S = replaceind(S, ind_to_replace => ind_to_replace_with') + V = replaceind(V, ind_to_replace => ind_to_replace_with) - ψv1, bond_tensors[e], bond_tensors[reverse(e)], ψv2 = U * Qᵥ₁, S, S, V * Qᵥ₂ + ψv1, bond_tensors[e], ψv2 = U * Qᵥ₁, S, V * Qᵥ₂ for vn in neighbors(ψ, src(e)) if (vn != dst(e)) - ψv1 = noprime(ψv1 * inv_diag(bond_tensors[NamedEdge(vn => src(e))])) + ψv1 = noprime(ψv1 * inv_diag(bond_tensors[vn => src(e)])) end end for vn in neighbors(ψ, dst(e)) if (vn != src(e)) - ψv2 = noprime(ψv2 * inv_diag(bond_tensors[NamedEdge(vn => dst(e))])) + ψv2 = noprime(ψv2 * inv_diag(bond_tensors[vn => dst(e)])) end end diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index a7f2f9a6..33b9b33d 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -1,28 +1,51 @@ -function message_tensors( - ptn::PartitionedGraph; itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))] -) - mts = Dict() - for e in partitionedges(ptn) - src_e_itn = subgraph(ptn, src(e)) - dst_e_itn = subgraph(ptn, dst(e)) - inds_e = commoninds(src_e_itn, dst_e_itn) - mts[e] = itensor_constructor(inds_e) - mts[reverse(e)] = dag.(mts[e]) - end +default_mt_constructor(inds_e) = ITensor[denseblocks(delta(inds_e))] +default_bp_cache(ptn::PartitionedGraph) = Dict() +function default_contractor(contract_list::Vector{ITensor}) + return ITensor[normalize!( + contract(contract_list; sequence=contraction_sequence(contract_list; alg="optimal")) + )] +end + +function contract_to_MPS(contract_list::Vector{ITensor}; svd_kwargs...) + contract_kwargs = (; + alg="density_matrix", + output_structure=path_graph_structure, + contraction_sequence_alg="optimal", + svd_kwargs..., + ) + mts = ITensor(first(contract(ITensorNetwork(contract_list); contract_kwargs...))) + mts = normalize!.(mts) return mts end +function message_tensor( + ptn::PartitionedGraph, edge::PartitionEdge; mt_constructor=default_mt_constructor +) + src_e_itn = subgraph(ptn, src(edge)) + dst_e_itn = subgraph(ptn, dst(edge)) + inds_e = commoninds(src_e_itn, dst_e_itn) + return mt_constructor(inds_e) +end + """ Do a single update of a message tensor using the current subgraph and the incoming mts """ function update_message_tensor( - ptn::PartitionedGraph, edge::PartitionEdge, mts; contract_kwargs=(; alg="exact") + ptn::PartitionedGraph, + edge::PartitionEdge, + mts; + contractor=default_contractor, + mt_constructor=default_mt_constructor, ) pedges = setdiff( partitionedges(ptn, boundary_edges(ptn, vertices(ptn, src(edge)); dir=:in)), [reverse(edge)], ) - incoming_messages = [mts[e_in] for e_in in pedges] + + incoming_messages = [ + e_in ∈ keys(mts) ? mts[e_in] : message_tensor(ptn, e_in; mt_constructor) for + e_in in pedges + ] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) contract_list = ITensor[ @@ -30,18 +53,7 @@ function update_message_tensor( ITensor(subgraph(ptn, src(edge))) ] - if contract_kwargs.alg != "exact" - mt = first(contract(ITensorNetwork(contract_list); contract_kwargs...)) - else - mt = contract( - contract_list; sequence=contraction_sequence(contract_list; alg="optimal") - ) - end - - mt = isa(mt, ITensor) ? ITensor[mt] : ITensor(mt) - normalize!.(mt) - - return mt + return contractor(contract_list) end """ @@ -51,16 +63,17 @@ function belief_propagation_iteration( ptn::PartitionedGraph, mts, edges::Vector{<:PartitionEdge}; - contract_kwargs=(; alg="exact"), + contractor=default_contractor, compute_norm=false, ) new_mts = copy(mts) c = 0 for e in edges - new_mts[e] = update_message_tensor(ptn, e, new_mts; contract_kwargs) + new_mts[e] = update_message_tensor(ptn, e, new_mts; contractor) if compute_norm - LHS, RHS = ITensors.contract(mts[e]), ITensors.contract(new_mts[e]) + LHS = e ∈ keys(mts) ? contract(mts[e]) : contract(message_tensor(ptn, e)) + RHS = contract(new_mts[e]) #This line only makes sense if the message tensors are rank 2??? Should fix this. LHS /= sum(diag(LHS)) RHS /= sum(diag(RHS)) @@ -79,14 +92,14 @@ function belief_propagation_iteration( ptn::PartitionedGraph, mts, edge_groups::Vector{<:Vector{<:PartitionEdge}}; - contract_kwargs=(; alg="exact"), + contractor=default_contractor, compute_norm=false, ) new_mts = copy(mts) c = 0 for edges in edge_groups updated_mts, ct = belief_propagation_iteration( - ptn, mts, edges; contract_kwargs, compute_norm + ptn, mts, edges; contractor, compute_norm ) for e in edges new_mts[e] = updated_mts[e] @@ -99,17 +112,17 @@ end function belief_propagation_iteration( ptn::PartitionedGraph, mts; - contract_kwargs=(; alg="exact"), + contractor=default_contractor, compute_norm=false, edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), ) - return belief_propagation_iteration(ptn, mts, edges; contract_kwargs, compute_norm) + return belief_propagation_iteration(ptn, mts, edges; contractor, compute_norm) end function belief_propagation( ptn::PartitionedGraph, mts; - contract_kwargs=(; alg="exact"), + contractor=default_contractor, niters=default_bp_niters(partitioned_graph(ptn)), target_precision=nothing, edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), @@ -120,7 +133,7 @@ function belief_propagation( error("You need to specify a number of iterations for BP!") end for i in 1:niters - mts, c = belief_propagation_iteration(ptn, mts, edges; contract_kwargs, compute_norm) + mts, c = belief_propagation_iteration(ptn, mts, edges; contractor, compute_norm) if compute_norm && c <= target_precision if verbose println("BP converged to desired precision after $i iterations.") @@ -131,12 +144,8 @@ function belief_propagation( return mts end -function belief_propagation( - ptn::PartitionedGraph; - itensor_constructor=inds_e -> ITensor[dense(delta(inds_e))], - kwargs..., -) - mts = message_tensors(ptn; itensor_constructor) +function belief_propagation(ptn::PartitionedGraph; bp_cache=default_bp_cache, kwargs...) + mts = bp_cache(ptn) return belief_propagation(ptn, mts; kwargs...) end """ diff --git a/src/exports.jl b/src/exports.jl index f298994d..97194466 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -51,6 +51,7 @@ export AbstractITensorNetwork, itensors, reverse_bfs_edges, data_graph, + default_bp_cache, flatten_networks, inner_network, norm_network, diff --git a/src/gauging.jl b/src/gauging.jl index 4b36badf..ca8c13f9 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -1,11 +1,10 @@ """initialize bond tensors of an ITN to identity matrices""" function initialize_bond_tensors(ψ::ITensorNetwork; index_map=prime) - bond_tensors = Dict() + bond_tensors = DataGraph{vertextype(ψ),Nothing,ITensor}(underlying_graph(ψ)) for e in edges(ψ) index = commoninds(ψ[src(e)], ψ[dst(e)]) - bond_tensors[e] = dense(delta(index, index_map(index))) - bond_tensors[reverse(e)] = bond_tensors[e] + bond_tensors[e] = denseblocks(delta(index, index_map(index))) end return bond_tensors @@ -16,7 +15,7 @@ function vidal_gauge( ψ::ITensorNetwork, pψψ::PartitionedGraph, mts, - bond_tensors; + bond_tensors::DataGraph; eigen_message_tensor_cutoff=10 * eps(real(scalartype(ψ))), regularization=10 * eps(real(scalartype(ψ))), edges=NamedGraphs.edges(ψ), @@ -68,7 +67,7 @@ function vidal_gauge( [commoninds(S, U)..., commoninds(S, V)...] => [new_edge_ind..., prime(new_edge_ind)...], ) - bond_tensors[e], bond_tensors[reverse(e)] = S, S + bond_tensors[e] = S end return ψ_vidal, bond_tensors @@ -109,27 +108,19 @@ function vidal_gauge( ) ψψ = norm_network(ψ) pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mts = message_tensors(pψψ) - mts = belief_propagation( - pψψ, - mts; - contract_kwargs=(; alg="exact"), - niters, - target_precision=target_canonicalness, - verbose, - ) + mts = belief_propagation(pψψ; niters, target_precision=target_canonicalness, verbose) return vidal_gauge( ψ, pψψ, mts; eigen_message_tensor_cutoff, regularization, svd_kwargs... ) end """Transform from an ITensor in the Vidal Gauge (bond tensors) to the Symmetric Gauge (partitionedgraph, message tensors)""" -function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors) +function vidal_to_symmetric_gauge(ψ::ITensorNetwork, bond_tensors::DataGraph) ψsymm = copy(ψ) ψψsymm = norm_network(ψsymm) pψψsymm = PartitionedGraph(ψψsymm, group(v -> v[1], vertices(ψψsymm))) - ψsymm_mts = message_tensors(pψψsymm) + ψsymm_mts = default_bp_cache(pψψsymm) for e in edges(ψsymm) vsrc, vdst = src(e), dst(e) @@ -176,7 +167,7 @@ function symmetric_to_vidal_gauge( mts; regularization=10 * eps(real(scalartype(ψ))), ) - bond_tensors = Dict() + bond_tensors = DataGraph{vertextype(ψ),Nothing,ITensor}(underlying_graph(ψ)) ψ_vidal = copy(ψ) @@ -195,7 +186,7 @@ end """Function to measure the 'isometries' of a state in the Vidal Gauge""" function vidal_itn_isometries( ψ::ITensorNetwork, - bond_tensors; + bond_tensors::DataGraph; edges=vcat(NamedGraphs.edges(ψ), reverse.(NamedGraphs.edges(ψ))), ) isometries = Dict() @@ -204,7 +195,7 @@ function vidal_itn_isometries( vsrc, vdst = src(e), dst(e) ψv = copy(ψ[vsrc]) for vn in setdiff(neighbors(ψ, vsrc), [vdst]) - ψv = noprime(ψv * bond_tensors[NamedEdge(vn => vsrc)]) + ψv = noprime(ψv * bond_tensors[vn => vsrc]) end ψvdag = dag(ψv) @@ -216,7 +207,7 @@ function vidal_itn_isometries( end """Function to measure the 'canonicalness' of a state in the Vidal Gauge""" -function vidal_itn_canonicalness(ψ::ITensorNetwork, bond_tensors) +function vidal_itn_canonicalness(ψ::ITensorNetwork, bond_tensors::DataGraph) f = 0 isometries = vidal_itn_isometries(ψ, bond_tensors) diff --git a/test/test_apply.jl b/test/test_apply.jl index 51587cac..2df2f491 100644 --- a/test/test_apply.jl +++ b/test/test_apply.jl @@ -3,7 +3,6 @@ using ITensorNetworks: belief_propagation, environment_tensors, contract_inner, - message_tensors, vidal_gauge, vidal_apply, vidal_to_symmetric_gauge, @@ -31,14 +30,14 @@ using SplitApplyCombine #Simple Belief Propagation Grouping pψψ_SBP = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mtsSBP = belief_propagation(pψψ_SBP; contract_kwargs=(; alg="exact"), niters=50) + mtsSBP = belief_propagation(pψψ_SBP; niters=20) envsSBP = environment_tensors(pψψ_SBP, mtsSBP, PartitionVertex.([v1, v2])) ψ_vidal, bond_tensors = vidal_gauge(ψ, pψψ_SBP, mtsSBP) #This grouping will correspond to calculating the environments exactly (each column of the grid is a partition) pψψ_GBP = PartitionedGraph(ψψ, group(v -> v[1][1], vertices(ψψ))) - mtsGBP = belief_propagation(pψψ_GBP; contract_kwargs=(; alg="exact"), niters=50) + mtsGBP = belief_propagation(pψψ_GBP; niters=20) envsGBP = environment_tensors(pψψ_GBP, mtsGBP, [(v1, 1), (v1, 2), (v2, 1), (v2, 2)]) ngates = 5 diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index a6976807..1ffc089e 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -5,7 +5,6 @@ using ITensorNetworks: split_index, contract_inner, contract_boundary_mps, - message_tensors, environment_tensors using Test using Compat @@ -121,7 +120,6 @@ ITensors.disable_warn_order() s = siteinds("S=1/2", g) χ = 2 ψ = randomITensorNetwork(s; link_space=χ) - maxdim = 8 v = (2, 2) ψψ = flatten_networks(ψ, dag(ψ); combine_linkinds=false, map_bra_linkinds=prime) @@ -133,12 +131,9 @@ ITensors.disable_warn_order() ψψ = combine_linkinds(ψψ, combiners) ψOψ = combine_linkinds(ψOψ, combiners) pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - mts = belief_propagation( - pψψ; - contract_kwargs=(; - alg="density_matrix", output_structure=path_graph_structure, cutoff=1e-12, maxdim - ), - ) + my_contract_to_MPS(args...) = + ITensorNetworks.contract_to_MPS(args...; cutoff=1e-6, maxdim=4) + mts = belief_propagation(pψψ; contractor=my_contract_to_MPS) env_tensors = environment_tensors(pψψ, mts, [v]) numerator = contract(vcat(env_tensors, ITensor[ψOψ[v]]))[] diff --git a/test/test_gauging.jl b/test/test_gauging.jl index fa003896..6441b766 100644 --- a/test/test_gauging.jl +++ b/test/test_gauging.jl @@ -5,11 +5,9 @@ using ITensorNetworks: contract_inner, symmetric_gauge, symmetric_to_vidal_gauge, - message_tensors, vidal_itn_canonicalness, vidal_gauge, - symmetric_itn_canonicalness, - belief_propagation_iteration + symmetric_itn_canonicalness using NamedGraphs using Test using Compat @@ -35,9 +33,7 @@ using SplitApplyCombine ψψ_symm_V2 = ψ_symm ⊗ prime(dag(ψ_symm); sites=[]) pψψ_symm_V2 = PartitionedGraph(ψψ_symm_V2, group(v -> v[1], vertices(ψψ_symm_V2))) - ψ_symm_mts_V2 = belief_propagation( - pψψ_symm_V2; contract_kwargs=(; alg="exact"), niters=50 - ) + ψ_symm_mts_V2 = belief_propagation(pψψ_symm_V2; niters=50) for m_e in values(ψ_symm_mts_V2) #Test all message tensors are approximately diagonal From 0e7ded22581b6aad948df8b87ff6774fd5393d20 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 24 Jan 2024 14:04:24 -0500 Subject: [PATCH 11/12] Rename ITensor(itn) -> Vector{ITensor}(itn) --- src/beliefpropagation/beliefpropagation.jl | 6 ++++-- src/itensornetwork.jl | 9 ++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index 33b9b33d..f5e2171f 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -13,7 +13,9 @@ function contract_to_MPS(contract_list::Vector{ITensor}; svd_kwargs...) contraction_sequence_alg="optimal", svd_kwargs..., ) - mts = ITensor(first(contract(ITensorNetwork(contract_list); contract_kwargs...))) + + tn, _ = contract(ITensorNetwork(contract_list); contract_kwargs...) + mts = Vector{ITensor}(tn) mts = normalize!.(mts) return mts end @@ -50,7 +52,7 @@ function update_message_tensor( contract_list = ITensor[ incoming_messages - ITensor(subgraph(ptn, src(edge))) + Vector{ITensor}(subgraph(ptn, src(edge))) ] return contractor(contract_list) diff --git a/src/itensornetwork.jl b/src/itensornetwork.jl index 0b8178c3..6c3e346d 100644 --- a/src/itensornetwork.jl +++ b/src/itensornetwork.jl @@ -264,11 +264,6 @@ end ITensorNetwork(itns::Vector{ITensorNetwork}) = reduce(⊗, itns) -function ITensors.ITensor(ψ::ITensorNetwork) - vs = vertices(ψ) - if length(vs) == 1 - return ψ[first(vs)] - else - return ITensor[ψ[v] for v in vertices(ψ)] - end +function Vector{ITensor}(ψ::ITensorNetwork) + return ITensor[ψ[v] for v in vertices(ψ)] end From 48c6d8eeee9e9dae2aba09ca1536562dfb3a69c1 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 26 Jan 2024 09:52:17 -0500 Subject: [PATCH 12/12] Dispatching message tensor update based on user provided functions --- src/beliefpropagation/beliefpropagation.jl | 63 +++++-------------- .../beliefpropagation_schedule.jl | 3 + src/contract.jl | 27 ++++++++ src/gauging.jl | 2 +- test/test_belief_propagation.jl | 14 +++-- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/beliefpropagation/beliefpropagation.jl b/src/beliefpropagation/beliefpropagation.jl index f5e2171f..3b16e3b3 100644 --- a/src/beliefpropagation/beliefpropagation.jl +++ b/src/beliefpropagation/beliefpropagation.jl @@ -1,24 +1,9 @@ default_mt_constructor(inds_e) = ITensor[denseblocks(delta(inds_e))] default_bp_cache(ptn::PartitionedGraph) = Dict() -function default_contractor(contract_list::Vector{ITensor}) - return ITensor[normalize!( - contract(contract_list; sequence=contraction_sequence(contract_list; alg="optimal")) - )] -end - -function contract_to_MPS(contract_list::Vector{ITensor}; svd_kwargs...) - contract_kwargs = (; - alg="density_matrix", - output_structure=path_graph_structure, - contraction_sequence_alg="optimal", - svd_kwargs..., - ) - - tn, _ = contract(ITensorNetwork(contract_list); contract_kwargs...) - mts = Vector{ITensor}(tn) - mts = normalize!.(mts) - return mts +function default_contractor(contract_list::Vector{ITensor}; kwargs...) + return contract_exact(contract_list; kwargs...) end +default_contractor_kwargs() = (; normalize=true, contraction_sequence_alg="optimal") function message_tensor( ptn::PartitionedGraph, edge::PartitionEdge; mt_constructor=default_mt_constructor @@ -37,16 +22,14 @@ function update_message_tensor( edge::PartitionEdge, mts; contractor=default_contractor, + contractor_kwargs=default_contractor_kwargs(), mt_constructor=default_mt_constructor, ) - pedges = setdiff( - partitionedges(ptn, boundary_edges(ptn, vertices(ptn, src(edge)); dir=:in)), - [reverse(edge)], - ) + pb_edges = partitionedges(ptn, boundary_edges(ptn, vertices(ptn, src(edge)); dir=:in)) incoming_messages = [ e_in ∈ keys(mts) ? mts[e_in] : message_tensor(ptn, e_in; mt_constructor) for - e_in in pedges + e_in in setdiff(pb_edges, [reverse(edge)]) ] incoming_messages = reduce(vcat, incoming_messages; init=ITensor[]) @@ -55,23 +38,19 @@ function update_message_tensor( Vector{ITensor}(subgraph(ptn, src(edge))) ] - return contractor(contract_list) + return contractor(contract_list; contractor_kwargs...) end """ Do a sequential update of message tensors on `edges` for a given ITensornetwork and its partition into sub graphs """ function belief_propagation_iteration( - ptn::PartitionedGraph, - mts, - edges::Vector{<:PartitionEdge}; - contractor=default_contractor, - compute_norm=false, + ptn::PartitionedGraph, mts, edges::Vector{<:PartitionEdge}; compute_norm=false, kwargs... ) new_mts = copy(mts) c = 0 for e in edges - new_mts[e] = update_message_tensor(ptn, e, new_mts; contractor) + new_mts[e] = update_message_tensor(ptn, e, new_mts; kwargs...) if compute_norm LHS = e ∈ keys(mts) ? contract(mts[e]) : contract(message_tensor(ptn, e)) @@ -91,18 +70,12 @@ Currently we send the full message tensor data struct to belief_propagation_iter mts relevant to that subgraph. """ function belief_propagation_iteration( - ptn::PartitionedGraph, - mts, - edge_groups::Vector{<:Vector{<:PartitionEdge}}; - contractor=default_contractor, - compute_norm=false, + ptn::PartitionedGraph, mts, edge_groups::Vector{<:Vector{<:PartitionEdge}}; kwargs... ) new_mts = copy(mts) c = 0 for edges in edge_groups - updated_mts, ct = belief_propagation_iteration( - ptn, mts, edges; contractor, compute_norm - ) + updated_mts, ct = belief_propagation_iteration(ptn, mts, edges; kwargs...) for e in edges new_mts[e] = updated_mts[e] end @@ -112,30 +85,26 @@ function belief_propagation_iteration( end function belief_propagation_iteration( - ptn::PartitionedGraph, - mts; - contractor=default_contractor, - compute_norm=false, - edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), + ptn::PartitionedGraph, mts; edges=default_edge_sequence(ptn), kwargs... ) - return belief_propagation_iteration(ptn, mts, edges; contractor, compute_norm) + return belief_propagation_iteration(ptn, mts, edges; kwargs...) end function belief_propagation( ptn::PartitionedGraph, mts; - contractor=default_contractor, niters=default_bp_niters(partitioned_graph(ptn)), target_precision=nothing, - edges=PartitionEdge.(edge_sequence(partitioned_graph(ptn))), + edges=default_edge_sequence(ptn), verbose=false, + kwargs..., ) compute_norm = !isnothing(target_precision) if isnothing(niters) error("You need to specify a number of iterations for BP!") end for i in 1:niters - mts, c = belief_propagation_iteration(ptn, mts, edges; contractor, compute_norm) + mts, c = belief_propagation_iteration(ptn, mts, edges; compute_norm, kwargs...) if compute_norm && c <= target_precision if verbose println("BP converged to desired precision after $i iterations.") diff --git a/src/beliefpropagation/beliefpropagation_schedule.jl b/src/beliefpropagation/beliefpropagation_schedule.jl index cbc5f206..a56814cd 100644 --- a/src/beliefpropagation/beliefpropagation_schedule.jl +++ b/src/beliefpropagation/beliefpropagation_schedule.jl @@ -1,4 +1,7 @@ default_edge_sequence_alg() = "forest_cover" +function default_edge_sequence(pg::PartitionedGraph) + return PartitionEdge.(edge_sequence(partitioned_graph(pg))) +end @traitfn default_bp_niters(g::::(!IsDirected)) = is_tree(g) ? 1 : nothing @traitfn function default_bp_niters(g::::IsDirected) diff --git a/src/contract.jl b/src/contract.jl index 97265d77..77c4ee1a 100644 --- a/src/contract.jl +++ b/src/contract.jl @@ -17,3 +17,30 @@ function contract( ) return approx_itensornetwork(alg, tn, output_structure; kwargs...) end + +function contract_density_matrix( + contract_list::Vector{ITensor}; normalize=true, contractor_kwargs... +) + tn, _ = contract( + ITensorNetwork(contract_list); alg="density_matrix", contractor_kwargs... + ) + out = Vector{ITensor}(tn) + if normalize + out .= normalize!.(copy.(out)) + end + return out +end + +function contract_exact( + contract_list::Vector{ITensor}; + contraction_sequence_alg="optimal", + normalize=true, + contractor_kwargs..., +) + seq = contraction_sequence(contract_list; alg=contraction_sequence_alg) + out = ITensors.contract(contract_list; sequence=seq, contractor_kwargs...) + if normalize + normalize!(out) + end + return ITensor[out] +end diff --git a/src/gauging.jl b/src/gauging.jl index ca8c13f9..c6d3c3f6 100644 --- a/src/gauging.jl +++ b/src/gauging.jl @@ -27,7 +27,7 @@ function vidal_gauge( vsrc, vdst = src(e), dst(e) ψvsrc, ψvdst = ψ_vidal[vsrc], ψ_vidal[vdst] - pe = partitionedge(pψψ, NamedEdge((vsrc, 1) => (vdst, 1))) + pe = partitionedge(pψψ, (vsrc, 1) => (vdst, 1)) edge_ind = commoninds(ψvsrc, ψvdst) edge_ind_sim = sim(edge_ind) diff --git a/test/test_belief_propagation.jl b/test/test_belief_propagation.jl index 1ffc089e..cab105b9 100644 --- a/test/test_belief_propagation.jl +++ b/test/test_belief_propagation.jl @@ -66,7 +66,7 @@ ITensors.disable_warn_order() @test abs.((numerator / denominator) - exact_sz) <= 1e-14 - #Now test two-site expec taking on the partition function of the Ising model. Not exact, but close + # #Now test two-site expec taking on the partition function of the Ising model. Not exact, but close g_dims = (3, 4) g = named_grid(g_dims) s = IndsNetwork(g; link_space=2) @@ -89,7 +89,7 @@ ITensors.disable_warn_order() @test abs.((numerator / denominator) - actual_szsz) <= 0.05 - #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD + # #Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD g_dims = (3, 3) g = named_grid(g_dims) s = siteinds("S=1/2", g) @@ -114,7 +114,7 @@ ITensors.disable_warn_order() @test size(rdm) == (2^length(vs), 2^length(vs)) @test all(>=(0), real(eigs)) && all(==(0), imag(eigs)) - #Test more advanced block BP with MPS message tensors on a grid + # #Test more advanced block BP with MPS message tensors on a grid g_dims = (4, 3) g = named_grid(g_dims) s = siteinds("S=1/2", g) @@ -131,9 +131,11 @@ ITensors.disable_warn_order() ψψ = combine_linkinds(ψψ, combiners) ψOψ = combine_linkinds(ψOψ, combiners) pψψ = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ))) - my_contract_to_MPS(args...) = - ITensorNetworks.contract_to_MPS(args...; cutoff=1e-6, maxdim=4) - mts = belief_propagation(pψψ; contractor=my_contract_to_MPS) + mts = belief_propagation( + pψψ; + contractor=ITensorNetworks.contract_density_matrix, + contractor_kwargs=(; cutoff=1e-6, maxdim=4), + ) env_tensors = environment_tensors(pψψ, mts, [v]) numerator = contract(vcat(env_tensors, ITensor[ψOψ[v]]))[]