diff --git a/docs/src/api.md b/docs/src/api.md index ec7999f..3e134c7 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -11,6 +11,7 @@ CurrentModule = SpinGlassNetworks ising_graph inter_cluster_edges prune +couplings ``` ## Clustered Hamiltonian @@ -18,6 +19,58 @@ prune split_into_clusters clustered_hamiltonian decode_clustered_hamiltonian_state +rank_reveal +energy +energy_2site +cluster_size +bond_energy +exact_cond_prob +truncate_clustered_hamiltonian +``` + +## Lattice +```@docs +super_square_lattice +pegasus_lattice +zephyr_lattice +``` + +## Belief propagation +```@docs +local_energy +interaction_energy +get_neighbors +belief_propagation +MergedEnergy +update_message +clustered_hamiltonian_2site +merge_vertices_cl_h +projector +SparseCSC +``` + +## Projectors +```@docs +PoolOfProjectors +get_projector! +add_projector! +empty! +``` + +## Spectrum +```@docs +Spectrum +matrix_to_integers +gibbs_tensor +brute_force +``` + +## Truncate +```@docs +truncate_clustered_hamiltonian_1site_BP +truncate_clustered_hamiltonian_2site_BP +truncate_clustered_hamiltonian_2site_energy +select_numstate_best ``` ## Auxiliary Functions diff --git a/docs/src/userguide.md b/docs/src/userguide.md index 69c8b84..f31cca2 100644 --- a/docs/src/userguide.md +++ b/docs/src/userguide.md @@ -73,7 +73,7 @@ cl_h = clustered_hamiltonian( ``` ## Pegasus graphs -The Pegasus graph is a type of graph architecture used in quantum computing systems, particularly in the quantum annealing machines developed by D-Wave Systems. It is a two-dimensional lattice of unit cells, each consisting of a bipartite graph of $K_{4,4}$ complete bipartite subgraphs. Futer details can be found [here](https://docs.dwavesys.com/docs/latest/c_gs_4.html#pegasus-graph). +The Pegasus graph is a type of graph architecture used in quantum computing systems, particularly in the quantum annealing machines developed by D-Wave Systems. It is designed to provide a grid of qubits with specific connectivity patterns optimized for solving certain optimization problems. Futer details can be found [here](https://docs.dwavesys.com/docs/latest/c_gs_4.html#pegasus-graph). ```@raw html ``` diff --git a/src/bp.jl b/src/bp.jl index af40d27..f9ddb34 100644 --- a/src/bp.jl +++ b/src/bp.jl @@ -2,9 +2,32 @@ export belief_propagation, clustered_hamiltonian_2site, - projector - - + projector, + get_neighbors, + MergedEnergy, + update_message, + merge_vertices_cl_h, + local_energy, + interaction_energy, + SparseCSC + +""" +Perform belief propagation on a given clustered hamiltonian. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered hamiltonian represented as a labeled graph. +- `beta::Real`: The temperature parameter for the belief propagation algorithm. +- `tol::Real (optional, default=1e-6)`: The convergence tolerance. The algorithm stops when the message updates between iterations are smaller than this value. +- `iter::Int (optional, default=1)`: The maximum number of iterations to perform. + +# Returns: +- `beliefs::Dict`: A dictionary where keys are vertices of clustered hamiltonian, and values are the resulting beliefs after belief propagation. + +The function implements belief propagation on the given clustered hamiltonian `cl_h` to calculate beliefs for each vertex. +Belief propagation is an iterative algorithm that computes beliefs by passing messages between vertices and edges of the clustered hamiltonian. +The algorithm continues until convergence or until the specified maximum number of iterations is reached. +The beliefs are computed based on the temperature parameter `beta`, which controls the influence of energy values on the beliefs. +""" function belief_propagation(cl_h::LabelledGraph{S, T}, beta::Real; tol=1e-6, iter=1) where {S, T} messages_ve = Dict() messages_ev = Dict() @@ -65,6 +88,21 @@ function belief_propagation(cl_h::LabelledGraph{S, T}, beta::Real; tol=1e-6, ite beliefs end +""" +Returns the neighbors of a given vertex in a clustered Hamiltonian. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `vertex::NTuple`: The vertex for which neighbors are to be retrieved. + +# Returns: +- `neighbors::Vector{Tuple}`: A vector of tuples representing the neighbors of the specified vertex. Each tuple contains the following information: + - `dst_node::T`: The neighboring vertex. + - `pv::Matrix`: The projector associated with the edge connecting the vertex and its neighbor. + - `en::Real`: The energy associated with the edge connecting the vertex and its neighbor. + +This function retrieves the neighbors of a given vertex in a clustered Hamiltonian graph. It iterates through the edges of the graph and identifies edges connected to the specified vertex. For each neighboring edge, it extracts and returns the neighboring vertex, the associated projector, and the energy. +""" function get_neighbors(cl_h::LabelledGraph{S, T}, vertex::NTuple) where {S, T} neighbors = [] for edge in edges(cl_h) @@ -84,6 +122,19 @@ function get_neighbors(cl_h::LabelledGraph{S, T}, vertex::NTuple) where {S, T} return neighbors end +""" +A custom Julia struct representing energy values in a merged format for use in specific calculations. + +# Fields: +- `e11::AbstractMatrix{T}` +- `e12::AbstractMatrix{T}` +- `e21::AbstractMatrix{T}` +- `e22::AbstractMatrix{T}` + +The `MergedEnergy` struct is used to represent energy values that are organized in a merged format. This format is often utilized in certain computational tasks, where energy values are categorized based on combinations of left and right factors. + +Each field of the `MergedEnergy` struct stores energy values as an `AbstractMatrix{T}` of type `T`, where `T` is a subtype of the `Real` abstract type. The specific organization and interpretation of these energy values depend on the context in which this struct is used. +""" struct MergedEnergy{T <: Real} e11::AbstractMatrix{T} e12::AbstractMatrix{T} @@ -93,11 +144,41 @@ end Base.adjoint(s::MergedEnergy) = MergedEnergy(s.e11', s.e21', s.e12', s.e22') +""" +Update a message using energy values and temperature. + +# Arguments: +- `E_bond::AbstractArray`: An array of energy values associated with a bond or interaction. +- `message::Vector`: The input message vector to be updated. +- `beta::Real`: The temperature parameter controlling the influence of energy values. + +# Returns: +- `updated_message::Vector`: The updated message vector after applying the energy-based update. + +This function takes energy values `E_bond` associated with a bond or interaction, an input message vector `message`, and a temperature parameter `beta`. It updates the message by first adjusting the energy values relative to their minimum value, exponentiating them with a negative sign and scaling by `beta`, and then multiplying them element-wise with the input message. + +The result is an updated message that reflects the influence of energy values and temperature. +""" function update_message(E_bond::AbstractArray, message::Vector, beta::Real) E_bond = E_bond .- minimum(E_bond) exp.(-beta * E_bond) * message end +""" +Update a message using energy values and temperature in a merged energy format. + +# Arguments: +- `E_bond::MergedEnergy`: An instance of the `MergedEnergy` type representing energy values for the bond or interaction. +- `message::Vector`: The input message vector to be updated. +- `beta::Real`: The temperature parameter controlling the influence of energy values. + +# Returns: +- `updated_message::Vector`: The updated message vector after applying the energy-based update. + +This function takes energy values `E_bond` in a merged energy format, an input message vector `message`, and a temperature parameter `beta`. It updates the message based on the energy values and temperature using a specified algorithm. + +The `MergedEnergy` type represents energy values in a merged format, and the function processes these values accordingly to update the message vector. +""" function update_message(E_bond::MergedEnergy, message::Vector, beta::Real) e11, e12, e21, e22 = E_bond.e11, E_bond.e12, E_bond.e21, E_bond.e22 # equivalent to @@ -143,7 +224,20 @@ function update_message(E_bond::MergedEnergy, message::Vector, beta::Real) R end - +""" +Constructs a clustered Hamiltonian for a given clustered Hamiltonian with a 2-site cluster approximation. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `beta::Real`: The temperature parameter for the 2-site cluster Hamiltonian construction. + +# Returns: +- `new_cl_h::LabelledGraph{MetaDiGraph}`: A new labeled graph representing the 2-site cluster Hamiltonian. + + This function constructs a clustered Hamiltonian `cl_h` by applying a 2-site cluster approximation. It combines and merges vertices and edges of the original graph to create a simplified representation of the Hamiltonian. + +The resulting `new_cl_h` graph represents the 2-site cluster Hamiltonian with simplified interactions between clusters. The energy values, projectors, and spectra associated with the new vertices and edges are computed based on the provided temperature parameter `beta`. +""" function clustered_hamiltonian_2site(cl_h::LabelledGraph{S, T}, beta::Real) where {S, T} unified_vertices = unique([vertex[1:2] for vertex in vertices(cl_h)]) @@ -173,7 +267,7 @@ function clustered_hamiltonian_2site(cl_h::LabelledGraph{S, T}, beta::Real) wher add_edge!(new_cl_h, (v1, v2), (w1, w2)) - E, pl, pr = merge_vertices(cl_h, beta, v, w) + E, pl, pr = merge_vertices_cl_h(cl_h, beta, v, w) ipl = add_projector!(new_lp, pl) ipr = add_projector!(new_lp, pr) set_props!(new_cl_h, (v1, v2), (w1, w2), Dict(:ipl => ipl, :en => E, :ipr => ipr)) @@ -183,7 +277,25 @@ function clustered_hamiltonian_2site(cl_h::LabelledGraph{S, T}, beta::Real) wher new_cl_h end -function merge_vertices(cl_h::LabelledGraph{S, T}, β::Real, node1::NTuple{3, Int64}, node2::NTuple{3, Int64} +""" +Merge two vertices in a clustered Hamiltonian to create a single merged vertex. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `β::Real`: The temperature parameter controlling the influence of energy values. +- `node1::NTuple{3, Int64}`: The coordinates of the first vertex to merge. +- `node2::NTuple{3, Int64}`: The coordinates of the second vertex to merge. + +# Returns: +- `merged_energy::MergedEnergy`: An instance of the `MergedEnergy` type representing the merged energy values. +- `pl::AbstractVector`: The merged left projector. +- `pr::AbstractVector`: The merged right projector. + +This function merges two vertices in a clustered Hamiltonian graph `cl_h` to create a single merged vertex. The merging process combines projectors and energy values associated with the original vertices based on the provided temperature parameter `β`. + +The merged energy values, left projector `pl`, and right projector `pr` are computed based on the interactions between the original vertices and their respective projectors. +""" +function merge_vertices_cl_h(cl_h::LabelledGraph{S, T}, β::Real, node1::NTuple{3, Int64}, node2::NTuple{3, Int64} ) where {S, T} i1, j1, _ = node1 i2, j2, _ = node2 @@ -220,10 +332,38 @@ function merge_vertices(cl_h::LabelledGraph{S, T}, β::Real, node1::NTuple{3, In MergedEnergy(e11, e12, e21, e22), pl, pr end +""" +Get the local energy associated with a vertex in a clustered Hamiltonian. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `v::NTuple{3, Int64}`: The coordinates of the vertex for which the local energy is requested. + +# Returns: +- `local_energy::AbstractVector`: An abstract vector containing the local energy values associated with the specified vertex. + +This function retrieves the local energy values associated with a given vertex `v` in a clustered Hamiltonian graph `cl_h`. If the vertex exists in the graph and has associated energy values, it returns those values; otherwise, it returns a vector of zeros. + +The local energy values are typically obtained from the spectrum associated with the vertex. +""" function local_energy(cl_h::LabelledGraph{S, T}, v::NTuple{3, Int64}) where {S, T} has_vertex(cl_h, v) ? get_prop(cl_h, v, :spectrum).energies : zeros(1) end +""" +Get the interaction energy between two vertices in a clustered Hamiltonian. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `v::NTuple{3, Int64}`: The coordinates of the first vertex. +- `w::NTuple{3, Int64}`: The coordinates of the second vertex. + +# Returns: +- `interaction_energy::AbstractMatrix`: An abstract matrix containing the interaction energy values between the specified vertices. + +This function retrieves the interaction energy values between two vertices, `v` and `w`, in a clustered Hamiltonian graph `cl_h`. If there is a directed edge from `w` to `v`, it returns the corresponding energy values; if there is a directed edge from `v` to `w`, it returns the transpose of the energy values; otherwise, it returns a matrix of zeros. +The interaction energy values represent the energy associated with the interaction or connection between the two vertices. +""" function interaction_energy(cl_h::LabelledGraph{S, T}, v::NTuple{3, Int64}, w::NTuple{3, Int64}) where {S, T} if has_edge(cl_h, w, v) get_prop(cl_h, w, v, :en)' @@ -234,6 +374,19 @@ function interaction_energy(cl_h::LabelledGraph{S, T}, v::NTuple{3, Int64}, w::N end end +""" +Get the projector associated with an edge between two vertices in a clustered Hamiltonian. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `v::NTuple{N, Int64}`: The coordinates of one of the two vertices connected by the edge. +- `w::NTuple{N, Int64}`: The coordinates of the other vertex connected by the edge. + +# Returns: +- `p::AbstractVector`: An abstract vector representing the projector associated with the specified edge. + +This function retrieves the projector associated with an edge between two vertices, `v` and `w`, in a clustered Hamiltonian graph `cl_h`. If there is a directed edge from `w` to `v`, it returns the index of right projector (`:ipr`); if there is a directed edge from `v` to `w`, it returns the index of left projector (`:ipl`). If no edge exists between the vertices, it returns a vector of ones. +""" function projector(cl_h::LabelledGraph{S, T}, v::NTuple{N, Int64}, w::NTuple{N, Int64}) where {S, T, N} if has_edge(cl_h, w, v) idx_p = get_prop(cl_h, w, v, :ipr) @@ -256,6 +409,19 @@ function outer_projector(p1::Array{T, 1}, p2::Array{T, 1}) where T <: Number reshape(reshape(p1, :, 1) .+ maximum(p1) .* reshape(p2 .- 1, 1, :), :) end +""" +Create a sparse column-compressed (CSC) matrix with specified column indices and values. + +# Arguments: +- `::Type{R}`: The element type of the sparse matrix (e.g., `Float64`, `Int64`). +- `p::Vector{Int64}`: A vector of column indices for the non-zero values. + +# Returns: +- `sparse_matrix::SparseMatrixCSC{R}`: A sparse column-compressed matrix with non-zero values at specified columns. + +This constructor function creates a sparse column-compressed (CSC) matrix of element type `R` based on the provided column indices `p` and values. The resulting matrix has non-zero values at the specified column indices, while all other elements are zero. +The `SparseCSC` constructor is useful for creating sparse matrices with specific column indices and values efficiently. +""" function SparseCSC(::Type{R}, p::Vector{Int64}) where R <: Real n = length(p) mp = maximum(p) diff --git a/src/clustered_hamiltonian.jl b/src/clustered_hamiltonian.jl index 41dac2c..ad0b220 100644 --- a/src/clustered_hamiltonian.jl +++ b/src/clustered_hamiltonian.jl @@ -8,12 +8,25 @@ export cluster_size, truncate_clustered_hamiltonian, exact_cond_prob, - bond_energy + bond_energy, + cluster_size """ -Groups spins into clusters: Dict(factor graph coordinates -> group of spins in Ising graph) -""" +Group spins into clusters based on an assignment rule, mapping factor graph coordinates to groups of spins in the Ising graph. +Dict(factor graph coordinates -> group of spins in Ising graph) + +# Arguments: +- `ig::LabelledGraph{G, L}`: The Ising graph represented as a labeled graph. +- `assignment_rule`: A mapping that assigns Ising graph vertices to clusters based on factor graph coordinates. + +# Returns: +- `clusters::Dict{L, Vertex}`: A dictionary mapping cluster identifiers to representative vertices in the Ising graph. + +This function groups spins in the Ising graph into clusters based on an assignment rule. The assignment rule defines how factor graph coordinates correspond to clusters of spins in the Ising graph. Each cluster is represented by a vertex from the Ising graph. + +The `split_into_clusters` function is useful for organizing and analyzing spins in complex spin systems, particularly in the context of clustered Hamiltonian. +""" function split_into_clusters(ig::LabelledGraph{G, L}, assignment_rule) where {G, L} cluster_id_to_verts = Dict(i => L[] for i in values(assignment_rule)) for v in vertices(ig) push!(cluster_id_to_verts[assignment_rule[v]], v) end @@ -21,8 +34,22 @@ function split_into_clusters(ig::LabelledGraph{G, L}, assignment_rule) where {G, end """ -Create factor graph. -Factor graph order introduced as a natural order in factor graph coordinates. +Create a clustered Hamiltonian. + +This function constructs a clustered Hamiltonian from an Ising graph by introducing a natural order in clustered Hamiltonian coordinates. + +# Arguments: +- `ig::IsingGraph`: The Ising graph representing the spin system. +- `num_states_cl::Int`: The number of states per cluster. +- `spectrum::Function`: A function for calculating the spectrum of the clustered Hamiltonian. +- `cluster_assignment_rule::Dict{Int, L}`: A dictionary specifying the assignment rule that maps Ising graph vertices to clusters. + +# Returns: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. + +The `clustered_hamiltonian` function takes an Ising graph (`ig`) as input and constructs a clustered Hamiltonian by introducing a natural order in clustered Hamiltonian coordinates. It allows you to specify the number of states per cluster, a spectrum calculation function, and a cluster assignment rule, which maps Ising graph vertices to clusters. + +This function is useful for organizing and studying spin systems in a clustered Hamiltonian framework. """ function clustered_hamiltonian( ig::IsingGraph, @@ -34,7 +61,25 @@ function clustered_hamiltonian( clustered_hamiltonian(ig, ns, spectrum=spectrum, cluster_assignment_rule=cluster_assignment_rule) end +""" +Create a clustered Hamiltonian. + +This function constructs a clustered Hamiltonian from an Ising graph by introducing a natural order in clustered Hamiltonian coordinates. + +# Arguments: +- `ig::IsingGraph`: The Ising graph representing the spin system. +- `num_states_cl::Dict{T, Int}`: A dictionary specifying the number of states per cluster for different clusters. +- `spectrum::Function`: A function for calculating the spectrum of the clustered Hamiltonian. +- `cluster_assignment_rule::Dict{Int, T}`: A dictionary specifying the assignment rule that maps Ising graph vertices to clusters. +# Returns: +- `cl_h::LabelledGraph{MetaDiGraph}`: The clustered Hamiltonian represented as a labeled graph. + +The `clustered_hamiltonian` function takes an Ising graph (`ig`) as input and constructs a clustered Hamiltonian by introducing a natural order in clustered Hamiltonian coordinates. It allows you to specify the number of states per cluster, a spectrum calculation function, and a cluster assignment rule, which maps Ising graph vertices to clusters. + +This function is useful for organizing and studying spin systems in a clustered Hamiltonian framework. + +""" function clustered_hamiltonian( ig::IsingGraph, num_states_cl::Dict{T, Int}; @@ -82,6 +127,25 @@ function clustered_hamiltonian( cl_h end +""" +Create a clustered Hamiltonian with optional cluster sizes. + +This function constructs a clustered Hamiltonian from an Ising graph by introducing a natural order in clustered Hamiltonian coordinates. + +# Arguments: +- `ig::IsingGraph`: The Ising graph representing the spin system. +- `spectrum::Function`: A function for calculating the spectrum of the clustered Hamiltonian. +- `cluster_assignment_rule::Dict{Int, T}`: A dictionary specifying the assignment rule that maps Ising graph vertices to clusters. + +# Returns: +- `cl_h::LabelledGraph{MetaDiGraph}`: The clustered Hamiltonian represented as a labeled graph. + +The `clustered_hamiltonian` function takes an Ising graph (`ig`) as input and constructs a clustered Hamiltonian by introducing a natural order in clustered Hamiltonian coordinates. You can optionally specify a spectrum calculation function and a cluster assignment rule, which maps Ising graph vertices to clusters. + +If you want to specify custom cluster sizes, use the alternative version of this function by passing a `Dict{T, Int}` containing the number of states per cluster as `num_states_cl`. + +This function is useful for organizing and studying spin systems in a clustered Hamiltonian framework. +""" function clustered_hamiltonian( ig::IsingGraph; spectrum::Function=full_spectrum, @@ -90,7 +154,24 @@ function clustered_hamiltonian( clustered_hamiltonian(ig, Dict{T, Int}(), spectrum=spectrum, cluster_assignment_rule=cluster_assignment_rule) end -function rank_reveal(energy, order=:PE) where T <: Real #TODO: add type +""" +Reveal ranks and energies in a specified order. + +This function calculates and reveals the ranks and energies of a set of states in either the 'PE' (Projector Energy) or 'EP' (Energy Projector) order. + +# Arguments: +- `energy`: The energy values of states. +- `order::Symbol`: The order in which to reveal the ranks and energies. It can be either `:PE` for 'Projector Energy)' order (default) or `:EP` for 'Energy Projector' order. + +# Returns: +- If `order` is `:PE`, the function returns a tuple `(P, E)` where: + - `P`: A permutation matrix representing projectors. + - `E`: An array of energy values. +- If `order` is `:EP`, the function returns a tuple `(E, P)` where: + - `E`: An array of energy values. + - `P`: A permutation matrix representing projectors. +""" +function rank_reveal(energy, order=:PE) #TODO: add type @assert order ∈ (:PE, :EP) dim = order == :PE ? 1 : 2 E, idx = unique_dims(energy, dim) @@ -99,9 +180,20 @@ function rank_reveal(energy, order=:PE) where T <: Real #TODO: add type end """ -Returns Dict(vertex of ising graph -> spin value) -Assumes that state has the same order as vertices in factor graph! TODO: check the order consistency over external packages. + +Decode a clustered Hamiltonian state into Ising graph spin values. + +This function decodes a state from a clustered Hamiltonian into Ising graph spin values and returns a dictionary mapping each Ising graph vertex to its corresponding spin value. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `state::Vector{Int}`: The state to be decoded, represented as an array of state indices for each vertex in the clustered Hamiltonian. + +# Returns: +- `spin_values::Dict{Int, Int}`: A dictionary mapping each Ising graph vertex to its corresponding spin value. + +This function assumes that the state has the same order as the vertices in the factor graph (clustered Hamiltonian). It decodes the state consistently based on the cluster assignments and spectra of the clustered Hamiltonian. """ function decode_clustered_hamiltonian_state(cl_h::LabelledGraph{S, T}, state::Vector{Int}) where {S, T} ret = Dict{Int, Int}() @@ -116,7 +208,20 @@ function decode_clustered_hamiltonian_state(cl_h::LabelledGraph{S, T}, state::Ve ret end +""" +Calculate the energy of a clustered Hamiltonian state. + +This function calculates the energy of a given state in a clustered Hamiltonian. The state is represented as a dictionary mapping each Ising graph vertex to its corresponding spin value. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `σ::Dict{T, Int}`: A dictionary mapping Ising graph vertices to their spin values. +# Returns: +- `en_cl_h::Float64`: The energy of the state in the clustered Hamiltonian. + +This function computes the energy by summing the energies associated with individual clusters and the interaction energies between clusters. It takes into account the cluster spectra and projectors stored in the clustered Hamiltonian. +""" function energy(cl_h::LabelledGraph{S, T}, σ::Dict{T, Int}) where {S, T} en_cl_h = 0.0 for v ∈ vertices(cl_h) en_cl_h += get_prop(cl_h, v, :spectrum).energies[σ[v]] end @@ -131,7 +236,21 @@ function energy(cl_h::LabelledGraph{S, T}, σ::Dict{T, Int}) where {S, T} en_cl_h end +""" +Calculate the interaction energy between two nodes in a clustered Hamiltonian. + +This function computes the interaction energy between two specified nodes in a clustered Hamiltonian, represented as a labeled graph. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `i::Int`: The index of the first site. +- `j::Int`: The index of the second site. + +# Returns: +- `int_eng::AbstractMatrix{T}`: The interaction energy matrix between the specified sites. +The function checks if there is an interaction edge between the two sites (i, j) in both directions (i -> j and j -> i). If such edges exist, it retrieves the interaction energy matrix, projectors, and calculates the interaction energy. If no interaction edge is found, it returns a zero matrix. +""" function energy_2site(cl_h::LabelledGraph{S, T}, i::Int, j::Int) where {S, T} # matrix of interaction energies between two nodes if has_edge(cl_h, (i, j, 1), (i, j, 2)) @@ -154,6 +273,22 @@ function energy_2site(cl_h::LabelledGraph{S, T}, i::Int, j::Int) where {S, T} int_eng end +""" +Calculate the bond energy between two clusters in a clustered Hamiltonian. + +This function computes the bond energy between two specified clusters (cluster nodes) in a clustered Hamiltonian, represented as a labeled graph. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `cl_h_u::NTuple{N, Int64}`: The coordinates of the first cluster. +- `cl_h_v::NTuple{N, Int64}`: The coordinates of the second cluster. +- `σ::Int`: The Ising spin value for which the bond energy is calculated. + +# Returns: +- `energies::AbstractVector{T}`: The bond energy vector between the two clusters for the specified Ising spin value. + +The function checks if there is an edge between the two clusters (u -> v and v -> u). If such edges exist, it retrieves the bond energy matrix and projectors and calculates the bond energy. If no bond edge is found, it returns a zero vector. +""" function bond_energy( cl_h::LabelledGraph{S, T}, cl_h_u::NTuple{N, Int64}, @@ -179,10 +314,39 @@ function bond_energy( end end +""" +Get the size of a cluster in a clustered Hamiltonian. + +This function returns the size (number of states) of a cluster in a clustered Hamiltonian, represented as a labeled graph. + +# Arguments: +- `clustered_hamiltonian::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `vertex::T`: The vertex (cluster) for which the size is to be determined. + +# Returns: +- `size::Int`: The number of states in the specified cluster. + +The function retrieves the spectrum associated with the specified cluster and returns the length of the energy vector in that spectrum. +""" function cluster_size(clustered_hamiltonian::LabelledGraph{S, T}, vertex::T) where {S, T} length(get_prop(clustered_hamiltonian, vertex, :spectrum).energies) end +""" +Calculate the exact conditional probability of a target state in a clustered Hamiltonian. + +This function computes the exact conditional probability of a specified target state in a clustered Hamiltonian, represented as a labeled graph. + +# Arguments: +- `clustered_hamiltonian::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `beta`: The inverse temperature parameter. +- `target_state::Dict`: A dictionary specifying the target state as a mapping of cluster vertices to Ising spin values. + +# Returns: +- `prob::Float64`: The exact conditional probability of the target state. + +The function generates all possible states for the clusters in the clustered Hamiltonian, calculates their energies, and computes the probability distribution based on the given inverse temperature parameter. It then calculates the conditional probability of the specified target state by summing the probabilities of states that match the target state. +""" function exact_cond_prob(clustered_hamiltonian::LabelledGraph{S, T}, beta, target_state::Dict) where {S, T} # TODO: Not going to work without PoolOfProjectors ver = vertices(clustered_hamiltonian) @@ -192,8 +356,26 @@ function exact_cond_prob(clustered_hamiltonian::LabelledGraph{S, T}, beta, targe prob = exp.(-beta .* energies) prob ./= sum(prob) sum(prob[findall([all(s[k] == v for (k, v) ∈ target_state) for s ∈ states])]) - end +end + +""" +Truncate a clustered Hamiltonian based on specified states. +This function truncates a given clustered Hamiltonian by selecting a subset of states for each cluster based on the provided `states` dictionary. +The resulting truncated Hamiltonian contains only the selected states for each cluster. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `states::Dict`: A dictionary specifying the states to be retained for each cluster. + +# Returns: +- `new_cl_h::LabelledGraph{MetaDiGraph}`: The truncated clustered Hamiltonian with reduced states. + +The function creates a new clustered Hamiltonian `new_cl_h` with the same structure as the input `cl_h`. +It then updates the spectrum of each cluster in `new_cl_h` by selecting the specified states from the original spectrum. +Additionally, it updates the interactions and projectors between clusters based on the retained states. +The resulting `new_cl_h` represents a truncated version of the original Hamiltonian. +""" function truncate_clustered_hamiltonian(cl_h::LabelledGraph{S, T}, states::Dict) where {S, T} new_cl_h = LabelledGraph{MetaDiGraph}(vertices(cl_h)) diff --git a/src/ising.jl b/src/ising.jl index 7ad2f0f..ca8fc6f 100644 --- a/src/ising.jl +++ b/src/ising.jl @@ -10,7 +10,8 @@ export biases, couplings, IsingGraph, - prune + prune, + inter_cluster_edges const Instance = Union{String, Dict} const IsingGraph{T} = LabelledGraph{MetaGraph{Int, T}} @@ -20,7 +21,21 @@ function unique_nodes(ising_tuples) end """ -Creates Ising graph, convention: H = scale * sum_{i, j} (J_{ij} * s_i * s_j + J_{ii} * s_i) +Create an Ising graph from interaction data. + +This function creates an Ising graph from interaction data provided in the form of an `inst` argument. The Ising graph represents a system of spins, where each spin is associated with a vertex, and interactions between spins are represented as edges with corresponding weights. + +# Arguments: +- `::Type{T}`: The type of the edge weights, typically `Float64` or `Float32`. +- `inst::Instance`: Interaction data, which can be either a file path to a CSV file or a collection of triples `(i, j, J)` representing interactions between spins, where `i` and `j` are spin indices, and `J` is the interaction strength. +- `scale::Real`: A scaling factor applied to interaction strengths (default is 1). +- `rank_override::Dict`: A dictionary specifying the rank (number of states) for each vertex. If not provided, a default rank of 2 is used for all vertices. + +# Returns: +- `ig::IsingGraph{T}`: The Ising graph representing the spin system. + +The function reads interaction data and constructs an Ising graph `ig`. It assigns interaction strengths to edges between spins and optionally scales them by the `scale` factor. The `rank_override` dictionary can be used to specify the rank (number of states) for individual vertices, allowing customization of the Ising model. +Convention: H = scale * sum_{i, j} (J_{ij} * s_i * s_j + J_{ii} * s_i) """ function ising_graph(::Type{T}, inst::Instance; scale::Real=1, rank_override::Dict=Dict{Int, Int}()) where T if inst isa String @@ -55,6 +70,19 @@ rank_vec(ig::IsingGraph) = Int[get_prop((ig), v, :rank) for v ∈ vertices(ig)] basis_size(ig::IsingGraph) = prod(rank_vec(ig)) biases(ig::IsingGraph) = get_prop.(Ref(ig), vertices(ig), :h) +""" +Return the coupling strengths between vertices of an Ising graph. + +This function computes and returns the coupling strengths (interaction energies) between pairs of vertices in an Ising graph `ig`. The coupling strengths are represented as a matrix, where each element `(i, j)` corresponds to the interaction energy between vertex `i` and vertex `j`. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph representing a system of spins with associated interaction strengths. + +# Returns: +- `J::Matrix{T}`: A matrix of coupling strengths between vertices of the Ising graph. + +The function iterates over the edges of the Ising graph and extracts the interaction strengths associated with each edge, populating the `J` matrix accordingly. +""" function couplings(ig::IsingGraph{T}) where T J = zeros(T, nv(ig), nv(ig)) for edge ∈ edges(ig) @@ -67,7 +95,20 @@ end cluster(ig::IsingGraph, verts) = induced_subgraph(ig, collect(verts)) """ -Returns dense adjacency matrix between clusters. +Return the dense adjacency matrix between clusters of vertices in an Ising graph. + +This function computes and returns the dense adjacency matrix `J` between clusters of vertices represented by two Ising graphs, `cl1` and `cl2`, within the context of the larger Ising graph `ig`. The adjacency matrix represents the interaction strengths between clusters of vertices, where each element `(i, j)` corresponds to the interaction strength between cluster `i` in `cl1` and cluster `j` in `cl2`. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph representing a system of spins with associated interaction strengths. +- `cl1::IsingGraph{T}`: The first Ising graph representing one cluster of vertices. +- `cl2::IsingGraph{T}`: The second Ising graph representing another cluster of vertices. + +# Returns: +- `outer_edges::Vector{LabelledEdge}`: A vector of labeled edges representing the interactions between clusters. +- `J::Matrix{T}`: A dense adjacency matrix representing interaction strengths between clusters. + +The function first identifies the outer edges that connect vertices between the two clusters in the context of the larger Ising graph `ig`. It then computes the interaction strengths associated with these outer edges and populates the dense adjacency matrix `J` accordingly. """ function inter_cluster_edges(ig::IsingGraph{T}, cl1::IsingGraph{T}, cl2::IsingGraph{T}) where T outer_edges = [LabelledEdge(i, j) for i ∈ vertices(cl1), j ∈ vertices(cl2) if has_edge(ig, i, j)] @@ -80,8 +121,19 @@ function inter_cluster_edges(ig::IsingGraph{T}, cl1::IsingGraph{T}, cl2::IsingGr end """ -Get rid of non-existing spins. Used only in MPS_search, would be obsolete if MPS_search uses QMps. +Remove non-existing spins from an Ising graph. + +This function removes non-existing spins from the given Ising graph `ig`. Non-existing spins are those that have zero degree (no connections to other spins) and also have an external magnetic field (`h`) that is not approximately equal to zero within the specified tolerance `atol`. + +# Arguments: +- `ig::IsingGraph`: The Ising graph to be pruned. +- `atol::Real`: The tolerance for considering the external magnetic field as zero. The default value is `1e-14`. + +# Returns: +- `pruned_graph::IsingGraph`: A new Ising graph with non-existing spins removed. + +The function returns a pruned version of the input Ising graph, where non-existing spins and their associated properties are removed. """ function prune(ig::IsingGraph; atol::Real=1e-14) to_keep = vcat( diff --git a/src/lattice.jl b/src/lattice.jl index ac71d5a..9ed4106 100644 --- a/src/lattice.jl +++ b/src/lattice.jl @@ -9,13 +9,39 @@ export zephyr_lattice_5tuple_rotated, periodic_lattice -"Variable number of Ising graph -> Factor graph coordinate system" +""" +Create a mapping from factor graph coordinates to a super square lattice arrangement. +Variable number of Ising graph -> Factor graph coordinate system + +This function generates a mapping that relates factor graph coordinates to a super square lattice arrangement. The super square lattice is defined by the size of five dimensions: `(m, um, n, un, t)`. + +# Arguments: +- `size::NTuple{5, Int}`: A tuple specifying the size of the super square lattice in five dimensions: `(m, um, n, un, t)`. + +# Returns: +- `coord_map::Dict`: A dictionary that maps factor graph coordinates to the corresponding lattice coordinates. + +The `size` tuple represents the dimensions of the super square lattice. The function creates a dictionary where factor graph coordinates are associated with their corresponding lattice coordinates. +""" function super_square_lattice(size::NTuple{5, Int}) m, um, n, un, t = size old = LinearIndices((1:t, 1:un, 1:n, 1:um, 1:m)) Dict(old[k, uj, j, ui, i] => (i, j) for i=1:m, ui=1:um, j=1:n, uj=1:un, k=1:t) end +""" +Create a mapping from factor graph coordinates to a simplified super square lattice arrangement. + +This function generates a mapping that relates factor graph coordinates to a simplified super square lattice arrangement. The simplified super square lattice is defined by the size of three dimensions: `(m, n, t)`. + +# Arguments: +- `size::NTuple{3, Int}`: A tuple specifying the size of the simplified super square lattice in three dimensions: `(m, n, t)`. + +# Returns: +- `coord_map::Dict`: A dictionary that maps factor graph coordinates to the corresponding lattice coordinates. + +The `size` tuple represents the dimensions of the simplified super square lattice. The function internally adds the required dimensions `(1, 1)` to make it compatible with the `super_square_lattice` function, which deals with five dimensions. +""" function super_square_lattice(size::NTuple{3, Int}) m, n, t = size super_square_lattice((m, 1, n, 1, t)) @@ -23,6 +49,19 @@ end pegasus_lattice(size::NTuple{2, Int}) = pegasus_lattice((size[1], size[2], 3)) +""" +Create a mapping from factor graph coordinates to Pegasus lattice coordinates. + +This function generates a mapping that relates factor graph coordinates to Pegasus lattice coordinates based on the specified size of the Pegasus lattice in three dimensions: `(m, n, t)`. + +# Arguments: +- `size::NTuple{3, Int}`: A tuple specifying the size of the Pegasus lattice in three dimensions: `(m, n, t)`. + +# Returns: +- `coord_map::Dict`: A dictionary that maps factor graph coordinates to the corresponding Pegasus lattice coordinates. + +The Pegasus lattice is a specialized lattice used in quantum computing, and this function allows you to convert between factor graph coordinates and Pegasus lattice coordinates. +""" function pegasus_lattice(size::NTuple{3, Int}) m, n, t = size old = LinearIndices((1:8*t, 1:n, 1:m)) @@ -82,6 +121,19 @@ end zephyr_lattice(size::NTuple{2, Int}) = zephyr_lattice((size[1], size[2], 4)) +""" +Create a mapping from factor graph coordinates to Zephyr lattice coordinates. + +This function generates a mapping that relates factor graph coordinates to Zephyr lattice coordinates based on the specified size of the Zephyr lattice in three dimensions: `(m, n, t)`. + +# Arguments: +- `size::NTuple{3, Int}`: A tuple specifying the size of the Zephyr lattice in three dimensions: `(m, n, t)`. + +# Returns: +- `coord_map::Dict`: A dictionary that maps factor graph coordinates to the corresponding Zephyr lattice coordinates. + +The Zephyr lattice is a specialized lattice used in quantum computing, and this function allows you to convert between factor graph coordinates and Zephyr lattice coordinates. +""" function zephyr_lattice(size::NTuple{3, Int}) m, n, t = size zephyr_lattice_5tuple_rotated(m+1, n+1, zephyr_lattice_5tuple((Int(m/2), Int(n/2), t))) diff --git a/src/projectors.jl b/src/projectors.jl index 56a6ef7..29e5d1c 100644 --- a/src/projectors.jl +++ b/src/projectors.jl @@ -1,10 +1,23 @@ export PoolOfProjectors, get_projector!, - add_projector! + add_projector!, + empty! const Proj{T} = Union{Vector{T}, CuArray{T, 1}} +""" +`PoolOfProjectors` is a data structure for managing projectors associated with Ising model sites. It allows efficient storage and retrieval of projectors based on their indices and provides support for different computational devices. + + # Fields: + - `data::Dict{Symbol, Dict{Int, Proj{T}}}`: A dictionary that stores projectors associated with different computational devices (`:CPU`, `:GPU`, etc.). The inner dictionary maps site indices to projectors. + - `default_device::Symbol`: A symbol representing the default computational device for projectors in the pool. + - `sizes::Dict{Int, Int}`: A dictionary that maps site indices to the maximum projector size for each site. + + # Constructors: + - `PoolOfProjectors(data::Dict{Int, Dict{Int, Vector{T}}}) where T`: Create a `PoolOfProjectors` with initial data for projectors. The data is provided as a dictionary that maps site indices to projectors stored in different computational devices. The `sizes` dictionary is automatically populated based on the maximum projector size for each site. + - `PoolOfProjectors{T}() where T`: Create an empty `PoolOfProjectors` with no projectors initially stored. +""" struct PoolOfProjectors{T <: Integer} data::Dict{Symbol, Dict{Int, Proj{T}}} default_device::Symbol @@ -25,6 +38,15 @@ Base.eltype(lp::PoolOfProjectors{T}) where T = T Base.length(lp::PoolOfProjectors) = length(lp.data[lp.default_device]) Base.length(lp::PoolOfProjectors, device::Symbol) = length(lp.data[device]) +""" +Empty the pool of projectors associated with a specific computational device. + +This function removes all projectors stored on the specified computational device, freeing up memory resources. + +# Arguments: +- `lp::PoolOfProjectors`: The `PoolOfProjectors` object containing projectors. +- `device::Symbol`: The computational device for which projectors should be emptied (e.g., `:CPU`, `:GPU`). +""" function Base.empty!(lp::PoolOfProjectors, device::Symbol) if device ∈ keys(lp.data) empty!(lp.data[device]) @@ -36,7 +58,21 @@ Base.size(lp::PoolOfProjectors, index::Int) = lp.sizes[index] get_projector!(lp::PoolOfProjectors, index::Int) = get_projector!(lp, index, lp.default_device) -# TODO This is version for only one GPU +""" +TODO This is version for only one GPU + +Retrieve or create a projector from the `PoolOfProjectors` associated with a specific device. + +This function retrieves a projector from the `PoolOfProjectors` if it already exists. If the projector does not exist in the pool, it creates a new one and stores it for future use on the specified computational device. + +# Arguments: +- `lp::PoolOfProjectors{T}`: The `PoolOfProjectors` object containing projectors. +- `index::Int`: The index of the projector to retrieve or create. +- `device::Symbol`: The computational device on which the projector should be stored or retrieved (e.g., `:CPU`, `:GPU`). + +# Returns: +- `Proj{T}`: The projector of type `T` associated with the specified index and device. +""" function get_projector!(lp::PoolOfProjectors{T}, index::Int, device::Symbol) where T <: Integer if device ∉ keys(lp.data) push!(lp.data, device => Dict{Int, Proj{T}}()) @@ -55,6 +91,18 @@ function get_projector!(lp::PoolOfProjectors{T}, index::Int, device::Symbol) whe lp.data[device][index] end +""" +Add a projector to the `PoolOfProjectors` and associate it with an index. + +This function adds a projector `p` to the `PoolOfProjectors`. The `PoolOfProjectors` stores projectors based on their computational device (e.g., CPU or GPU) and assigns a unique index to each projector. The index can be used to retrieve the projector later using `get_projector!`. + +# Arguments: +- `lp::PoolOfProjectors{T}`: The `PoolOfProjectors` object to which the projector should be added. +- `p::Proj`: The projector to be added to the pool. The type of the projector `Proj` should match the type `T` specified in the `PoolOfProjectors`. + +# Returns: +- `Int`: The unique index associated with the added projector in the pool. +""" function add_projector!(lp::PoolOfProjectors{T}, p::Proj) where T <: Integer if lp.default_device == :CPU p = Array{T}(p) diff --git a/src/spectrum.jl b/src/spectrum.jl index 35878fd..0a48858 100644 --- a/src/spectrum.jl +++ b/src/spectrum.jl @@ -16,6 +16,20 @@ all_states(rank::Union{Vector, NTuple}) = Iterators.product(local_basis.(rank).. const State = Vector{Int} +""" +A `Spectrum` represents the energy spectrum of a system. + +A `Spectrum` consists of energy levels, their corresponding states, and integer representations of the states. + +# Fields: +- `energies::Vector{<:Real}`: An array of energy levels. +- `states::AbstractArray{State}`: An array of states. +- `states_int::Vector{Int}`: An array of integer representations of states. + +# Constructors: +- `Spectrum(energies, states, states_int)`: Creates a `Spectrum` object with the specified energy levels, states, and integer representations. +- `Spectrum(energies, states)`: Creates a `Spectrum` object with the specified energy levels and states, automatically generating integer representations. +""" struct Spectrum energies::Vector{<:Real} states::AbstractArray{State} @@ -29,17 +43,52 @@ struct Spectrum end end +""" +Converts a matrix of binary vectors to their integer representations. + +This function takes a matrix of binary vectors, where each row represents a binary vector, and converts them into their corresponding integer representations. + +# Arguments: +- `matrix::Vector{Vector{T}}`: A matrix of binary vectors. + +# Returns: +- `Vector{Int}`: An array of integer representations of the binary vectors. +""" function matrix_to_integers(matrix::Vector{Vector{T}}) where T nrows = length(matrix[1]) multipliers = 2 .^ collect(0:nrows-1) div.((hcat(matrix...)' .+ 1) , 2) * multipliers end +""" +Calculates the energy of a state in an Ising graph. + +This function calculates the energy of a given state in the context of an Ising graph. The energy is computed based on the interactions between spins and their associated biases. + +# Arguments: +- `σ::AbstractArray{State}`: An array representing the state of spins in the Ising graph. +- `ig::IsingGraph`: The Ising graph defining the interactions and biases. + +# Returns: +- `Vector{Float64}`: An array of energy values for each state. +""" function energy(σ::AbstractArray{State}, ig::IsingGraph) J, h = couplings(ig), biases(ig) dot.(σ, Ref(J), σ) + dot.(Ref(h), σ) end +""" +Calculates the energy of a state in an Ising graph. + +This function computes the energy of a given state in the context of an Ising graph. The energy is calculated based on the interactions between spins and their associated biases. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph defining the interactions and biases. +- `ig_state::Dict{Int, Int}`: A dictionary mapping spin indices to their corresponding states. + +# Returns: +- `T`: The energy of the state in the Ising graph. +""" function energy(ig::IsingGraph{T}, ig_state::Dict{Int, Int}) where T en = zero(T) for (i, σ) ∈ ig_state @@ -55,6 +104,17 @@ function energy(ig::IsingGraph{T}, ig_state::Dict{Int, Int}) where T en end +""" +Generates the energy spectrum for an Ising graph. + +This function computes the energy spectrum (energies and corresponding states) for a given Ising graph. The energy spectrum represents all possible energy levels and their associated states in the Ising graph. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph for which the energy spectrum is generated. + +# Returns: +- `Spectrum`: An instance of the `Spectrum` type containing the energy levels and states. +""" function Spectrum(ig::IsingGraph{T}) where T L = nv(ig) N = 2^L @@ -70,6 +130,18 @@ function Spectrum(ig::IsingGraph{T}) where T Spectrum(energies, states) end +""" +Computes the Gibbs tensor for an Ising graph at a given inverse temperature. + +This function calculates the Gibbs tensor for an Ising graph at a specified inverse temperature (β). The Gibbs tensor represents the conditional probabilities of states given the inverse temperature and the Ising graph. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph for which the Gibbs tensor is computed. +- `β::T (optional)`: The inverse temperature parameter. Default is 1. + +# Returns: +- `Matrix{T}`: A matrix representing the Gibbs tensor with conditional probabilities. +""" function gibbs_tensor(ig::IsingGraph{T}, β::T=1) where T σ = collect.(all_states(rank_vec(ig))) ρ = exp.(-β .* energy(σ, ig)) @@ -80,7 +152,22 @@ function brute_force(ig::IsingGraph, s::Symbol=:CPU; num_states::Int=1) brute_force(ig, Val(s); num_states) end -#TODO only one of brute_force and full_spectrum should remain +""" +TODO only one of brute_force and full_spectrum should remain + +Performs brute-force calculation of the lowest-energy states and their energies for an Ising graph. + +This function exhaustively computes the lowest-energy states and their corresponding energies for an Ising graph. +The calculation is done using brute-force enumeration, making it feasible only for small Ising graphs. + +# Arguments: +- `ig::IsingGraph{T}`: The Ising graph for which the lowest-energy states are computed. +- `::Val{:CPU}`: A value indicating that the computation is performed on the CPU. +- `num_states::Int (optional)`: The maximum number of lowest-energy states to calculate. Default is 1. + +# Returns: +- `Spectrum`: A `Spectrum` object containing the lowest-energy states and their energies. +""" function brute_force(ig::IsingGraph{T}, ::Val{:CPU}; num_states::Int=1) where T L = nv(ig) L == 0 && return Spectrum(zeros(T, 1), Vector{Vector{Int}}[], zeros(T, 1)) diff --git a/src/truncate.jl b/src/truncate.jl index 4107720..2da4e79 100644 --- a/src/truncate.jl +++ b/src/truncate.jl @@ -1,9 +1,25 @@ export truncate_clustered_hamiltonian_2site_energy, truncate_clustered_hamiltonian_1site_BP, - truncate_clustered_hamiltonian_2site_BP + truncate_clustered_hamiltonian_2site_BP, + select_numstate_best +""" +Truncates a clustered Hamiltonian using belief propagation (BP) for a single site cluster. +This function employs belief propagation (BP) to approximate the most probable states and energies for a clustered Hamiltonian +associated with a single-site cluster. It then truncates the clustered Hamiltonian based on the most probable states. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `num_states::Int`: The maximum number of most probable states to keep. +- `beta::Real (optional)`: The inverse temperature parameter for the BP algorithm. Default is 1.0. +- `tol::Real (optional)`: The tolerance value for convergence in BP. Default is 1e-10. +- `iter::Int (optional)`: The maximum number of BP iterations. Default is 1. + +# Returns: +- `LabelledGraph{S, T}`: A truncated clustered Hamiltonian. +""" function truncate_clustered_hamiltonian_1site_BP( cl_h::LabelledGraph{S, T}, num_states::Int; @@ -20,6 +36,19 @@ function truncate_clustered_hamiltonian_1site_BP( truncate_clustered_hamiltonian(cl_h, states) end +""" +Truncate a clustered Hamiltonian based on 2-site energy states. + +This function truncates a clustered Hamiltonian by considering 2-site energy states and selecting the most probable states +to keep. It computes the energies for all 2-site combinations and selects the states that maximize the probability. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `num_states::Int`: The maximum number of most probable states to keep. + +# Returns: +- `LabelledGraph{S, T}`: A truncated clustered Hamiltonian. +""" function truncate_clustered_hamiltonian_2site_energy(cl_h::LabelledGraph{S, T}, num_states::Int) where {S, T} # TODO: name to be clean to make it consistent with square2 and squarestar2 states = Dict() @@ -38,6 +67,21 @@ function truncate_clustered_hamiltonian_2site_energy(cl_h::LabelledGraph{S, T}, truncate_clustered_hamiltonian(cl_h, states) end +""" +Truncate a clustered Hamiltonian based on 2-site belief propagation states. + +This function truncates a clustered Hamiltonian by considering 2-site belief propagation states and selecting the most probable states +to keep. It computes the beliefs for all 2-site combinations and selects the states that maximize the probability. + +# Arguments: +- `cl_h::LabelledGraph{S, T}`: The clustered Hamiltonian represented as a labeled graph. +- `beliefs::Dict`: A dictionary containing belief values for 2-site interactions. +- `num_states::Int`: The maximum number of most probable states to keep. +- `beta::Real (optional)`: The inverse temperature parameter (default is 1.0). + +# Returns: +- `LabelledGraph{S, T}`: A truncated clustered Hamiltonian. +""" function truncate_clustered_hamiltonian_2site_BP( cl_h::LabelledGraph{S, T}, beliefs::Dict, @@ -58,11 +102,20 @@ function truncate_clustered_hamiltonian_2site_BP( truncate_clustered_hamiltonian(cl_h, states) end -function select_numstate_best(E, sx, num_states) - # truncate based on energy in two nodes of factor graph; - # resulting states are a product of states in two nodes, - # so we have to fine-tune to end up with expected number of states +""" +Select a specified number of best states based on energy. +This function selects a specified number of best states from a list of energies based on energy values in two nodes of clustered hamiltonian. It fine-tunes the selection to ensure that the resulting states have the expected number. + +# Arguments: +- `E::Vector{Real}`: A vector of energy values. +- `sx::Int`: The size of the factor graph for one of the nodes. +- `num_states::Int`: The desired number of states to select. + +# Returns: +- `Tuple{Vector{Int}, Vector{Int}}`: A tuple containing two vectors of indices, `ind1` and `ind2`, which represent the selected states for two nodes of a factor graph. +""" +function select_numstate_best(E, sx, num_states) low, high = 1, min(num_states, length(E)) while true