Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions GNNGraphs/src/gnngraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A type representing a graph structure that also stores
feature arrays associated to nodes, edges, and the graph itself.

The feature arrays are stored in the fields `ndata`, `edata`, and `gdata`
as [`DataStore`](@ref) objects offering a convenient dictionary-like
as [`DataStore`](@ref) objects offering a convenient dictionary-like
and namedtuple-like interface. The features can be passed at construction
time or added later.

Expand All @@ -23,10 +23,10 @@ is determined by `graph_type`.

When constructed from another `GNNGraph`, the internal graph representation
is preserved and shared. The node/edge/graph features are retained
as well, unless explicitely set by the keyword arguments
as well, unless explicitly set by the keyword arguments
`ndata`, `edata`, and `gdata`.

A `GNNGraph` can also represent multiple graphs batched togheter
A `GNNGraph` can also represent multiple graphs batched together
(see [`MLUtils.batch`](@ref) or [`SparseArrays.blockdiag`](@ref)).
The field `g.graph_indicator` contains the graph membership
of each node.
Expand Down Expand Up @@ -102,7 +102,7 @@ g.edata.e # or just g.e
source, target = edge_index(g)
```
A `GNNGraph` can be sent to the GPU, for example by using Flux.jl's `gpu` function
or MLDataDevices.jl's utilities.
or MLDataDevices.jl's utilities.
"""
struct GNNGraph{T <: Union{COO_T, ADJMAT_T}} <: AbstractGNNGraph{T}
graph::T
Expand Down
23 changes: 11 additions & 12 deletions GNNGraphs/src/gnnheterograph/generate.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
rand_heterograph([rng,] n, m; bidirected=false, kws...)

Construct an [`GNNHeteroGraph`](@ref) with random edges and with number of nodes and edges
Construct an [`GNNHeteroGraph`](@ref) with random edges and with number of nodes and edges
specified by `n` and `m` respectively. `n` and `m` can be any iterable of pairs
specifing node/edge types and their numbers.
specifying node/edge types and their numbers.

Pass a random number generator as a first argument to make the generation reproducible.

Expand Down Expand Up @@ -67,18 +67,18 @@ end


"""
rand_bipartite_heterograph([rng,]
(n1, n2), (m12, m21);
bidirected = true,
node_t = (:A, :B),
edge_t = :to,
rand_bipartite_heterograph([rng,]
(n1, n2), (m12, m21);
bidirected = true,
node_t = (:A, :B),
edge_t = :to,
kws...)

Construct an [`GNNHeteroGraph`](@ref) with random edges representing a bipartite graph.
The graph will have two types of nodes, and edges will only connect nodes of different types.

The first argument is a tuple `(n1, n2)` specifying the number of nodes of each type.
The second argument is a tuple `(m12, m21)` specifying the number of edges connecting nodes of type `1` to nodes of type `2`
The second argument is a tuple `(m12, m21)` specifying the number of edges connecting nodes of type `1` to nodes of type `2`
and vice versa.

The type of nodes and edges can be specified with the `node_t` and `edge_t` keyword arguments,
Expand Down Expand Up @@ -109,16 +109,15 @@ GNNHeteroGraph:
"""
rand_bipartite_heterograph(n, m; kws...) = rand_bipartite_heterograph(Random.default_rng(), n, m; kws...)

function rand_bipartite_heterograph(rng::AbstractRNG, (n1, n2)::NTuple{2,Int}, m; bidirected=true,
function rand_bipartite_heterograph(rng::AbstractRNG, (n1, n2)::NTuple{2,Int}, m; bidirected=true,
node_t = (:A, :B), edge_t::Symbol = :to, kws...)
if m isa Integer
m12 = m21 = m
else
m12, m21 = m
end

return rand_heterograph(rng, Dict(node_t[1] => n1, node_t[2] => n2),
Dict((node_t[1], edge_t, node_t[2]) => m12, (node_t[2], edge_t, node_t[1]) => m21);
return rand_heterograph(rng, Dict(node_t[1] => n1, node_t[2] => n2),
Dict((node_t[1], edge_t, node_t[2]) => m12, (node_t[2], edge_t, node_t[1]) => m21);
bidirected, kws...)
end

72 changes: 36 additions & 36 deletions GNNGraphs/src/transform.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ end
remove_self_loops(g::GNNGraph)

Return a graph constructed from `g` where self-loops (edges from a node to itself)
are removed.
are removed.

See also [`add_self_loops`](@ref) and [`coalesce`](@ref).
"""
Expand Down Expand Up @@ -100,7 +100,7 @@ julia> g = GNNGraph([1, 1, 2, 2, 3], [2, 3, 1, 3, 1])
GNNGraph:
num_nodes: 3
num_edges: 5

# Remove the second edge
julia> g_new = remove_edges(g, [2]);

Expand Down Expand Up @@ -143,15 +143,15 @@ end

function remove_edges(g::GNNGraph{<:COO_T}, p = 0.5)
num_edges = g.num_edges
edges_to_remove = filter(_ -> rand() < p, 1:num_edges)
edges_to_remove = filter(_ -> rand() < p, 1:num_edges)
return remove_edges(g, edges_to_remove)
end

"""
coalesce(g::GNNGraph; aggr=+)

Return a new GNNGraph where all multiple edges between the same pair of nodes are merged
(using aggr for edge weights and features),
Return a new GNNGraph where all multiple edges between the same pair of nodes are merged
(using aggr for edge weights and features),
and the edge indices are sorted lexicographically (by target, then by source).
This method is only applicable to graphs of type `:coo`.

Expand Down Expand Up @@ -198,7 +198,7 @@ Remove specified nodes, and their associated edges, from a GNNGraph. This operat
- `nodes_to_remove`: Vector of node indices to be removed.

# Returns
A new GNNGraph with the specified nodes and all edges associated with these nodes removed.
A new GNNGraph with the specified nodes and all edges associated with these nodes removed.

# Example
```julia
Expand All @@ -219,14 +219,14 @@ function remove_nodes(g::GNNGraph{<:COO_T}, nodes_to_remove::AbstractVector)
w = get_edge_weight(g)
edata = g.edata
ndata = g.ndata

