Skip to content

Commit

Permalink
Make factor_graph a graph labelled with tuples (#2)
Browse files Browse the repository at this point in the history
* Add LabelledGraphs dependency

* Make factor_grpah indexed with tuples

* Bump version to 0.2.0
  • Loading branch information
dexter2206 authored Mar 24, 2021
1 parent 2ae1352 commit 6223a7a
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 303 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
name = "SpinGlassNetworks"
uuid = "b7f6bd3e-55dc-4da6-96a9-ef9dbec6ac19"
authors = ["Krzysztof Domino <[email protected]>", "Anna Maria Dziubyna <[email protected]>", "Bartłomiej Gardas <[email protected]>", "Konrad Jałowiecki <[email protected]>", "Łukasz Pawela <[email protected]>", "Marek M. Rams <[email protected]>"]
version = "0.1.0"
version = "0.2.0"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
LabelledGraphs = "605abd48-4d17-4660-b914-d4df33194460"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"

[compat]
CSV = "0.8"
DocStringExtensions = "0.8"
LabelledGraphs = "0.3"
LightGraphs = "1.3"
MetaGraphs = "0.6"
julia = "1.5"
Expand Down
25 changes: 12 additions & 13 deletions src/SpinGlassNetworks.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module SpinGlassNetworks
using LabelledGraphs
using LightGraphs
using MetaGraphs
using MetaGraphs # TODO: remove that
using CSV
using DocStringExtensions
using LinearAlgebra
Expand All @@ -9,31 +10,29 @@ module SpinGlassNetworks
import Base.Prehashed

export unique_neighbors

function unique_neighbors(ig::MetaGraph, i::Int)
nbrs = neighbors(ig::MetaGraph, i::Int)
filter(j -> j > i, nbrs)
end


unique_neighbors(ig::LabelledGraph, i::Int) = filter(j -> j > i, neighbors(ig, i))

@generated function unique_dims(A::AbstractArray{T,N}, dim::Integer) where {T,N}
quote
1 <= dim <= $N || return copy(A)
hashes = zeros(UInt, axes(A, dim))

# Compute hash for each row
k = 0
@nloops $N i A d->(if d == dim; k = i_d; end) begin
@inbounds hashes[k] = hash(hashes[k], hash((@nref $N A i)))
end

# Collect index of first row for each hash
uniquerow = similar(Array{Int}, axes(A, dim))
firstrow = Dict{Prehashed,Int}()
for k = axes(A, dim)
uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k)
end
uniquerows = collect(values(firstrow))

# Check for collisions
collided = falses(axes(A, dim))
@inbounds begin
Expand All @@ -48,7 +47,7 @@ module SpinGlassNetworks
end
end
end

if any(collided)
nowcollided = similar(BitArray, axes(A, dim))
while any(collided)
Expand All @@ -61,7 +60,7 @@ module SpinGlassNetworks
for v values(firstrow)
push!(uniquerows, v)
end

# Check for collisions
fill!(nowcollided, false)
@nloops $N i A d->begin
Expand All @@ -80,14 +79,14 @@ module SpinGlassNetworks
(collided, nowcollided) = (nowcollided, collided)
end
end

(@nref $N A d->d == dim ? sort!(uniquerows) : (axes(A, d))), indexin(uniquerow, uniquerows)
end
end

include("states.jl")
include("ising.jl")
include("spectrum.jl")
include("lattice.jl")
include("ising.jl")
include("factor.jl")
end # module
71 changes: 32 additions & 39 deletions src/factor.jl
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
export factor_graph, rank_reveal, projectors, split_into_clusters


function split_into_clusters(vertices, assignment_rule)
# TODO: check how to do this in functional-style
clusters = Dict(
i => [] for i in values(assignment_rule)
)
for v in vertices
push!(clusters[assignment_rule[v]], v)
end
clusters
end

function split_into_clusters(ig::MetaGraph, assignment_rule)
function split_into_clusters(ig::LabelledGraph{S, T}, assignment_rule) where {S, T}
cluster_id_to_verts = Dict(
i => Int[] for i in values(assignment_rule)
i => T[] for i in values(assignment_rule)
)

for (i, v) in enumerate(nodes(ig))
push!(cluster_id_to_verts[assignment_rule[v]], i)
for v in vertices(ig)
push!(cluster_id_to_verts[assignment_rule[v]], v)
end

