Skip to content

Commit

Permalink
CI for GNNGraphs.jl (#451)
Browse files Browse the repository at this point in the history
* CI from GNNGraphs.jl

* cleanup

* fix perturb_edges

* fix spelling

* import

* fix runtest

* fix

* workflow
  • Loading branch information
CarloLucibello authored Jul 24, 2024
1 parent a590094 commit 1823e0d
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 54 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/tests_GNNGraphs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: GNNGraphs
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.10' # Replace this with the minimum Julia version that your package supports.
- '1' # '1' will automatically expand to the latest stable 1.x release of Julia.
# - 'pre'
os:
- ubuntu-latest
arch:
- x64

steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- name: Install Julia dependencies and run tests
shell: julia --project=monorepo {0}
run: |
using Pkg
# dev mono repo versions
pkg"registry up"
Pkg.update()
pkg"dev ./GNNGraphs"
Pkg.test("GNNGraphs"; coverage=true)
- uses: julia-actions/julia-processcoverage@v1
with:
# directories: ./GNNGraphs/src, ./GNNGraphs/ext
directories: ./GNNGraphs/src
- uses: codecov/codecov-action@v4
with:
files: lcov.info
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: GNN
on:
pull_request:
branches:
Expand All @@ -8,15 +8,15 @@ on:
- master
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
# - '1.9' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
- 'nightly'
- '1.10' # Replace this with the minimum Julia version that your package supports.
- '1' # '1' will automatically expand to the latest stable 1.x release of Julia.
# - 'pre'
os:
- ubuntu-latest
arch:
Expand Down
3 changes: 2 additions & 1 deletion GNNGraphs/src/GNNGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import KrylovKit
using ChainRulesCore
using LinearAlgebra, Random, Statistics
import MLUtils
using MLUtils: getobs, numobs, ones_like, zeros_like, chunk, batch
using MLUtils: getobs, numobs, ones_like, zeros_like, chunk, batch, rand_like
import Functors
using LuxDeviceUtils: get_device, cpu_device, LuxCPUDevice

Expand Down Expand Up @@ -77,6 +77,7 @@ export add_nodes,
to_bidirected,
to_unidirected,
random_walk_pe,
perturb_edges,
remove_nodes,
ppr_diffusion,
drop_nodes,
Expand Down
2 changes: 1 addition & 1 deletion GNNGraphs/src/query.jl
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ function _degree((s, t)::Tuple, T::Type, dir::Symbol, edge_weight::Nothing, num_
end

function _degree((s, t)::Tuple, T::Type, dir::Symbol, edge_weight::AbstractVector, num_nodes::Int)
degs = fill!(similar(s, T, num_nodes), 0)
degs = zeros_like(s, T, num_nodes)

if dir [:out, :both]
degs = degs .+ NNlib.scatter(+, edge_weight, s, dstsize = (num_nodes,))
Expand Down
68 changes: 68 additions & 0 deletions GNNGraphs/src/transform.jl
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,74 @@ function add_edges(g::GNNHeteroGraph{<:COO_T},
end


"""
perturb_edges([rng], g::GNNGraph, perturb_ratio)
Return a new graph obtained from `g` by adding random edges, based on a specified `perturb_ratio`.
The `perturb_ratio` determines the fraction of new edges to add relative to the current number of edges in the graph.
These new edges are added without creating self-loops.
Optionally, a random `seed` can be provided to ensure reproducible perturbations.
The function returns a new `GNNGraph` instance that shares some of the underlying data with `g` but includes the additional edges.
The nodes for the new edges are selected randomly, and no edge data (`edata`) or weights (`w`) are assigned to these new edges.
# Arguments
- `g::GNNGraph`: The graph to be perturbed.
- `perturb_ratio`: The ratio of the number of new edges to add relative to the current number of edges in the graph. For example, a `perturb_ratio` of 0.1 means that 10% of the current number of edges will be added as new random edges.
- `rng`: An optionalrandom number generator to ensure reproducible results.
# Examples
```julia
julia> g = GNNGraph((s, t, w))
GNNGraph:
num_nodes: 4
num_edges: 5
julia> perturbed_g = perturb_edges(g, 0.2)
GNNGraph:
num_nodes: 4
num_edges: 6
```
"""
perturb_edges(g::GNNGraph{<:COO_T}, perturb_ratio::AbstractFloat) =
perturb_edges(Random.default_rng(), g, perturb_ratio)

function perturb_edges(rng::AbstractRNG, g::GNNGraph{<:COO_T}, perturb_ratio::AbstractFloat)
@assert perturb_ratio >= 0 && perturb_ratio <= 1 "perturb_ratio must be between 0 and 1"

num_current_edges = g.num_edges
num_edges_to_add = ceil(Int, num_current_edges * perturb_ratio)

if num_edges_to_add == 0
return g
end

num_nodes = g.num_nodes
@assert num_nodes > 1 "Graph must contain at least 2 nodes to add edges"

snew = ceil.(Int, rand_like(rng, ones(num_nodes), Float32, num_edges_to_add) .* num_nodes)
tnew = ceil.(Int, rand_like(rng, ones(num_nodes), Float32, num_edges_to_add) .* num_nodes)

mask_loops = snew .!= tnew
snew = snew[mask_loops]
tnew = tnew[mask_loops]

while length(snew) < num_edges_to_add
n = num_edges_to_add - length(snew)
snewnew = ceil.(Int, rand_like(rng, ones(num_nodes), Float32, n) .* num_nodes)
tnewnew = ceil.(Int, rand_like(rng, ones(num_nodes), Float32, n) .* num_nodes)
mask_new_loops = snewnew .!= tnewnew
snewnew = snewnew[mask_new_loops]
tnewnew = tnewnew[mask_new_loops]
snew = [snew; snewnew]
tnew = [tnew; tnewnew]
end

return add_edges(g, (snew, tnew, nothing))
end


### TODO Cannot implement this since GNNGraph is immutable (cannot change num_edges). make it mutable
# function Graphs.add_edge!(g::GNNGraph{<:COO_T}, snew::T, tnew::T; edata=nothing) where T<:Union{Integer, AbstractVector}
Expand Down
File renamed without changes.
3 changes: 1 addition & 2 deletions GNNGraphs/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ tests = [
"sampling",
"gnnheterograph",
"temporalsnapshotsgnngraph",
"ext/SimpleWeightedGraphs/SimpleWeightedGraphs"
"ext/SimpleWeightedGraphs"
]

!CUDA.functional() && @warn("CUDA unavailable, not testing GPU support")
Expand All @@ -49,7 +49,6 @@ for graph_type in (:coo, :dense, :sparse)
# global TEST_GPU = false

@testset "$t" for t in tests
t == "GNNGraphs/sampling" && GRAPH_T != :coo && continue
include("$t.jl")
end
end
90 changes: 46 additions & 44 deletions GNNGraphs/test/sampling.jl
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
@testset "sample_neighbors" begin
# replace = false
dir = :in
nodes = 2:3
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
sg = sample_neighbors(g, nodes; dir)
@test sg.num_nodes == 10
@test sg.num_edges == sum(degree(g, i; dir) for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
@test length(union(sg.edata.EID)) == length(sg.edata.EID)
adjlist = adjacency_list(g; dir)
s, t = edge_index(sg)
@test all(t .∈ Ref(nodes))
for i in nodes
@test sort(neighbors(sg, i; dir)) == sort(neighbors(g, i; dir))
end
if GRAPH_T == :coo
@testset "sample_neighbors" begin
# replace = false
dir = :in
nodes = 2:3
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
sg = sample_neighbors(g, nodes; dir)
@test sg.num_nodes == 10
@test sg.num_edges == sum(degree(g, i; dir) for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
@test length(union(sg.edata.EID)) == length(sg.edata.EID)
adjlist = adjacency_list(g; dir)
s, t = edge_index(sg)
@test all(t .∈ Ref(nodes))
for i in nodes
@test sort(neighbors(sg, i; dir)) == sort(neighbors(g, i; dir))
end

# replace = true
dir = :out
nodes = 2:3
K = 2
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
sg = sample_neighbors(g, nodes, K; dir, replace = true)
@test sg.num_nodes == 10
@test sg.num_edges == sum(K for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
adjlist = adjacency_list(g; dir)
s, t = edge_index(sg)
@test all(s .∈ Ref(nodes))
for i in nodes
@test issubset(neighbors(sg, i; dir), adjlist[i])
end
# replace = true
dir = :out
nodes = 2:3
K = 2
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
sg = sample_neighbors(g, nodes, K; dir, replace = true)
@test sg.num_nodes == 10
@test sg.num_edges == sum(K for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
adjlist = adjacency_list(g; dir)
s, t = edge_index(sg)
@test all(s .∈ Ref(nodes))
for i in nodes
@test issubset(neighbors(sg, i; dir), adjlist[i])
end

# dropnodes = true
dir = :in
nodes = 2:3
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
g = GNNGraph(g, ndata = (x1 = rand(10),), edata = (e1 = rand(40),))
sg = sample_neighbors(g, nodes; dir, dropnodes = true)
@test sg.num_edges == sum(degree(g, i; dir) for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
@test size(sg.ndata.NID) == (sg.num_nodes,)
@test sg.edata.e1 == g.edata.e1[sg.edata.EID]
@test sg.ndata.x1 == g.ndata.x1[sg.ndata.NID]
@test length(union(sg.ndata.NID)) == length(sg.ndata.NID)
end
# dropnodes = true
dir = :in
nodes = 2:3
g = rand_graph(10, 40, bidirected = false, graph_type = GRAPH_T)
g = GNNGraph(g, ndata = (x1 = rand(10),), edata = (e1 = rand(40),))
sg = sample_neighbors(g, nodes; dir, dropnodes = true)
@test sg.num_edges == sum(degree(g, i; dir) for i in nodes)
@test size(sg.edata.EID) == (sg.num_edges,)
@test size(sg.ndata.NID) == (sg.num_nodes,)
@test sg.edata.e1 == g.edata.e1[sg.edata.EID]
@test sg.ndata.x1 == g.ndata.x1[sg.ndata.NID]
@test length(union(sg.ndata.NID)) == length(sg.ndata.NID)
end
end
2 changes: 1 addition & 1 deletion GNNGraphs/test/transform.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ end
s, t = [1, 2, 3, 4, 5], [2, 3, 4, 5, 1]
g = GNNGraph((s, t))
rng = MersenneTwister(42)
g_per = perturb_edges(g, 0.5, rng=rng)
g_per = perturb_edges(rng, g, 0.5)
@test g_per.num_edges == 8
end end

Expand Down

0 comments on commit 1823e0d

Please sign in to comment.