function find_edges_to_remove(nodes, nodes_to_remove)
return findall(node_id -> begin
idx = searchsortedlast(nodes_to_remove, node_id)
idx >= 1 && idx <= length(nodes_to_remove) && nodes_to_remove[idx] == node_id
end, nodes)
end

edges_to_remove_s = find_edges_to_remove(s, nodes_to_remove)
edges_to_remove_t = find_edges_to_remove(t, nodes_to_remove)
edges_to_remove = union(edges_to_remove_s, edges_to_remove_t)
Expand All @@ -238,7 +238,7 @@ function remove_nodes(g::GNNGraph{<:COO_T}, nodes_to_remove::AbstractVector)

w = isnothing(w) ? nothing : getobs(w, mask_edges_to_keep)

for node in sort(nodes_to_remove, rev=true)
for node in sort(nodes_to_remove, rev=true)
s[s .> node] .-= 1
t[t .> node] .-= 1
end
Expand All @@ -248,7 +248,7 @@ function remove_nodes(g::GNNGraph{<:COO_T}, nodes_to_remove::AbstractVector)
edata = getobs(edata, mask_edges_to_keep)

num_nodes = g.num_nodes - length(nodes_to_remove)

return GNNGraph((s, t, w),
num_nodes, length(s), g.num_graphs,
g.graph_indicator,
Expand All @@ -258,7 +258,7 @@ end
"""
remove_nodes(g::GNNGraph, p)

Returns a new graph obtained by dropping nodes from `g` with independent probabilities `p`.
Returns a new graph obtained by dropping nodes from `g` with independent probabilities `p`.

# Examples

Expand Down Expand Up @@ -359,11 +359,11 @@ 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.
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.

The function returns a new `GNNGraph` instance that shares some of the underlying data with `g` but includes the additional edges.
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
Expand All @@ -386,7 +386,7 @@ GNNGraph:
num_edges: 6
```
"""
perturb_edges(g::GNNGraph{<:COO_T}, perturb_ratio::AbstractFloat) =
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)
Expand Down Expand Up @@ -444,10 +444,10 @@ end
"""
to_bidirected(g)

Adds a reverse edge for each edge in the graph, then calls
[`coalesce`](@ref) with `mean` aggregation to simplify the graph.
Adds a reverse edge for each edge in the graph, then calls
[`coalesce`](@ref) with `mean` aggregation to simplify the graph.

See also [`is_bidirected`](@ref).
See also [`is_bidirected`](@ref).

# Examples

Expand Down Expand Up @@ -550,7 +550,7 @@ end
"""
add_nodes(g::GNNGraph, n; [ndata])

Add `n` new nodes to graph `g`. In the
Add `n` new nodes to graph `g`. In the
new graph, these nodes will have indexes from `g.num_nodes + 1`
to `g.num_nodes + n`.
"""
Expand All @@ -567,7 +567,7 @@ end
"""
set_edge_weight(g::GNNGraph, w::AbstractVector)

