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

Dimensional check for graph #7

Merged
merged 4 commits into from Oct 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Expand Up @@ -4,13 +4,15 @@ authors = ["Yueh-Hua Tu <a504082002@gmail.com>"]
version = "0.1.1"

[deps]
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
GraphLaplacians = "a1251efa-393a-423f-9d7b-faaecba535dc"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[compat]
FillArrays = "0.9"
GraphLaplacians = "0.1"
LightGraphs = "1.3"
SimpleWeightedGraphs = "1.1"
Expand Down
3 changes: 2 additions & 1 deletion src/GraphSignals.jl
@@ -1,7 +1,8 @@
module GraphSignals

using LinearAlgebra: issymmetric
using LinearAlgebra: issymmetric, diag, diagm

using FillArrays
using GraphLaplacians
using LightGraphs
using LightGraphs: AbstractSimpleGraph, outneighbors
Expand Down
86 changes: 68 additions & 18 deletions src/featuredgraph.jl
Expand Up @@ -34,48 +34,98 @@ mutable struct FeaturedGraph{T,S<:AbstractMatrix,R<:AbstractMatrix,Q<:AbstractVe
end
end

FeaturedGraph() = FeaturedGraph(zeros(0,0), zeros(0,0), zeros(0,0), zeros(0), zeros(0,0))
FeaturedGraph() = FeaturedGraph(Fill(0., (0,0)), Fill(0., (0,0)), Fill(0., (0,0)),
Fill(0., 0), Fill(0., (0,0)))

function FeaturedGraph(graph)
T = eltype(graph)
N = nv(graph)
mask = zeros(T, N, N)
FeaturedGraph(graph, zeros(0,0), zeros(0,0), zeros(0), mask)
E = ne(graph)

nf = Fill(zero(T), (0, N))
ef = Fill(zero(T), (0, E))
gf = Fill(zero(T), 0)
mask = Fill(zero(T), (N, N))
FeaturedGraph(graph, nf, ef, gf, mask)
end

function FeaturedGraph(graph::T) where {T<:AbstractMatrix}
z = zero(eltype(graph))
nf = similar(graph,0,0).*z
ef = similar(graph,0,0).*z
gf = similar(graph,0).*z
mask = similar(graph,size(graph)...).*z
N = nv(graph)
E = ne(graph)

nf = Fill(z, (0, N))
ef = Fill(z, (0, E))
gf = Fill(z, 0)
mask = Fill(z, (N, N))
FeaturedGraph(graph, nf, ef, gf, mask)
end

function FeaturedGraph(graph, nf::S) where {S<:AbstractMatrix}
z = zero(eltype(nf))
ef = similar(nf,0,0).*z
gf = similar(nf,0).*z
mask = similar(graph,size(graph)...).*z
N = nv(graph)
E = ne(graph)
check_num_node(N, nf)

ef = Fill(z, (0, E))
gf = Fill(z, 0)
mask = Fill(z, (N, N))
FeaturedGraph(graph, nf, ef, gf, mask)
end

function FeaturedGraph(graph::T, nf::S) where {T<:AbstractMatrix,S<:AbstractMatrix}
z = zero(eltype(nf))
N = nv(graph)
E = ne(graph)
check_num_node(N, nf)

graph = convert(typeof(nf), graph)
ef = similar(nf,0,0).*z
gf = similar(nf,0).*z
mask = similar(graph,size(graph)...).*z
ef = Fill(z, (0, E))
gf = Fill(z, 0)
mask = Fill(z, (N, N))
FeaturedGraph(graph, nf, ef, gf, mask)
end

function FeaturedGraph(graph::T, nf::S, ef::R, gf::Q) where {T,S<:AbstractMatrix,R<:AbstractMatrix,Q<:AbstractVector}
ET = eltype(graph)
N = nv(graph)
mask = zeros(ET, N, N)
E = ne(graph)
check_num_node(N, nf)
check_num_node(E, ef)

mask = Fill(zero(ET), (N, N))
FeaturedGraph(graph, nf, ef, gf, mask)
end

function check_num_node(nv::Real, nf)
N = size(nf, 2)
if nv != N
throw(DimensionMismatch("number of nodes must match between graph ($nv) and node features ($N)"))
end
end

function check_num_edge(ne::Real, ef)
E = size(ef, 2)
if ne != E
throw(DimensionMismatch("number of nodes must match between graph ($ne) and edge features ($E)"))
end
end

check_num_node(g, nf) = check_num_node(nv(g), nf)
check_num_edge(g, ef) = check_num_edge(ne(g), ef)

function Base.setproperty!(fg::FeaturedGraph, prop::Symbol, x)
if prop == :graph
check_num_node(x, fg.nf)
check_num_edge(x, fg.ef)
elseif prop == :nf
check_num_node(fg.graph, x)
elseif prop == :ef
check_num_edge(fg.graph, x)
end
setfield!(fg, prop, x)
end

"""
graph(::AbstractFeaturedGraph)

Expand Down Expand Up @@ -112,13 +162,13 @@ mask(::NullGraph) = nothing
mask(fg::FeaturedGraph) = fg.mask

has_graph(::NullGraph) = false
has_graph(fg::FeaturedGraph) = fg.graph != zeros(0,0)
has_graph(fg::FeaturedGraph) = fg.graph != Fill(0., (0,0))

has_node_feature(::NullGraph) = false
has_node_feature(fg::FeaturedGraph) = fg.nf != zeros(0,0)
has_node_feature(fg::FeaturedGraph) = !isempty(fg.nf)

has_edge_feature(::NullGraph) = false
has_edge_feature(fg::FeaturedGraph) = fg.ef != zeros(0,0)
has_edge_feature(fg::FeaturedGraph) = !isempty(fg.ef)

has_global_feature(::NullGraph) = false
has_global_feature(fg::FeaturedGraph) = fg.gf != zeros(0)
has_global_feature(fg::FeaturedGraph) = !isempty(fg.gf)
17 changes: 17 additions & 0 deletions src/graph.jl
Expand Up @@ -46,6 +46,23 @@ ne(::NullGraph) = 0
ne(fg::FeaturedGraph) = ne(graph(fg))
ne(fg::FeaturedGraph{T}) where {T<:AbstractMatrix} = sum(graph(fg) .!= zero(eltype(T)))
ne(fg::FeaturedGraph{T}) where {T<:AbstractVector} = sum(map(length, graph(fg)))÷2
function ne(g::AbstractMatrix; self_loop::Bool=false)
g = g .!= 0

if issymmetric(g)
if self_loop
return div(sum(g + diagm(diag(g))), 2)
else
return div(sum(g - diagm(diag(g))), 2)
end
else
if self_loop
return sum(g)
else
return sum(g - diagm(diag(g)))
end
end
end

"""
fetch_graph(g1, g2)
Expand Down
35 changes: 27 additions & 8 deletions test/featuredgraph.jl
@@ -1,10 +1,15 @@
N = 4
adj = [0 1 0 1;
E = 5
adj = [0 1 1 1;
1 0 1 0;
0 1 0 1;
1 1 0 1;
1 0 1 0]
nf = rand(3, 4)
ef = rand(5, 6)
adj2 = [0 1 0 1;
1 0 1 1;
0 1 0 1;
1 1 1 0]
nf = rand(3, N)
ef = rand(5, E)
gf = rand(7)


Expand All @@ -27,9 +32,9 @@ gf = rand(7)
@test has_edge_feature(fg) == false
@test has_global_feature(fg) == false
@test graph(fg) == adj
@test node_feature(fg) == zeros(0,0)
@test edge_feature(fg) == zeros(0,0)
@test global_feature(fg) == zeros(0)
@test node_feature(fg) == Fill(0, (0, N))
@test edge_feature(fg) == Fill(0, (0, E))
@test global_feature(fg) == Fill(0, 0)
@test mask(fg) == zeros(N, N)


Expand All @@ -40,7 +45,7 @@ gf = rand(7)
@test has_global_feature(fg) == false
@test graph(fg) == adj
@test node_feature(fg) == nf
@test edge_feature(fg) == zeros(0,0)
@test edge_feature(fg) == Fill(0., (0, E))
@test global_feature(fg) == zeros(0)
@test mask(fg) == zeros(N, N)

Expand All @@ -55,4 +60,18 @@ gf = rand(7)
@test edge_feature(fg) == ef
@test global_feature(fg) == gf
@test mask(fg) == zeros(N, N)

fg.graph = adj2
@test fg.graph == adj2
@test_throws DimensionMismatch fg.graph = [0 1; 0 1]

nf_ = rand(10, N)
fg.nf = nf_
@test fg.nf == nf_
@test_throws DimensionMismatch fg.nf = rand(10, 10)

ef_ = rand(10, E)
fg.ef = ef_
@test fg.ef == ef_
@test_throws DimensionMismatch fg.ef = rand(10, 10)
end
13 changes: 11 additions & 2 deletions test/graph.jl
@@ -1,12 +1,17 @@
adj1 = [0 1 0 1;
adj1 = [0 1 0 1; # symmetric
1 0 1 0;
0 1 0 1;
1 0 1 0]
adj2 = [1 1 0 1 0;
adj2 = [1 1 0 1 0; # asymmetric
1 1 1 0 0;
0 1 1 1 1;
1 0 1 1 0;
1 0 1 0 1]
adj3 = [1 1 0 1 0; # symmetric
1 1 1 0 0;
0 1 1 1 1;
1 0 1 1 0;
0 0 1 0 1]
adjl = [[2, 4], [1, 3], [2, 4], [1, 3]]

@testset "graph" begin
Expand All @@ -26,6 +31,10 @@ adjl = [[2, 4], [1, 3], [2, 4], [1, 3]]
@test ne(ng) == 0
@test ne(fg1) == 8
@test ne(fg2) == 16
@test ne(adj2) == 11
@test ne(adj2, self_loop=true) == 16
@test ne(adj3) == 5
@test ne(adj3, self_loop=true) == 10

@test fetch_graph(ng, fg1) == adj1
@test fetch_graph(fg1, ng) == adj1
Expand Down
1 change: 1 addition & 0 deletions test/runtests.jl
@@ -1,5 +1,6 @@
using GraphSignals
using CUDA
using FillArrays
using GraphLaplacians
using LightGraphs
using SimpleWeightedGraphs
Expand Down