Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for vertex and hyperedge values #12

Merged
merged 5 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ Hypergraph
Manipulating vertices and hyperedges
------------------------------------
```@docs
add_hyperedge!(::Hypergraph{T};::Dict{Int,T}) where T <: Real
add_vertex!(::Hypergraph{T};::Dict{Int,T}) where T <: Real

add_hyperedge!(::Hypergraph{T, V, E}; ::Dict{Int,T}, ::Union{UndefInitializer, E}) where {T <: Real, V, E}
add_vertex!(::Hypergraph{T, V, E};::Dict{Int,T},::Union{UndefInitializer,V}) where {T <: Real, V, E}
set_vertex_value!(::Hypergraph{T, V, E}, ::V, ::Int) where {T <: Real, V, E}
get_vertex_value(::Hypergraph{T, V, E}, ::Int) where {T <: Real, V, E}
set_hyperedge_value!(::Hypergraph{T, V, E}, ::E, ::Int) where {T <: Real, V, E}
get_hyperedge_value(::Hypergraph{T, V, E}, ::Int) where {T <: Real, V, E}
```

Hypergraph array getters and setters
Expand All @@ -33,7 +38,7 @@ h

# output

2×3 Hypergraph{Int64}:
2×3 Hypergraph{Int64,Nothing,Nothing}:
nothing 5 5
nothing nothing nothing
```
Expand Down
2 changes: 2 additions & 0 deletions src/SimpleHypergraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ using LightGraphs

export Hypergraph, getvertices, gethyperedges, hg_load, hg_save
export add_vertex!, add_hyperedge!
export set_vertex_value!, get_vertex_value
export set_hyperedge_value!, get_hyperedge_value
export BipartiteView, shortest_path
export TwoSectionView

Expand Down
115 changes: 100 additions & 15 deletions src/hypergraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,61 @@ A hypergraph storing information about vertices and hyperedges.
**Constructors**

Hypergraph{T}(n,k) where {T<:Real}
Hypergraph{T,V}(n, k) where {T<:Real, V}
Hypergraph{T,V,E}(n, k) where {T<:Real, V, E}

Construct a hypergraph with a given number of vertices and hyperedges.
Optionally, values of type `V` can be stored at vertices and values of type `E`
can be stored at hyperedges.

Hypergraph(m::AbstractMatrix{T}) where {T<:Real}
Hypergraph(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real}
Hypergraph{V}(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real, V}
Hypergraph{V, E}(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real, V, E}

Construct a hypergraph using its matrix representation.
In the matrix representation rows are vertices and columns are hyperedges.
Optionally, values of type `V` can be stored at vertices and values of type `E`
can be stored at hyperedges.

**Arguments**

* `T` : type of values stored in the hypergraph
* `T` : type of weight values stored in the hypergraph
* `V` : type of values stored in the vertices of the hypergraph
* `E` : type of values stored in the edges of the hypergraph
* `n` : number of vertices
* `k` : number of hyperedges
* `m` : a matrix representation rows are vertices and columns are hyperedges

"""
struct Hypergraph{T} <: AbstractMatrix{Union{T, Nothing}}
struct Hypergraph{T,V,E} <: AbstractMatrix{Union{T, Nothing}}
v2he::Vector{Dict{Int,T}}
he2v::Vector{Dict{Int,T}}
Hypergraph{T}(n, k) where {T<:Real} =
new([Dict{Int,T}() for i in 1:n], [Dict{Int,T}() for i in 1:k])
vs::Vector{V}
pszufe marked this conversation as resolved.
Show resolved Hide resolved
hes::Vector{E}
pszufe marked this conversation as resolved.
Show resolved Hide resolved
Hypergraph{T,V,E}(n, k) where {T<:Real, V, E} =
new{T,V,E}([Dict{Int,T}() for i in 1:n], [Dict{Int,T}() for i in 1:k],Vector{V}(undef,n),Vector{E}(undef,k))
pszufe marked this conversation as resolved.
Show resolved Hide resolved
end

function Hypergraph(m::AbstractMatrix{T}) where {T<:Real}
h = Hypergraph{T}(size(m)...)
Hypergraph{T,V}(n, k) where {T<:Real, V} = Hypergraph{T,V,Nothing}(n, k)
Hypergraph{T}(n, k) where {T<:Real} = Hypergraph{T,Nothing,Nothing}(n, k)


function Hypergraph{V, E}(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real, V, E}
n, k = size(m)
h = Hypergraph{T, V, E}(n,k)
h .= m
h
end

function Hypergraph{V}(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real, V}
Hypergraph{V, Nothing}(m)
end

function Hypergraph(m::AbstractMatrix{Union{T, Nothing}}) where {T<:Real}
Hypergraph{Nothing, Nothing}(m)
end


"""
Base.size(h::Hypergraph)

Expand Down Expand Up @@ -72,8 +98,8 @@ Removes a vertex from a given hyperedge for a hypergraph `h` and a given vertex-
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

so maybe add a comment that trying to remove a vertex from a hyperedge when it is not present is not an error, earlier we wanted it to throw an error.

@inline function Base.setindex!(h::Hypergraph, ::Nothing, idx::Vararg{Int,2})
@boundscheck checkbounds(h, idx...)
pop!(h.v2he[idx[1]], idx[2])
pop!(h.he2v[idx[2]], idx[1])
pop!(h.v2he[idx[1]], idx[2], nothing)
pop!(h.he2v[idx[2]], idx[1], nothing)
h
end

Expand Down Expand Up @@ -108,41 +134,100 @@ Returns hyperedges for a given vertex `v_id` in a hypergraph `h`.
@inline gethyperedges(h::Hypergraph, v_id::Int) = h.v2he[v_id]

"""
add_vertex!(h::Hypergraph{T};hyperedges::Dict{Int,T} = Dict{Int,T}()) where T <: Real
add_vertex!(h::Hypergraph{T, V, E}; hyperedges::Dict{Int,T} = Dict{Int,T}(), vertex_val::Union{UndefInitializer, V} = undef) where {T <: Real, V, E}

Adds a vertex to a given hypergraph `h`. Optionally, the vertex can be added
to existing hyperedges. The `hyperedges` parameter presents a dictionary
of hyperedge identifiers and values stored at the hyperedges
of hyperedge identifiers and values stored at the hyperedges.
Additionally, a value can be stored with the vertex using the `vertex_val` parameter.
pszufe marked this conversation as resolved.
Show resolved Hide resolved

"""
function add_vertex!(h::Hypergraph{T};hyperedges::Dict{Int,T} = Dict{Int,T}()) where T <: Real
function add_vertex!(h::Hypergraph{T, V, E}; hyperedges::Dict{Int,T} = Dict{Int,T}(), vertex_val::Union{UndefInitializer, V} = undef) where {T <: Real, V, E}
pszufe marked this conversation as resolved.
Show resolved Hide resolved
@boundscheck (checkbounds(h,1,k) for k in keys(hyperedges))
push!(h.v2he,hyperedges)
ix = length(h.v2he)
for k in keys(hyperedges)
h[ix,k]=hyperedges[k]
end
if vertex_val == undef
resize!(h.vs, length(h.vs)+1)
else
push!(h.vs, vertex_val)
pszufe marked this conversation as resolved.
Show resolved Hide resolved
end
ix
end

"""
add_hyperedge!(h::Hypergraph{T};vertices::Dict{Int,T} = Dict{Int,T}()) where T <: Real
add_hyperedge!(h::Hypergraph{T, V, E}; vertices::Dict{Int,T} = Dict{Int,T}(), hyperedge_val::Union{UndefInitializer, E}=undef) where {T <: Real, V, E}
pszufe marked this conversation as resolved.
Show resolved Hide resolved

Adds a hyperedge to a given hypergraph `h`. Optionally, existing vertices can be added
to the created hyperedge. The paramater `vertices` represents a dictionary
of vertex identifiers and values stored at the hyperedges
of vertex identifiers and values stored at the hyperedges.
Additionally, a value can be stored with the hyperedge using the `hyperedge_val` parameter.

"""
function add_hyperedge!(h::Hypergraph{T};vertices::Dict{Int,T} = Dict{Int,T}()) where T <: Real
function add_hyperedge!(h::Hypergraph{T, V, E}; vertices::Dict{Int,T} = Dict{Int,T}(), hyperedge_val::Union{UndefInitializer, E}=undef) where {T <: Real, V, E}
@boundscheck (checkbounds(h,k,1) for k in keys(vertices))
push!(h.he2v,vertices)
ix = length(h.he2v)
for k in keys(vertices)
h[k,ix]=vertices[k]
end
if hyperedge_val == undef
resize!(h.hes, length(h.hes)+1)
else
push!(h.hes, hyperedge_val)
end
ix
end

"""
set_vertex_value!(h::Hypergraph{T, V, E}, new_value::V, id::Int) where {T <: Real, V, E}

Sets a new value `new_value` for the vertex `id` in the hypegraph `h`.

"""
function set_vertex_value!(h::Hypergraph{T, V, E}, new_value::V, id::Int) where {T <: Real, V, E}
@boundscheck checkbounds(h.vs, id)
pszufe marked this conversation as resolved.
Show resolved Hide resolved
h.vs[id] = new_value
h.vs
end

"""
get_vertex_value(h::Hypergraph{T, V, E}, id::Int) where {T <: Real, V, E}

Returns a value stored at the vertex `id` in the hypergraph `h`.

"""
function get_vertex_value(h::Hypergraph{T, V, E}, id::Int) where {T <: Real, V, E}
@boundscheck checkbounds(h.vs, id)
pszufe marked this conversation as resolved.
Show resolved Hide resolved
h.vs[id]
end

"""
set_hyperedge_value!(h::Hypergraph{T, V, E}, new_value::E, id::Int) where {T <: Real, V, E}

Sets a new value `new_value` for the hyperedge `id` in the hypegraph `h`.

"""
function set_hyperedge_value!(h::Hypergraph{T, V, E}, new_value::E, id::Int) where {T <: Real, V, E}
@boundscheck checkbounds(h.hes, id)
pszufe marked this conversation as resolved.
Show resolved Hide resolved
h.hes[id] = new_value
h.hes
end

"""
get_hyperedge_value(h::Hypergraph{T, V, E}, id::Int) where {T <: Real, V, E}

Returns a value stored at the hyperedge `id` in the hypergraph `h`.

"""
function get_hyperedge_value(h::Hypergraph{T, V, E}, id::Int) where {T <: Real, V, E}
@boundscheck checkbounds(h.hes, id)
pszufe marked this conversation as resolved.
Show resolved Hide resolved
h.hes[id]
end


# TODO needs remove_vertex!(h::Hypergraph{T}, v_id::Int)

# TODO needs add_hyperedge!(h::Hypergraph{T}, he_id::Int)
Expand Down
136 changes: 82 additions & 54 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
using Test, SimpleHypergraphs
import LightGraphs

h = hg_load("data/test1.hgf", Int)
@test size(h) == (4, 4)
m = Matrix(h)
@test m == h
@test h == [1 nothing 4 nothing
2 3 nothing nothing
nothing nothing 5 nothing
nothing nothing 6 nothing]
mktemp("data") do path, _
println(path)
hg_save(path, h)
@test read(path, String) == replace(read("data/test1.hgf", String), "\r\n" => "\n")
end

h1 = Hypergraph{Float64}(5,4)
h1[1:3,1] .= 1.5
Expand All @@ -23,63 +10,104 @@ h1[4,3:4] .= 4.5
h1[5,4] = 5.5
h1[5,2] = 6.5

h2 = Hypergraph{Float64}(0,0)
for i in 1:4 add_vertex!(h2) end
add_hyperedge!(h2;vertices=Dict(1:3 .=> 1.5))
add_hyperedge!(h2)
add_vertex!(h2;hyperedges=Dict(2=>6.5))
add_hyperedge!(h2;vertices=Dict(2 => 3.5, 4 => 4.5))
add_hyperedge!(h2;vertices=Dict(3:5 .=> (2.5,4.5,5.5)))
@test h1 == h2

@testset "SimpleHypergraphs Hypergraph " begin

h = hg_load("data/test1.hgf", Int)
@test size(h) == (4, 4)
m = Matrix(h)
@test m == h
@test h == [1 nothing 4 nothing
2 3 nothing nothing
nothing nothing 5 nothing
nothing nothing 6 nothing]
mktemp("data") do path, _
println(path)
hg_save(path, h)
@test read(path, String) == replace(read("data/test1.hgf", String), "\r\n" => "\n")
end

b = BipartiteView(h1)
h2 = Hypergraph{Float64}(0,0)
for i in 1:4 add_vertex!(h2) end
add_hyperedge!(h2;vertices=Dict(1:3 .=> 1.5))
add_hyperedge!(h2)
add_vertex!(h2;hyperedges=Dict(2=>6.5))
add_hyperedge!(h2;vertices=Dict(2 => 3.5, 4 => 4.5))
add_hyperedge!(h2;vertices=Dict(3:5 .=> (2.5,4.5,5.5)))
@test h1 == h2
m = Matrix(h1)
@test m == Matrix(h2)
@test h1 == Hypergraph(m)
@test h1 == Hypergraph{Nothing}(m)
@test h1 == Hypergraph{Nothing, Nothing}(m)
@test getindex(h1,3,1) == 1.5

@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(b))) == 18
h3 = Hypergraph{Float64,String,Nothing}(1,1)
@test add_vertex!(h3,vertex_val="test") == 2
@test set_vertex_value!(h3,"t",1) == ["t","test"]
@test get_vertex_value(h3,2) == "test"
@test get_hyperedge_value(h3,1) == nothing
@test_throws BoundsError get_hyperedge_value(h3,2)

@test sort(collect(LightGraphs.outneighbors(b,5))) == [7,9]
@test sort(collect(LightGraphs.outneighbors(b,1))) == [6]
@test sort(collect(LightGraphs.inneighbors(b,9))) == [3,4,5]
h4 = Hypergraph{Float64,Nothing,String}(1,1)
@test add_hyperedge!(h4,hyperedge_val="test") == 2
@test set_hyperedge_value!(h4,"t",1) == ["t","test"]
@test get_hyperedge_value(h4,2) == "test"
@test get_vertex_value(h4,1) == nothing
@test_throws BoundsError get_vertex_value(h4,2)
end;

@test Set(LightGraphs.vertices(b)) == Set(1:LightGraphs.nv(b))
@testset "SimpleHypergraphs BipartiteView " begin
b = BipartiteView(h1)

@test shortest_path(b,1,5) == [1,3,5]
@test LightGraphs.is_weakly_connected(b) == true
@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(b))) == 18

@test SimpleHypergraphs.add_vertex!(h1) == 6
@test add_hyperedge!(h1) == 5
h1[5,5] = 1
h1[6,5] = 1
@test sort(collect(LightGraphs.outneighbors(b,5))) == [7,9]
@test sort(collect(LightGraphs.outneighbors(b,1))) == [6]
@test sort(collect(LightGraphs.inneighbors(b,9))) == [3,4,5]

@test shortest_path(b,1,6) == [1,3,5,6]
@test Set(LightGraphs.vertices(b)) == Set(1:LightGraphs.nv(b))

@test shortest_path(b,1,5) == [1,3,5]
@test LightGraphs.is_weakly_connected(b) == true

#TwoSectionView test
t = TwoSectionView(h1)
@test SimpleHypergraphs.add_vertex!(h1) == 6
@test add_hyperedge!(h1) == 5
h1[5,5] = 1
h1[6,5] = 1

@test LightGraphs.nv(t) == 6
@test LightGraphs.ne(t) == 8
@test shortest_path(b,1,6) == [1,3,5,6]

@test sort(LightGraphs.all_neighbors(t, 1)) == [2,3]
@test sort(LightGraphs.outneighbors(t, 5)) == [3,4,6]
@test sort(LightGraphs.inneighbors(t, 4)) == [2,3,5]
@inferred LightGraphs.all_neighbors(t, 1)
end;

@test LightGraphs.has_edge(t, 1, 2) == true
@test LightGraphs.has_edge(t, 1, 5) == false
@testset "SimpleHypergraphs TwoSectionView" begin

@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(t))) == 16
@test shortest_path(t,1,5) == [1,3,5]
@test LightGraphs.is_weakly_connected(t) == true
t = TwoSectionView(h1)

@test SimpleHypergraphs.add_vertex!(h1) == 7
h1[7,5] = 1
@test LightGraphs.nv(t) == 6
@test LightGraphs.ne(t) == 8

@test shortest_path(t,1,6) == [1,3,5,6]
@test sort(LightGraphs.all_neighbors(t, 1)) == [2,3]
@test sort(LightGraphs.outneighbors(t, 5)) == [3,4,6]
@test sort(LightGraphs.inneighbors(t, 4)) == [2,3,5]
@inferred LightGraphs.all_neighbors(t, 1)

@test LightGraphs.nv(t) == 7
@test LightGraphs.ne(t) == 10
@test sort(LightGraphs.outneighbors(t, 5)) == [3,4,6,7]
@test LightGraphs.has_edge(t, 1, 2) == true
@test LightGraphs.has_edge(t, 1, 5) == false

@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(t))) == 20
@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(t))) == 16
@test shortest_path(t,1,5) == [1,3,5]
@test LightGraphs.is_weakly_connected(t) == true

@test SimpleHypergraphs.add_vertex!(h1) == 7
h1[7,5] = 1

@test shortest_path(t,1,6) == [1,3,5,6]

@test LightGraphs.nv(t) == 7
@test LightGraphs.ne(t) == 10
@test sort(LightGraphs.outneighbors(t, 5)) == [3,4,6,7]

@test sum(LightGraphs.adjacency_matrix(LightGraphs.SimpleGraph(t))) == 20

end;