Set `w` as edge weights in the returned graph.
Set `w` as edge weights in the returned graph.
"""
function set_edge_weight(g::GNNGraph, w::AbstractVector)
# TODO preserve the representation instead of converting to COO
Expand Down Expand Up @@ -634,7 +634,7 @@ end
"""
batch(gs::Vector{<:GNNGraph})

Batch together multiple `GNNGraph`s into a single one
Batch together multiple `GNNGraph`s into a single one
containing the total number of original nodes and edges.

Equivalent to [`SparseArrays.blockdiag`](@ref).
Expand Down Expand Up @@ -719,7 +719,7 @@ end
"""
unbatch(g::GNNGraph)

Opposite of the [`MLUtils.batch`](@ref) operation, returns
Opposite of the [`MLUtils.batch`](@ref) operation, returns
an array of the individual graphs batched together in `g`.

See also [`MLUtils.batch`](@ref) and [`getgraph`](@ref).
Expand Down Expand Up @@ -816,10 +816,10 @@ CRC.@non_differentiable _unbatch_edgemasks(::Any...)

Return the subgraph of `g` induced by those nodes `j`
for which `g.graph_indicator[j] == i` or,
if `i` is a collection, `g.graph_indicator[j] ∈ i`.
In other words, it extract the component graphs from a batched graph.
if `i` is a collection, `g.graph_indicator[j] ∈ i`.
In other words, it extract the component graphs from a batched graph.

If `nmap=true`, return also a vector `v` mapping the new nodes to the old ones.
If `nmap=true`, return also a vector `v` mapping the new nodes to the old ones.
The node `i` in the subgraph will correspond to the node `v[i]` in `g`.
"""
getgraph(g::GNNGraph, i::Int; kws...) = getgraph(g, [i]; kws...)
Expand Down Expand Up @@ -905,14 +905,14 @@ function getgraph(g::GNNGraph, i::AbstractVector{Int}; nmap = false)
end

