Skip to content

Commit

Permalink
move around docs
Browse files Browse the repository at this point in the history
  • Loading branch information
CarloLucibello committed Dec 1, 2024
1 parent bd6fc93 commit 8979735
Show file tree
Hide file tree
Showing 15 changed files with 738 additions and 30 deletions.
6 changes: 4 additions & 2 deletions GNNGraphs/docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ makedocs(;
format = Documenter.HTML(; mathengine, prettyurls, assets = assets, size_threshold=nothing),
sitename = "GNNGraphs.jl",
pages = ["Home" => "index.md",
"Graphs" => ["gnngraph.md", "heterograph.md", "temporalgraph.md"],
"Datasets" => "datasets.md",
"Guides" => [
"Graphs" => ["guides/gnngraph.md", "guides/heterograph.md", "guides/temporalgraph.md"],
"Datasets" => "guides/datasets.md",
],
"API Reference" => [
"GNNGraph" => "api/gnngraph.md",
"GNNHeteroGraph" => "api/heterograph.md",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion GNNlib/docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ makedocs(;
format = Documenter.HTML(; mathengine, prettyurls, assets = assets, size_threshold=nothing),
sitename = "GNNlib.jl",
pages = ["Home" => "index.md",
"Message Passing" => "messagepassing.md",
"Message Passing" => "guides/messagepassing.md",

"API Reference" => [

Expand Down
File renamed without changes.
60 changes: 38 additions & 22 deletions GraphNeuralNetworks/docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,44 @@ interlinks = InterLinks(

)

# Copy the guides from GNNGraphs and GNNlib
dest_guides_dir = joinpath(@__DIR__, "src/guides")
gnngraphs_guides_dir = joinpath(@__DIR__, "../../GNNGraphs/docs/src/guides")
gnnlib_guides_dir = joinpath(@__DIR__, "../../GNNlib/docs/src/guides")
for file in readdir(gnngraphs_guides_dir)
cp(joinpath(gnngraphs_guides_dir, file), joinpath(dest_guides_dir, file))
end
for file in readdir(gnnlib_guides_dir)
cp(joinpath(gnnlib_guides_dir, file), joinpath(dest_guides_dir, file))
end

makedocs(;
modules = [GraphNeuralNetworks],
doctest = false,
clean = true,
plugins = [interlinks],
format = Documenter.HTML(; mathengine, prettyurls, assets = assets, size_threshold=nothing),
sitename = "GraphNeuralNetworks.jl",
pages = [
"Home" => "index.md",
"Guides" => [
"Models" => "models.md",
],
"API Reference" => [
"Basic" => "api/basic.md",
"Convolutional layers" => "api/conv.md",
"Pooling layers" => "api/pool.md",
"Temporal Convolutional layers" => "api/temporalconv.md",
"Hetero Convolutional layers" => "api/heteroconv.md",
],
"Developer guide" => "dev.md",

],
)
modules = [GraphNeuralNetworks],
doctest = false,
clean = true,
plugins = [interlinks],
format = Documenter.HTML(; mathengine, prettyurls, assets = assets, size_threshold=nothing),
sitename = "GraphNeuralNetworks.jl",
pages = [

"Home" => "index.md",

"Guides" => [
"Graphs" => ["guides/gnngraph.md", "guides/heterograph.md", "guides/temporalgraph.md"],
"Message Passing" => "guides/messagepassing.md",
"Models" => "guides/models.md",
"Datasets" => "guides/datasets.md",
],

"API Reference" => [
"Basic" => "api/basic.md",
"Convolutional layers" => "api/conv.md",
"Pooling layers" => "api/pool.md",
"Temporal Convolutional layers" => "api/temporalconv.md",
"Hetero Convolutional layers" => "api/heteroconv.md",
],
"Developer guide" => "dev.md",
],
)

deploydocs(;repo = "github.com/JuliaGraphs/GraphNeuralNetworks.jl.git", devbranch = "master", dirname= "GraphNeuralNetworks")
5 changes: 0 additions & 5 deletions GraphNeuralNetworks/docs/src/datasets.md

This file was deleted.

10 changes: 10 additions & 0 deletions GraphNeuralNetworks/docs/src/guides/datasets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Datasets

GNNGraphs.jl doesn't come with its own datasets, but leverages those available in the Julia (and non-Julia) ecosystem. In particular, the [examples in the GraphNeuralNetworks.jl repository](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/tree/master/examples) make use of the [MLDatasets.jl](https://github.com/JuliaML/MLDatasets.jl) package. There you will find common graph datasets such as Cora, PubMed, Citeseer, TUDataset and [many others](https://juliaml.github.io/MLDatasets.jl/dev/datasets/graphs/).
For graphs with static structures and temporal features, datasets such as METRLA, PEMSBAY, ChickenPox, and WindMillEnergy are available. For graphs featuring both temporal structures and temporal features, the TemporalBrains dataset is suitable.

GraphNeuralNetworks.jl provides the [`mldataset2gnngraph`](@ref) method for interfacing with MLDatasets.jl.

```@docs
mldataset2gnngraph
```
257 changes: 257 additions & 0 deletions GraphNeuralNetworks/docs/src/guides/gnngraph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
# Static Graphs

The fundamental graph type in GNNGraphs.jl is the [`GNNGraph`](@ref).
A GNNGraph `g` is a directed graph with nodes labeled from 1 to `g.num_nodes`.
The underlying implementation allows for efficient application of graph neural network
operators, gpu movement, and storage of node/edge/graph related feature arrays.

`GNNGraph` inherits from [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl)'s `AbstractGraph`,
therefore it supports most functionality from that library.

## Graph Creation
A GNNGraph can be created from several different data sources encoding the graph topology:

```julia
using GNNGraphs, Graphs, SparseArrays


# Construct a GNNGraph from from a Graphs.jl's graph
lg = erdos_renyi(10, 30)
g = GNNGraph(lg)

# Same as above using convenience method rand_graph
g = rand_graph(10, 60)

# From an adjacency matrix
A = sprand(10, 10, 0.3)
g = GNNGraph(A)

# From an adjacency list
adjlist = [[2,3], [1,3], [1,2,4], [3]]
g = GNNGraph(adjlist)

# From COO representation
source = [1,1,2,2,3,3,3,4]
target = [2,3,1,3,1,2,4,3]
g = GNNGraph(source, target)
```

See also the related methods [`Graphs.adjacency_matrix`](@ref), [`edge_index`](@ref), and [`adjacency_list`](@ref).

## Basic Queries

```julia
julia> source = [1,1,2,2,3,3,3,4];

julia> target = [2,3,1,3,1,2,4,3];

julia> g = GNNGraph(source, target)
GNNGraph:
num_nodes: 4
num_edges: 8


julia> @assert g.num_nodes == 4 # number of nodes

julia> @assert g.num_edges == 8 # number of edges

julia> @assert g.num_graphs == 1 # number of subgraphs (a GNNGraph can batch many graphs together)

julia> is_directed(g) # a GNNGraph is always directed
true

julia> is_bidirected(g) # for each edge, also the reverse edge is present
true

julia> has_self_loops(g)
false

julia> has_multi_edges(g)
false
```

## Data Features

One or more arrays can be associated to nodes, edges, and (sub)graphs of a `GNNGraph`.
They will be stored in the fields `g.ndata`, `g.edata`, and `g.gdata` respectively.

The data fields are [`DataStore`](@ref) objects. [`DataStore`](@ref)s conveniently offer an interface similar to both dictionaries and named tuples. Similarly to dictionaries, DataStores support addition of new features after creation time.

The array contained in the datastores have last dimension equal to `num_nodes` (in `ndata`), `num_edges` (in `edata`), or `num_graphs` (in `gdata`) respectively.

```julia
# Create a graph with a single feature array `x` associated to nodes
g = rand_graph(10, 60, ndata = (; x = rand(Float32, 32, 10)))

g.ndata.x # access the features

# Equivalent definition passing directly the array
g = rand_graph(10, 60, ndata = rand(Float32, 32, 10))

g.ndata.x # `:x` is the default name for node features

g.ndata.z = rand(Float32, 3, 10) # add new feature array `z`

# For convenience, we can access the features through the shortcut
g.x

# You can have multiple feature arrays
g = rand_graph(10, 60, ndata = (; x=rand(Float32, 32, 10), y=rand(Float32, 10)))

g.ndata.y, g.ndata.x # or g.x, g.y

# Attach an array with edge features.
# Since `GNNGraph`s are directed, the number of edges
# will be double that of the original Graphs' undirected graph.
g = GNNGraph(erdos_renyi(10, 30), edata = rand(Float32, 60))
@assert g.num_edges == 60

g.edata.e # or g.e

# If we pass only half of the edge features, they will be copied
# on the reversed edges.
g = GNNGraph(erdos_renyi(10, 30), edata = rand(Float32, 30))


# Create a new graph from previous one, inheriting edge data
# but replacing node data
g′ = GNNGraph(g, ndata =(; z = ones(Float32, 16, 10)))

g′.z
g′.e
```

## Edge weights

It is common to denote scalar edge features as edge weights. The `GNNGraph` has specific support
for edge weights: they can be stored as part of internal representations of the graph (COO or adjacency matrix). Some graph convolutional layers, most notably the `GCNConv`, can use the edge weights to perform weighted sums over the nodes' neighborhoods.

```julia
julia> source = [1, 1, 2, 2, 3, 3];

julia> target = [2, 3, 1, 3, 1, 2];

julia> weight = [1.0, 0.5, 2.1, 2.3, 4, 4.1];

julia> g = GNNGraph(source, target, weight)
GNNGraph:
num_nodes: 3
num_edges: 6

julia> get_edge_weight(g)
6-element Vector{Float64}:
1.0
0.5
2.1
2.3
4.0
4.1
```

## Batches and Subgraphs

Multiple `GNNGraph`s can be batched together into a single graph
that contains the total number of the original nodes
and where the original graphs are disjoint subgraphs.

```julia
using Flux
using Flux: DataLoader

data = [rand_graph(10, 30, ndata=rand(Float32, 3, 10)) for _ in 1:160]
gall = Flux.batch(data)

# gall is a GNNGraph containing many graphs
@assert gall.num_graphs == 160
@assert gall.num_nodes == 1600 # 10 nodes x 160 graphs
@assert gall.num_edges == 4800 # 30 undirected edges x 160 graphs

# Let's create a mini-batch from gall
g23 = getgraph(gall, 2:3)
@assert g23.num_graphs == 2
@assert g23.num_nodes == 20 # 10 nodes x 2 graphs
@assert g23.num_edges == 60 # 30 undirected edges X 2 graphs

# We can pass a GNNGraph to Flux's DataLoader
train_loader = DataLoader(gall, batchsize=16, shuffle=true)

for g in train_loader
@assert g.num_graphs == 16
@assert g.num_nodes == 160
@assert size(g.ndata.x) = (3, 160)
# .....
end

# Access the nodes' graph memberships
graph_indicator(gall)
```

## DataLoader and mini-batch iteration

While constructing a batched graph and passing it to the `DataLoader` is always
an option for mini-batch iteration, the recommended way for better performance is
to pass an array of graphs directly and set the `collate` option to `true`:

```julia
using Flux: DataLoader

data = [rand_graph(10, 30, ndata=rand(Float32, 3, 10)) for _ in 1:320]

train_loader = DataLoader(data, batchsize=16, shuffle=true, collate=true)

for g in train_loader
@assert g.num_graphs == 16
@assert g.num_nodes == 160
@assert size(g.ndata.x) = (3, 160)
# .....
end
```

## Graph Manipulation

```julia
g′ = add_self_loops(g)
g′ = remove_self_loops(g)
g′ = add_edges(g, [1, 2], [2, 3]) # add edges 1->2 and 2->3
```

## GPU movement

Move a `GNNGraph` to a CUDA device using `Flux.gpu` method.

```julia
using CUDA, Flux

g_gpu = g |> Flux.gpu
```

## Integration with Graphs.jl

Since `GNNGraph <: Graphs.AbstractGraph`, we can use any functionality from [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl) for querying and analyzing the graph structure.
Moreover, a `GNNGraph` can be easily constructed from a `Graphs.Graph` or a `Graphs.DiGraph`:

```julia
julia> import Graphs

julia> using GNNGraphs

# A Graphs.jl undirected graph
julia> gu = Graphs.erdos_renyi(10, 20)
{10, 20} undirected simple Int64 graph

# Since GNNGraphs are undirected, the edges are doubled when converting
# to GNNGraph
julia> GNNGraph(gu)
GNNGraph:
num_nodes: 10
num_edges: 40

# A Graphs.jl directed graph
julia> gd = Graphs.erdos_renyi(10, 20, is_directed=true)
{10, 20} directed simple Int64 graph

julia> GNNGraph(gd)
GNNGraph:
num_nodes: 10
num_edges: 20
```
Loading

0 comments on commit 8979735

Please sign in to comment.