return Dict(
i => cluster(ig, verts) for (i, verts) cluster_id_to_verts
Dict(
i => first(cluster(ig, verts)) for (i, verts) cluster_id_to_verts
)
end


function factor_graph(
ig::MetaGraph,
ig::IsingGraph,
num_states_cl::Int;
spectrum::Function=full_spectrum,
cluster_assignment_rule::Dict{Int, Int} # e.g. square lattice
)
cluster_assignment_rule::Dict{Int, T} # e.g. square lattice
) where {T}
ns = Dict(i => num_states_cl for i Set(values(cluster_assignment_rule)))
factor_graph(
ig,
Expand All @@ -42,44 +32,47 @@ function factor_graph(
end

function factor_graph(
ig::MetaGraph,
num_states_cl::Dict{Int, Int}=Dict{Int, Int}();
ig::IsingGraph,
num_states_cl::Dict{T, Int};
spectrum::Function=full_spectrum,
cluster_assignment_rule::Dict{Int, Int} # e.g. square lattice
)
cluster_assignment_rule::Dict{Int, T} # e.g. square lattice
) where {T}
L = maximum(values(cluster_assignment_rule))
fg = MetaDiGraph(L)
fg = LabelledGraph{MetaDiGraph}(sort(unique(values(cluster_assignment_rule))))

for (v, cl) split_into_clusters(ig, cluster_assignment_rule)
set_prop!(fg, v, :cluster, cl)
sp = spectrum(cl, num_states=get(num_states_cl, v, basis_size(cl)))
set_prop!(fg, v, :spectrum, sp)
set_prop!(fg, v, :loc_en, vec(sp.energies))
set_prop!(fg, v, :loc_dim, length(vec(sp.energies)))
set_props!(fg, v, Dict(:cluster => cl, :spectrum => sp))
end

for i 1:L, j i+1:L
v, w = get_prop(fg, i, :cluster), get_prop(fg, j, :cluster)
for (i, v) enumerate(vertices(fg)), w vertices(fg)[i+1:end]
cl1, cl2 = get_prop(fg, v, :cluster), get_prop(fg, w, :cluster)

outer_edges, J = inter_cluster_edges(ig, v, w)
outer_edges, J = inter_cluster_edges(ig, cl1, cl2)

if !isempty(outer_edges)
en = inter_cluster_energy(
get_prop(fg, i, :spectrum).states, J, get_prop(fg, j, :spectrum).states
get_prop(fg, v, :spectrum).states, J, get_prop(fg, w, :spectrum).states
)

pl, en = rank_reveal(en, :PE)
en, pr = rank_reveal(en, :EP)

add_edge!(
fg, i, j,
Dict(:outer_edges => outer_edges, :pl => pl, :en => en, :pr => pr)
add_edge!(fg, v, w)
set_props!(
fg, v, w, Dict(:outer_edges => outer_edges, :pl => pl, :en => en, :pr => pr)
)
end
end
fg
end

function factor_graph(
ig::IsingGraph;
spectrum::Function=full_spectrum,
cluster_assignment_rule::Dict{Int, T}
) where {T}
factor_graph(ig, Dict{T, Int}(), spectrum=spectrum, cluster_assignment_rule=cluster_assignment_rule)
end

function rank_reveal(energy, order=:PE)
@assert order (:PE, :EP)
dim = order == :PE ? 1 : 2
Expand All @@ -97,4 +90,4 @@ function rank_reveal(energy, order=:PE)
end

order == :PE ? (P, E) : (E, P)
end
end
81 changes: 39 additions & 42 deletions src/ising.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
export ising_graph
export rank_vec
export cluster, rank, nodes, basis_size
using LabelledGraphs

export ising_graph, rank_vec, cluster, rank, nodes, basis_size, biases, couplings, IsingGraph

const Instance = Union{String, Dict}


unique_nodes(ising_tuples) = sort(collect(Set(Iterators.flatten((i, j) for (i, j, _) ising_tuples))))

const IsingGraph = LabelledGraph{MetaGraph{Int64, Float64}, Int64}

"""
$(TYPEDSIGNATURES)
Expand All @@ -17,7 +19,7 @@ Create the Ising spin glass model.
Store extra information
"""
function ising_graph(
instance::Instance,
instance::Instance;
sgn::Number=1.0,
rank_override::Dict{Int, Int}=Dict{Int, Int}()
)
Expand All @@ -28,72 +30,67 @@ function ising_graph(
ising = [ (i, j, J) for ((i, j), J) instance ]
end

original_nodes = unique_nodes(ising)
L = length(original_nodes)
nodes_to_vertices = Dict(w => i for (i, w) enumerate(original_nodes))

ig = MetaGraph(length(original_nodes))
nodes = unique_nodes(ising)
L = length(nodes)
nodes_to_vertices = Dict(w => i for (i, w) enumerate(nodes))

foreach(args -> set_prop!(ig, args[1], :node, args[2]), enumerate(original_nodes))
ig = LabelledGraph{MetaGraph}(nodes)

J = zeros(L, L)
h = zeros(L)
set_prop!.(Ref(ig), vertices(ig), :h, 0)
foreach(v -> set_prop!(ig, v, :rank, get(rank_override, v, 2)), vertices(ig))

# setup the model (J_ij, h_i)
for (_i, _j, v) ising
i, j = nodes_to_vertices[_i], nodes_to_vertices[_j]
for (i, j, v) ising
v *= sgn

if i == j
h[i] = v
set_prop!(ig, i, :h, v)
else
add_edge!(ig, i, j, :J, v) || throw(ArgumentError("Duplicate Egde ($i, $j)"))
J[i, j] = v
add_edge!(ig, i, j) || throw(ArgumentError("Duplicate Egde ($i, $j)"))
set_prop!(ig, i, j, :J, v)
end
end

foreach(i -> set_prop!(ig, i, :h, h[i]), vertices(ig))

set_prop!(
ig,
:rank,
Dict{Int, Int}(
v => get(rank_override, w, 2) for (w, v) in nodes_to_vertices
v => get(rank_override, v, 2) for v in vertices(ig)
)
)

set_prop!(ig, :J, J)
set_prop!(ig, :h, h)
set_prop!(ig, :nodes_map, nodes_to_vertices)
ig
end

nodes(ig::MetaGraph) = collect(get_prop.(Ref(ig), vertices(ig), :node))
rank_vec(ig::MetaGraph) = collect(values(get_prop(ig, :rank)))
basis_size(ig::MetaGraph) = prod(prod(rank_vec(ig)))
rank_vec(ig::IsingGraph) = Int[get_prop((ig), v, :rank) for v vertices(ig)]
basis_size(ig::IsingGraph) = prod(prod(rank_vec(ig)))
biases(ig::IsingGraph) = get_prop.(Ref(ig), vertices(ig), :h)

function cluster(ig::MetaGraph, verts)
sub_ig, vmap = induced_subgraph(ig, collect(verts))
function couplings(ig::IsingGraph)
J = zeros(nv(ig), nv(ig))
for edge in edges(ig)
i, j = ig.reverse_label_map[src(edge)], ig.reverse_label_map[dst(edge)]
J[i, j] = get_prop(ig, edge, :J)
end
J
end

h = get_prop.(Ref(sub_ig), vertices(sub_ig), :h)
rank = getindex.(Ref(get_prop(ig, :rank)), vmap)
J = get_prop(ig, :J)[vmap, vmap]
cluster(ig::IsingGraph, verts) = induced_subgraph(ig, collect(verts))

set_props!(sub_ig, Dict(:rank => rank, :J => J, :h => h, :vmap => vmap))
sub_ig
end
function inter_cluster_edges(ig::IsingGraph, cl1::IsingGraph, cl2::IsingGraph)
verts1, verts2 = vertices(cl1), vertices(cl2)

outer_edges = [
LabelledEdge(i, j)
for i vertices(cl1), j vertices(cl2)
if has_edge(ig, i, j)
]

function inter_cluster_edges(ig::MetaGraph, cl1::MetaGraph, cl2::MetaGraph)
verts1, verts2 = get_prop(cl1, :vmap), get_prop(cl2, :vmap)
outer_edges = filter_edges(
ig,
(_, e) -> (src(e) verts1 && dst(e) verts2) ||
(src(e) verts1 && dst(e) verts2)
)
J = zeros(nv(cl1), nv(cl2))
# FIXME: don't use indexin
for e outer_edges
@inbounds J[indexin(src(e), verts1)[1], indexin(dst(e), verts2)[1]] = get_prop(ig, e, :J)
i, j = cl1.reverse_label_map[src(e)], cl2.reverse_label_map[dst(e)]
@inbounds J[i, j] = get_prop(ig, e, :J)
end
outer_edges, J
end
3 changes: 1 addition & 2 deletions src/lattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ export super_square_lattice

function super_square_lattice(size::NTuple{5, Int})
m, um, n, un, t = size
new = LinearIndices((1:n, 1:m))
old = LinearIndices((1:t, 1:un, 1:n, 1:um, 1:m))

Dict(
old[k, uj, j, ui, i] => new[j, i]
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
Expand Down
Loading

2 comments on commit 6223a7a

@dexter2206
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/32742

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.0 -m "<description of version>" 6223a7a9171558fcbc1d752195e4c4423b61a9a9
git push origin v0.2.0

Please sign in to comment.