Skip to content

Commit 5968e4d

Browse files
fixes to degree
1 parent 4ef3014 commit 5968e4d

File tree

3 files changed

+61
-23
lines changed

3 files changed

+61
-23
lines changed

src/GNNGraphs/convert.jl

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ end
6363

6464
to_dense(A::AbstractSparseMatrix, x...; kws...) = to_dense(collect(A), x...; kws...)
6565

66-
function to_dense(A::ADJMAT_T, T::DataType=eltype(A); dir=:out, num_nodes=nothing)
66+
function to_dense(A::ADJMAT_T, T=nothing; dir=:out, num_nodes=nothing)
6767
@assert dir [:out, :in]
68+
T = T === nothing ? eltype(A) : T
6869
num_nodes = size(A, 1)
6970
@assert num_nodes == size(A, 2)
7071
# @assert all(x -> (x == 1) || (x == 0), A)
@@ -78,11 +79,12 @@ function to_dense(A::ADJMAT_T, T::DataType=eltype(A); dir=:out, num_nodes=nothin
7879
return A, num_nodes, num_edges
7980
end
8081

81-
function to_dense(adj_list::ADJLIST_T, T::DataType=Int; dir=:out, num_nodes=nothing)
82+
function to_dense(adj_list::ADJLIST_T, T=nothing; dir=:out, num_nodes=nothing)
8283
@assert dir [:out, :in]
8384
num_nodes = length(adj_list)
8485
num_edges = sum(length.(adj_list))
8586
@assert num_nodes > 0
87+
T = T === nothing ? eltype(adj_list[1]) : T
8688
A = similar(adj_list[1], T, (num_nodes, num_nodes))
8789
if dir == :out
8890
for (i, neigs) in enumerate(adj_list)
@@ -96,13 +98,13 @@ function to_dense(adj_list::ADJLIST_T, T::DataType=Int; dir=:out, num_nodes=noth
9698
A, num_nodes, num_edges
9799
end
98100

99-
function to_dense(coo::COO_T, T::DataType=Nothing; dir=:out, num_nodes=nothing)
101+
function to_dense(coo::COO_T, T=nothing; dir=:out, num_nodes=nothing)
100102
# `dir` will be ignored since the input `coo` is always in source -> target format.
101103
# The output will always be a adjmat in :out format (e.g. A[i,j] denotes from i to j)
102104
s, t, val = coo
103105
n = isnothing(num_nodes) ? max(maximum(s), maximum(t)) : num_nodes
104106
val = isnothing(val) ? eltype(s)(1) : val
105-
T = T == Nothing ? eltype(val) : T
107+
T = T === nothing ? eltype(val) : T
106108
A = fill!(similar(s, T, (n, n)), 0)
107109
v = vec(A)
108110
idxs = s .+ n .* (t .- 1)
@@ -113,10 +115,11 @@ end
113115

114116
### SPARSE #############
115117

116-
function to_sparse(A::ADJMAT_T, T::DataType=eltype(A); dir=:out, num_nodes=nothing)
118+
function to_sparse(A::ADJMAT_T, T=nothing; dir=:out, num_nodes=nothing)
117119
@assert dir [:out, :in]
118120
num_nodes = size(A, 1)
119121
@assert num_nodes == size(A, 2)
122+
T = T === nothing ? eltype(A) : T
120123
num_edges = A isa AbstractSparseMatrix ? nnz(A) : count(!=(0), A)
121124
if dir == :in
122125
A = A'
@@ -130,13 +133,14 @@ function to_sparse(A::ADJMAT_T, T::DataType=eltype(A); dir=:out, num_nodes=nothi
130133
return A, num_nodes, num_edges
131134
end
132135

133-
function to_sparse(adj_list::ADJLIST_T, T::DataType=Int; dir=:out, num_nodes=nothing)
136+
function to_sparse(adj_list::ADJLIST_T, T=nothing; dir=:out, num_nodes=nothing)
134137
coo, num_nodes, num_edges = to_coo(adj_list; dir, num_nodes)
135138
return to_sparse(coo; dir, num_nodes)
136139
end
137140

138-
function to_sparse(coo::COO_T, T::DataType=Int; dir=:out, num_nodes=nothing)
141+
function to_sparse(coo::COO_T, T=nothing; dir=:out, num_nodes=nothing)
139142
s, t, eweight = coo
143+
T = T === nothing ? eltype(s) : T
140144
eweight = isnothing(eweight) ? fill!(similar(s, T), 1) : eweight
141145
num_nodes = isnothing(num_nodes) ? max(maximum(s), maximum(t)) : num_nodes
142146
A = sparse(s, t, eweight, num_nodes, num_nodes)

src/GNNGraphs/query.jl

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ get_edge_weight(g::GNNGraph{<:ADJMAT_T}) = to_coo(g.graph, num_nodes=g.num_nodes
2020
Graphs.edges(g::GNNGraph) = zip(edge_index(g)...)
2121

2222
Graphs.edgetype(g::GNNGraph) = Tuple{Int, Int}
23+
nodetype(g::GNNGraph) = Base.eltype(g)
24+
25+
"""
26+
nodetype(g::GNNGraph)
27+
28+
Type of nodes in `g`,
29+
an integer type like `Int`, `Int32`, `Uint16`, ....
30+
"""
31+
function nodetype(g::GNNGraph{<:COO_T}, T=nothing)
32+
s, t = edge_index(g)
33+
return eltype(s)
34+
end
35+
36+
function nodetype(g::GNNGraph{<:ADJMAT_T}, T=nothing)
37+
T !== nothing && return T
38+
return eltype(g.graph)
39+
end
2340

2441
function Graphs.has_edge(g::GNNGraph{<:COO_T}, i::Integer, j::Integer)
2542
s, t = edge_index(g)
@@ -77,7 +94,7 @@ function adjacency_list(g::GNNGraph; dir=:out)
7794
return [fneighs(g, i) for i in 1:g.num_nodes]
7895
end
7996

80-
function Graphs.adjacency_matrix(g::GNNGraph{<:COO_T}, T::DataType=Int; dir=:out)
97+
function Graphs.adjacency_matrix(g::GNNGraph{<:COO_T}, T::DataType=nodetype(g); dir=:out)
8198
if g.graph[1] isa CuVector
8299
# TODO revisit after https://github.com/JuliaGPU/CUDA.jl/pull/1152
83100
A, n, m = to_dense(g.graph, T, num_nodes=g.num_nodes)
@@ -88,7 +105,7 @@ function Graphs.adjacency_matrix(g::GNNGraph{<:COO_T}, T::DataType=Int; dir=:out
88105
return dir == :out ? A : A'
89106
end
90107

91-
function Graphs.adjacency_matrix(g::GNNGraph{<:ADJMAT_T}, T::DataType=eltype(g.graph); dir=:out)
108+
function Graphs.adjacency_matrix(g::GNNGraph{<:ADJMAT_T}, T::DataType=nodetype(g); dir=:out)
92109
@assert dir [:in, :out]
93110
A = g.graph
94111
A = T != eltype(A) ? T.(A) : A
@@ -125,27 +142,37 @@ function Graphs.degree(g::GNNGraph{<:COO_T}, T=nothing; dir=:out, edge_weight=tr
125142
return degs
126143
end
127144

128-
function Graphs.degree(g::GNNGraph{<:ADJMAT_T}, T=Int; dir=:out, edge_weight=true)
145+
function Graphs.degree(g::GNNGraph{<:ADJMAT_T}, T=nothing; dir=:out, edge_weight=true)
129146
@assert !(edge_weight isa AbstractArray) "passing the edge weights is not support by adjacency matrix representations"
130147
@assert dir (:in, :out, :both)
131-
A = adjacency_matrix(g, T)
148+
if T === nothing
149+
Nt = nodetype(g)
150+
if ((edge_weight === false) || (edge_weight === nothing)) && !(Nt <: Integer)
151+
T = Nt == Float32 ? Int32 :
152+
Nt == Float16 ? Int16 : Int
153+
else
154+
T = Nt
155+
end
156+
end
157+
A = adjacency_matrix(g)
132158
if (edge_weight === false) || (edge_weight === nothing)
133-
A = map(>(0), A)
159+
A = map(x -> x > 0 ? T(1) : T(0), A)
134160
end
161+
A = eltype(A) != T ? T.(A) : A
135162
return dir == :out ? vec(sum(A, dims=2)) :
136163
dir == :in ? vec(sum(A, dims=1)) :
137164
vec(sum(A, dims=1)) .+ vec(sum(A, dims=2))
138165
end
139166

140-
function Graphs.laplacian_matrix(g::GNNGraph, T::DataType=Int; dir::Symbol=:out)
167+
function Graphs.laplacian_matrix(g::GNNGraph, T=nothing; dir::Symbol=:out)
141168
A = adjacency_matrix(g, T; dir=dir)
142169
D = Diagonal(vec(sum(A; dims=2)))
143170
return D - A
144171
end
145172

146173

147174
"""
148-
normalized_laplacian(g, T=Float32; add_self_loops=false, dir=:out)
175+
normalized_laplacian(g, T=nothing; add_self_loops=false, dir=:out)
149176
150177
Normalized Laplacian matrix of graph `g`.
151178
@@ -156,13 +183,13 @@ Normalized Laplacian matrix of graph `g`.
156183
- `add_self_loops`: add self-loops while calculating the matrix.
157184
- `dir`: the edge directionality considered (:out, :in, :both).
158185
"""
159-
function normalized_laplacian(g::GNNGraph, T::DataType=Float32;
186+
function normalized_laplacian(g::GNNGraph, T=nodetype(g);
160187
add_self_loops::Bool=false, dir::Symbol=:out)
161188
= normalized_adjacency(g, T; dir, add_self_loops)
162189
return I -
163190
end
164191

165-
function normalized_adjacency(g::GNNGraph, T::DataType=Float32;
192+
function normalized_adjacency(g::GNNGraph, T=nodetype(g);
166193
add_self_loops::Bool=false, dir::Symbol=:out)
167194
A = adjacency_matrix(g, T; dir=dir)
168195
if add_self_loops
@@ -174,7 +201,7 @@ function normalized_adjacency(g::GNNGraph, T::DataType=Float32;
174201
end
175202

176203
@doc raw"""
177-
scaled_laplacian(g, T=Float32; dir=:out)
204+
scaled_laplacian(g, T=nothing; dir=:out)
178205
179206
Scaled Laplacian matrix of graph `g`,
180207
defined as ``\hat{L} = \frac{2}{\lambda_{max}} L - I`` where ``L`` is the normalized Laplacian matrix.
@@ -185,7 +212,7 @@ defined as ``\hat{L} = \frac{2}{\lambda_{max}} L - I`` where ``L`` is the normal
185212
- `T`: result element type.
186213
- `dir`: the edge directionality considered (:out, :in, :both).
187214
"""
188-
function scaled_laplacian(g::GNNGraph, T::DataType=Float32; dir=:out)
215+
function scaled_laplacian(g::GNNGraph, T=nothing; dir=:out)
189216
L = normalized_laplacian(g, T)
190217
@assert issymmetric(L) "scaled_laplacian only works with symmetric matrices"
191218
λmax = _eigmax(L)

test/GNNGraphs/query.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,21 @@
4444
@test eltype(degree(g, Float32)) == Float32
4545

4646
# weighted degree
47-
# if GRAPH_T == :coo
48-
eweight = [0.1, 2.1, 1.2, 1]
49-
g = GNNGraph((s, t, eweight), graph_type=GRAPH_T)
50-
@test degree(g) == [2.2, 1.2, 1.0, 0.0]
47+
eweight = [0.1, 2.1, 1.2, 1]
48+
g = GNNGraph((s, t, eweight), graph_type=GRAPH_T)
49+
@test degree(g) == [2.2, 1.2, 1.0, 0.0]
50+
if GRAPH_T == :coo
5151
@test degree(g, edge_weight=false) == [2, 1, 1, 0]
5252
@test degree(g, edge_weight=nothing) == [2, 1, 1, 0]
53+
else
54+
# Adjacency matrix representation cannot disambiguate multiple edges
55+
# and edge weights
56+
@test degree(g, edge_weight=false) == [1, 1, 1, 0]
57+
@test degree(g, edge_weight=nothing) == [1, 1, 1, 0]
58+
end
59+
if GRAPH_T == :coo
5360
@test degree(g, edge_weight=2*eweight) == [4.4, 2.4, 2.0, 0.0]
54-
# end
61+
end
5562

5663
if TEST_GPU
5764
d = degree(g)

0 commit comments

Comments
 (0)