"""
negative_sample(g::GNNGraph;
num_neg_edges = g.num_edges,
negative_sample(g::GNNGraph;
num_neg_edges = g.num_edges,
bidirected = is_bidirected(g))

Return a graph containing random negative edges (i.e. non-edges) from graph `g` as edges.

If `bidirected=true`, the output graph will be bidirected and there will be no
leakage from the origin graph.
leakage from the origin graph.

See also [`is_bidirected`](@ref).
"""
Expand All @@ -933,11 +933,11 @@ function negative_sample(g::GNNGraph;
idx_pos, maxid = edge_encoding(s, t, n)
if bidirected
num_neg_edges = num_neg_edges ÷ 2
pneg = 1 - g.num_edges / 2maxid # prob of selecting negative edge
pneg = 1 - g.num_edges / 2maxid # prob of selecting negative edge
else
pneg = 1 - g.num_edges / 2maxid # prob of selecting negative edge
pneg = 1 - g.num_edges / 2maxid # prob of selecting negative edge
end
# pneg * sample_prob * maxid == num_neg_edges
# pneg * sample_prob * maxid == num_neg_edges
sample_prob = min(1, num_neg_edges / (pneg * maxid) * 1.1)
idx_neg = Int[]
for _ in 1:max_trials
Expand All @@ -962,14 +962,14 @@ end

Randomly partition the edges in `g` to form two graphs, `g1`
and `g2`. Both will have the same number of nodes as `g`.
`g1` will contain a fraction `frac` of the original edges,
while `g2` wil contain the rest.
`g1` will contain a fraction `frac` of the original edges,
while `g2` will contain the rest.

If `bidirected = true` makes sure that an edge and its reverse go into the same split.
This option is supported only for bidirected graphs with no self-loops
and multi-edges.

`rand_edge_split` is tipically used to create train/test splits in link prediction tasks.
`rand_edge_split` is typically used to create train/test splits in link prediction tasks.
"""
function rand_edge_split(g::GNNGraph, frac; bidirected = is_bidirected(g))
s, t = edge_index(g)
Expand Down Expand Up @@ -999,7 +999,7 @@ end
"""
random_walk_pe(g, walk_length)

Return the random walk positional encoding from the paper [Graph Neural Networks with Learnable Structural and Positional Representations](https://arxiv.org/abs/2110.07875) of the given graph `g` and the length of the walk `walk_length` as a matrix of size `(walk_length, g.num_nodes)`.
Return the random walk positional encoding from the paper [Graph Neural Networks with Learnable Structural and Positional Representations](https://arxiv.org/abs/2110.07875) of the given graph `g` and the length of the walk `walk_length` as a matrix of size `(walk_length, g.num_nodes)`.
"""
function random_walk_pe(g::GNNGraph, walk_length::Int)
matrix = zeros(walk_length, g.num_nodes)
Expand Down
10 changes: 5 additions & 5 deletions GraphNeuralNetworks/docs/src/dev.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Developer Notes


## Development Enviroment
## Development Environment
GraphNeuralNetworks.jl is a collection of packages hosted on a single GitHub repository.
These packages are `GraphNeuralNetworks.jl` (a package with the same name as the whole project), `GNNGraphs.jl`, `GNNlib.jl`, and `GNNLux.jl`.

Expand All @@ -17,7 +17,7 @@ graph TD;

Thanks to [workspaces](https://pkgdocs.julialang.org/v1/toml-files/#The-%5Bworkspace%5D-section) introduced in julia 1.12, developing multiple packages in a monorepo is straightforward.

Clone the repository with
Clone the repository with
```
git clone https://github.com/JuliaGraphs/GraphNeuralNetworks.jl
```
Expand All @@ -29,7 +29,7 @@ julia --project=.
```
Dependencies in the monorepo will be automatically available in `dev` mode, so you can start editing the code of any of the packages and test your changes immediately.

## Add a New Layer
## Add a New Layer

To add a new graph convolutional layer and make it available in both the Flux-based frontend (GraphNeuralNetworks.jl) and the Lux-based frontend (GNNLux), you need to:

Expand Down Expand Up @@ -70,7 +70,7 @@ end
```

## Versions and Tagging
Each PR should update the version number in the Porject.toml file of each involved package if needed by semnatic versioning. For instance, when adding new features GNNGraphs could move from "1.17.5" to "1.18.0-DEV". The "DEV" will be removed when the package is tagged and released. Pay also attention to updating
Each PR should update the version number in the Project.toml file of each involved package if needed by semnatic versioning. For instance, when adding new features GNNGraphs could move from "1.17.5" to "1.18.0-DEV". The "DEV" will be removed when the package is tagged and released. Pay also attention to updating
the compat bounds, e.g. GraphNeuralNetworks might require a newer version of GNNGraphs.

## Generate Documentation Locally
Expand Down Expand Up @@ -137,4 +137,4 @@ git add docs/pluto_output # add generated cache

Check the [documenter CI logs](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/actions/workflows/docs.yml) to ensure that it used the local cache:

![](https://user-images.githubusercontent.com/55111154/210061301-c84b7274-9e66-46fd-b272-d45b1c681d00.png)
![](https://user-images.githubusercontent.com/55111154/210061301-c84b7274-9e66-46fd-b272-d45b1c681d00.png)
4 changes: 2 additions & 2 deletions GraphNeuralNetworks/notebooks/graph_classification.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"source": [
"# Graph Classification with Graph Neural Networks\n",
"\n",
"*This noteboook is a julia adaptation of the Pytorch Geometric tutorials that can be found [here](https://pytorch-geometric.readthedocs.io/en/latest/notes/colabs.html).*\n",
"*This notebook is a julia adaptation of the Pytorch Geometric tutorials that can be found [here](https://pytorch-geometric.readthedocs.io/en/latest/notes/colabs.html).*\n",
"\n",
"In this tutorial session we will have a closer look at how to apply **Graph Neural Networks (GNNs) to the task of graph classification**.\n",
"Graph classification refers to the problem of classifiying entire graphs (in contrast to nodes), given a **dataset of graphs**, based on some structural graph properties.\n",
"Graph classification refers to the problem of classifying entire graphs (in contrast to nodes), given a **dataset of graphs**, based on some structural graph properties.\n",
"Here, we want to embed entire graphs, and we want to embed those graphs in such a way so that they are linearly separable given a task at hand.\n",
"\n",
"\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"source": [
"# Graph Classification with Graph Neural Networks\n",
"\n",
"*This noteboook is a julia adaptation of the Pytorch Geometric tutorials that can be found [here](https://pytorch-geometric.readthedocs.io/en/latest/notes/colabs.html).*\n",
"*This notebook is a julia adaptation of the Pytorch Geometric tutorials that can be found [here](https://pytorch-geometric.readthedocs.io/en/latest/notes/colabs.html).*\n",
"\n",
"In this tutorial session we will have a closer look at how to apply **Graph Neural Networks (GNNs) to the task of graph classification**.\n",
"Graph classification refers to the problem of classifiying entire graphs (in contrast to nodes), given a **dataset of graphs**, based on some structural graph properties.\n",
"Graph classification refers to the problem of classifying entire graphs (in contrast to nodes), given a **dataset of graphs**, based on some structural graph properties.\n",
"Here, we want to embed entire graphs, and we want to embed those graphs in such a way so that they are linearly separable given a task at hand.\n",
"\n",
"\n",
Expand Down
Loading
Loading