From 3ab7bf68ad8e9eb83c882eadccb8030cb0081b55 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Wed, 24 Jul 2024 20:41:27 +0200 Subject: [PATCH 01/10] fix docs --- .github/workflows/docs.yml | 10 +- docs/Project.toml | 4 +- docs/make.jl | 11 +- docs/pluto_output/gnn_intro_pluto.md | 30 +- .../graph_classification_pluto.md | 22 +- .../pluto_output/node_classification_pluto.md | 18 +- docs/src/api/gnngraph.md | 12 +- docs/src/api/heterograph.md | 2 +- docs/src/api/temporalconv.md | 15 + docs/src/api/temporalgraph.md | 2 +- docs/src/democards/gridtheme.css | 59 + .../introductory_tutorials/gnn_intro_pluto.jl | 1415 +++++++----- .../graph_classification_pluto.jl | 859 +++---- .../node_classification_pluto.jl | 1268 ++++++----- .../temporal_graph_classification_pluto.jl | 495 ++-- .../traffic_prediction.jl | 2018 ++++++++++++++++- 16 files changed, 4408 insertions(+), 1832 deletions(-) create mode 100644 docs/src/api/temporalconv.md create mode 100644 docs/src/democards/gridtheme.css diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8d9b42dc4..68e3fda9b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,9 +14,15 @@ jobs: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: - version: '1.9.1' + version: '1.10.4' - name: Install dependencies - run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + shell: julia --project=docs/ {0} + run: | + using Pkg + # dev mono repo versions + pkg"registry up" + Pkg.update() + pkg"dev ./GNNGraphs ." - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token diff --git a/docs/Project.toml b/docs/Project.toml index f35adbd75..4535b9ce9 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,11 +2,11 @@ DemoCards = "311a05b2-6137-4a5a-b473-18580a3d38b5" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" +GNNGraphs = "aed8fd31-079b-4b5a-b342-a13352159b8c" GraphNeuralNetworks = "cffab07f-9bc2-4db1-8861-388f63bf7694" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458" -MarkdownLiteral = "736d6165-7244-6769-4267-6b50796e6954" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" @@ -17,4 +17,4 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] DemoCards = "0.5.0" -Documenter = "0.27" +Documenter = "1.5" diff --git a/docs/make.jl b/docs/make.jl index 818a7191e..6e00d5290 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,11 +1,12 @@ using Flux, NNlib, GraphNeuralNetworks, Graphs, SparseArrays +using GNNGraphs using Pluto, PlutoStaticHTML # for tutorials using Documenter, DemoCards -tutorials, tutorials_cb, tutorial_assets = makedemos("tutorials") +# tutorials, tutorials_cb, tutorial_assets = makedemos("tutorials") assets = [] -isnothing(tutorial_assets) || push!(assets, tutorial_assets) +# isnothing(tutorial_assets) || push!(assets, tutorial_assets) DocMeta.setdocmeta!(GraphNeuralNetworks, :DocTestSetup, :(using GraphNeuralNetworks, Graphs, SparseArrays, NNlib, Flux); @@ -15,7 +16,7 @@ prettyurls = get(ENV, "CI", nothing) == "true" mathengine = MathJax3() makedocs(; - modules = [GraphNeuralNetworks, NNlib, Flux, Graphs, SparseArrays], + modules = [GraphNeuralNetworks, GNNGraphs], doctest = false, clean = true, format = Documenter.HTML(; mathengine, prettyurls, assets = assets), @@ -25,7 +26,7 @@ makedocs(; "Message Passing" => "messagepassing.md", "Model Building" => "models.md", "Datasets" => "datasets.md", - "Tutorials" => tutorials, + # "Tutorials" => tutorials, "API Reference" => [ "GNNGraph" => "api/gnngraph.md", "Basic Layers" => "api/basic.md", @@ -40,6 +41,6 @@ makedocs(; "Summer Of Code" => "gsoc.md", ]) -tutorials_cb() +# tutorials_cb() deploydocs(repo = "github.com/CarloLucibello/GraphNeuralNetworks.jl.git") diff --git a/docs/pluto_output/gnn_intro_pluto.md b/docs/pluto_output/gnn_intro_pluto.md index 4680965bd..e188bf1d8 100644 --- a/docs/pluto_output/gnn_intro_pluto.md +++ b/docs/pluto_output/gnn_intro_pluto.md @@ -1,13 +1,13 @@ ```@raw html - - - - -

In this tutorial, we will learn how to extend the graph classification task to the case of temporal graphs, i.e., graphs whose topology and features are time-varying.

We will design and train a simple temporal graph neural network architecture to classify subjects' gender (female or male) using the temporal graphs extracted from their brain fMRI scan signals. Given the large amount of data, we will implement the training so that it can also run on the GPU.

- - -``` -## Import -```@raw html -
-

We start by importing the necessary libraries. We use GraphNeuralNetworks.jl, Flux.jl and MLDatasets.jl, among others.

- -
begin
-    using Flux
-    using GraphNeuralNetworks
-    using Statistics, Random
-    using LinearAlgebra
-    using MLDatasets: TemporalBrains
-    using CUDA
-    using cuDNN
-end
- - - -``` -## Dataset: TemporalBrains -```@raw html -
-

The TemporalBrains dataset contains a collection of functional brain connectivity networks from 1000 subjects obtained from resting-state functional MRI data from the Human Connectome Project (HCP). Functional connectivity is defined as the temporal dependence of neuronal activation patterns of anatomically separated brain regions.

The graph nodes represent brain regions and their number is fixed at 102 for each of the 27 snapshots, while the edges, representing functional connectivity, change over time. For each snapshot, the feature of a node represents the average activation of the node during that snapshot. Each temporal graph has a label representing gender ('M' for male and 'F' for female) and age group (22-25, 26-30, 31-35, and 36+). The network's edge weights are binarized, and the threshold is set to 0.6 by default.

- -
brain_dataset = TemporalBrains()
-
dataset TemporalBrains:
-  graphs  =>    1000-element Vector{MLDatasets.TemporalSnapshotsGraph}
- - -

After loading the dataset from the MLDatasets.jl package, we see that there are 1000 graphs and we need to convert them to the TemporalSnapshotsGNNGraph format. So we create a function called data_loader that implements the latter and splits the dataset into the training set that will be used to train the model and the test set that will be used to test the performance of the model.

- -
function data_loader(brain_dataset)
-    graphs = brain_dataset.graphs
-    dataset = Vector{TemporalSnapshotsGNNGraph}(undef, length(graphs))
-    for i in 1:length(graphs)
-        graph = graphs[i]
-        dataset[i] = TemporalSnapshotsGNNGraph(GraphNeuralNetworks.mlgraph2gnngraph.(graph.snapshots))
-        # Add graph and node features
-        for t in 1:27
-            s = dataset[i].snapshots[t]
-            s.ndata.x = [I(102); s.ndata.x']
-        end
-        dataset[i].tgdata.g = Float32.(Flux.onehot(graph.graph_data.g, ["F", "M"]))
-    end
-    # Split the dataset into a 80% training set and a 20% test set
-    train_loader = dataset[1:200]
-    test_loader = dataset[201:250]
-    return train_loader, test_loader
-end;
- - - -

The first part of the data_loader function calls the mlgraph2gnngraph function for each snapshot, which takes the graph and converts it to a GNNGraph. The vector of GNNGraphs is then rewritten to a TemporalSnapshotsGNNGraph.

The second part adds the graph and node features to the temporal graphs, in particular it adds the one-hot encoding of the label of the graph (in this case we directly use the identity matrix) and appends the mean activation of the node of the snapshot (which is contained in the vector dataset[i].snapshots[t].ndata.x, where i is the index indicating the subject and t is the snapshot). For the graph feature, it adds the one-hot encoding of gender.

The last part splits the dataset.

- - -``` -## Model -```@raw html -
-

We now implement a simple model that takes a TemporalSnapshotsGNNGraph as input. It consists of a GINConv applied independently to each snapshot, a GlobalPool to get an embedding for each snapshot, a pooling on the time dimension to get an embedding for the whole temporal graph, and finally a Dense layer.

First, we start by adapting the GlobalPool to the TemporalSnapshotsGNNGraphs.

- -
function (l::GlobalPool)(g::TemporalSnapshotsGNNGraph, x::AbstractVector)
-    h = [reduce_nodes(l.aggr, g[i], x[i]) for i in 1:(g.num_snapshots)]
-    sze = size(h[1])
-    reshape(reduce(hcat, h), sze[1], length(h))
-end
- - - -

Then we implement the constructor of the model, which we call GenderPredictionModel, and the foward pass.

- -
begin
-    struct GenderPredictionModel
-        gin::GINConv
-        mlp::Chain
-        globalpool::GlobalPool
-        f::Function
-        dense::Dense
-    end
-    
-    Flux.@functor GenderPredictionModel
-    
-    function GenderPredictionModel(; nfeatures = 103, nhidden = 128, activation = relu)
-        mlp = Chain(Dense(nfeatures, nhidden, activation), Dense(nhidden, nhidden, activation))
-        gin = GINConv(mlp, 0.5)
-        globalpool = GlobalPool(mean)
-        f = x -> mean(x, dims = 2)
-        dense = Dense(nhidden, 2)
-        GenderPredictionModel(gin, mlp, globalpool, f, dense)
-    end
-    
-    function (m::GenderPredictionModel)(g::TemporalSnapshotsGNNGraph)
-        h = m.gin(g, g.ndata.x)
-        h = m.globalpool(g, h)
-        h = m.f(h)
-        m.dense(h)
-    end
-    
-end
- - - -``` -## Training -```@raw html -
-

We train the model for 100 epochs, using the Adam optimizer with a learning rate of 0.001. We use the logitbinarycrossentropy as the loss function, which is typically used as the loss in two-class classification, where the labels are given in a one-hot format. The accuracy expresses the number of correct classifications.

- -
lossfunction(ŷ, y) = Flux.logitbinarycrossentropy(ŷ, y);
- - -
function eval_loss_accuracy(model, data_loader)
-    error = mean([lossfunction(model(g), g.tgdata.g) for g in data_loader])
-    acc = mean([round(100 * mean(Flux.onecold(model(g)) .==     Flux.onecold(g.tgdata.g)); digits = 2) for g in data_loader])
-    return (loss = error, acc = acc)
-end;
- - -
function train(dataset; usecuda::Bool, kws...)
-
-    if usecuda && CUDA.functional() #check if GPU is available 
-        my_device = gpu
-        @info "Training on GPU"
-    else
-        my_device = cpu
-        @info "Training on CPU"
-    end
-    
-    function report(epoch)
-        train_loss, train_acc = eval_loss_accuracy(model, train_loader)
-        test_loss, test_acc = eval_loss_accuracy(model, test_loader)
-        println("Epoch: $epoch  $((; train_loss, train_acc))  $((; test_loss, test_acc))")
-        return (train_loss, train_acc, test_loss, test_acc)
-    end
-
-    model = GenderPredictionModel() |> my_device
-
-    opt = Flux.setup(Adam(1.0f-3), model)
-
-    train_loader, test_loader = data_loader(dataset)
-    train_loader = train_loader |> my_device
-    test_loader = test_loader |> my_device
-
-    report(0)
-    for epoch in 1:100
-        for g in train_loader
-            grads = Flux.gradient(model) do model
-                ŷ = model(g)
-                lossfunction(vec(ŷ), g.tgdata.g)
-            end
-            Flux.update!(opt, model, grads[1])
-        end
-        if  epoch % 10 == 0
-            report(epoch)
-        end
-    end
-    return model
-end;
-
- - -
train(brain_dataset; usecuda = true)
-
GenderPredictionModel(GINConv(Chain(Dense(103 => 128, relu), Dense(128 => 128, relu)), 0.5), Chain(Dense(103 => 128, relu), Dense(128 => 128, relu)), GlobalPool{typeof(mean)}(Statistics.mean), var"#4#5"(), Dense(128 => 2))
- - -

We set up the training on the GPU because training takes a lot of time, especially when working on the CPU.

- - -``` -## Conclusions -```@raw html -
-

In this tutorial, we implemented a very simple architecture to classify temporal graphs in the context of gender classification using brain data. We then trained the model on the GPU for 100 epochs on the TemporalBrains dataset. The accuracy of the model is approximately 75-80%, but can be improved by fine-tuning the parameters and training on more data.

- - -``` - diff --git a/docs/pluto_output/traffic_prediction.md b/docs/pluto_output/traffic_prediction.md deleted file mode 100644 index a38104e89..000000000 --- a/docs/pluto_output/traffic_prediction.md +++ /dev/null @@ -1,224 +0,0 @@ -```@raw html - - - - - -

In this tutorial, we will learn how to use a recurrent Temporal Graph Convolutional Network (TGCN) to predict traffic in a spatio-temporal setting. Traffic forecasting is the problem of predicting future traffic trends on a road network given historical traffic data, such as, in our case, traffic speed and time of day.

- -
begin
-    using Pkg
-    Pkg.develop("GraphNeuralNetworks")
-    Pkg.add("MLDatasets")
-    Pkg.add("Plots")
-end
- - - -``` -## Import -```@raw html -
-

We start by importing the necessary libraries. We use GraphNeuralNetworks.jl, Flux.jl and MLDatasets.jl, among others.

- -
begin
-    using GraphNeuralNetworks
-    using Flux
-    using Flux.Losses: mae
-    using MLDatasets: METRLA
-    using Statistics
-    using Plots
-end
- - - -``` -## Dataset: METR-LA -```@raw html -
-

We use the METR-LA dataset from the paper Diffusion Convolutional Recurrent Neural Network: Data-driven Traffic Forecasting, which contains traffic data from loop detectors in the highway of Los Angeles County. The dataset contains traffic speed data from March 1, 2012 to June 30, 2012. The data is collected every 5 minutes, resulting in 12 observations per hour, from 207 sensors. Each sensor is a node in the graph, and the edges represent the distances between the sensors.

- -
dataset_metrla = METRLA(; num_timesteps = 3)
-
dataset METRLA:
-  graphs  =>    1-element Vector{MLDatasets.Graph}
- -
 g = dataset_metrla[1]
-
Graph:
-  num_nodes   =>    207
-  num_edges   =>    1722
-  edge_index  =>    ("1722-element Vector{Int64}", "1722-element Vector{Int64}")
-  node_data   =>    (features = "34269-element Vector{Any}", targets = "34269-element Vector{Any}")
-  edge_data   =>    1722-element Vector{Float32}
- - -

edge_data contains the weights of the edges of the graph and node_data contains a node feature vector and a target vector. The latter vectors contain batches of dimension num_timesteps, which means that they contain vectors with the node features and targets of num_timesteps time steps. Two consecutive batches are shifted by one-time step. The node features are the traffic speed of the sensors and the time of the day, and the targets are the traffic speed of the sensors in the next time step. Let's see some examples:

- -
size(g.node_data.features[1])
-
(2, 207, 3)
- - -

The first dimension correspond to the two features (first line the speed value and the second line the time of the day), the second to the nodes and the third to the number of timestep num_timesteps.

- -
size(g.node_data.targets[1])
-
(1, 207, 3)
- - -

In the case of the targets the first dimension is 1 because they store just the speed value.

- -
g.node_data.features[1][:,1,:]
-
2×3 Matrix{Float32}:
-  1.17081    1.11647   1.15888
- -0.876741  -0.87663  -0.87652
- -
g.node_data.features[2][:,1,:]
-
2×3 Matrix{Float32}:
-  1.11647   1.15888  -0.876741
- -0.87663  -0.87652  -0.87641
- -
g.node_data.targets[1][:,1,:]
-
1×3 Matrix{Float32}:
- 1.11647  1.15888  -0.876741
- -
function plot_data(data,sensor)
-    p = plot(legend=false, xlabel="Time (h)", ylabel="Normalized speed")
-    plotdata = []
-    for i in 1:3:length(data)
-        push!(plotdata,data[i][1,sensor,:])
-    end
-    plotdata = reduce(vcat,plotdata)
-    plot!(p, collect(1:length(data)), plotdata, color = :green, xticks =([i for i in 0:50:250], ["$(i)" for i in 0:4:24]))
-    return p
-end
-
plot_data (generic function with 1 method)
- -
plot_data(g.node_data.features[1:288],1)
- - - -

Now let's construct the static graph, the temporal features and targets from the dataset.

- -
begin
-    graph = GNNGraph(g.edge_index; edata = g.edge_data, g.num_nodes)
-    features = g.node_data.features
-    targets = g.node_data.targets
-end;  
- - - -

Now let's construct the train_loader and data_loader.

- -
begin
-    train_loader = zip(features[1:200], targets[1:200])
-    test_loader = zip(features[2001:2288], targets[2001:2288])
-end;
- - - -``` -## Model: T-GCN -```@raw html -
-

We use the T-GCN model from the paper T-GCN: A Temporal Graph Convolutional Network for Traffic Prediction, which consists of a graph convolutional network (GCN) and a gated recurrent unit (GRU). The GCN is used to capture spatial features from the graph, and the GRU is used to capture temporal features from the feature time series.

- -
model = GNNChain(TGCN(2 => 100), Dense(100, 1))
-
GNNChain(Recur(TGCNCell(2 => 100)), Dense(100 => 1))
- - -

- - -``` -## Training -```@raw html -
-

We train the model for 100 epochs, using the Adam optimizer with a learning rate of 0.001. We use the mean absolute error (MAE) as the loss function.

- -
function train(graph, train_loader, model)
-
-    opt = Flux.setup(Adam(0.001), model)
-
-    for epoch in 1:100
-        for (x, y) in train_loader
-            x, y = (x, y)
-            grads = Flux.gradient(model) do model
-                ŷ = model(graph, x)
-                Flux.mae(ŷ, y) 
-            end
-            Flux.update!(opt, model, grads[1])
-        end
-        
-        if epoch % 10 == 0
-            loss = mean([Flux.mae(model(graph,x), y) for (x, y) in train_loader])
-            @show epoch, loss
-        end
-    end
-    return model
-end
-
train (generic function with 1 method)
- -
train(graph, train_loader, model)
-
GNNChain(Recur(TGCNCell(2 => 100)), Dense(100 => 1))
- -
function plot_predicted_data(graph,features,targets, sensor)
-    p = plot(xlabel="Time (h)", ylabel="Normalized speed")
-    prediction = []
-    grand_truth = []
-    for i in 1:3:length(features)
-        push!(grand_truth,targets[i][1,sensor,:])
-        push!(prediction, model(graph, features[i])[1,sensor,:]) 
-    end
-    prediction = reduce(vcat,prediction)
-    grand_truth = reduce(vcat, grand_truth)
-    plot!(p, collect(1:length(features)), grand_truth, color = :blue, label = "Grand Truth", xticks =([i for i in 0:50:250], ["$(i)" for i in 0:4:24]))
-    plot!(p, collect(1:length(features)), prediction, color = :red, label= "Prediction")
-    return p
-end
-
plot_predicted_data (generic function with 1 method)
- -
plot_predicted_data(graph,features[301:588],targets[301:588], 1)
- - -
accuracy(ŷ, y) = 1 - Statistics.norm(y-ŷ)/Statistics.norm(y)
-
accuracy (generic function with 1 method)
- -
mean([accuracy(model(graph,x), y) for (x, y) in test_loader])
-
0.42344558f0
- - -

The accuracy is not very good but can be improved by training using more data. We used a small subset of the dataset for this tutorial because of the computational cost of training the model. From the plot of the predictions, we can see that the model is able to capture the general trend of the traffic speed, but it is not able to capture the peaks of the traffic.

- - -``` -## Conclusion -```@raw html -
-

In this tutorial, we learned how to use a recurrent temporal graph convolutional network to predict traffic in a spatio-temporal setting. We used the TGCN model, which consists of a graph convolutional network (GCN) and a gated recurrent unit (GRU). We then trained the model for 100 epochs on a small subset of the METR-LA dataset. The accuracy of the model is not very good, but it can be improved by training on more data.

- - -``` - diff --git a/docs/src/dev.md b/docs/src/dev.md index 88ec12f5a..284146c67 100644 --- a/docs/src/dev.md +++ b/docs/src/dev.md @@ -10,6 +10,24 @@ pkg> activate . pkg> dev ./GNNGraphs ``` + + +For generating the documentation locally instead +``` +cd docs +julia +``` +```julia +(@v1.10) pkg> activate . + Activating project at `~/.julia/dev/GraphNeuralNetworks/docs` + +(docs) pkg> dev ../ ../GNNGraphs/ + Resolving package versions... + No Changes to `~/.julia/dev/GraphNeuralNetworks/docs/Project.toml` + No Changes to `~/.julia/dev/GraphNeuralNetworks/docs/Manifest.toml` + +julia> include("make.jl") +``` ## Benchmarking You can benchmark the effect on performance of your commits using the script `perf/perf.jl`. diff --git a/docs/tutorials/introductory_tutorials/temporal_graph_classification_pluto.jl b/docs/tutorials_broken/temporal_graph_classification_pluto.jl similarity index 100% rename from docs/tutorials/introductory_tutorials/temporal_graph_classification_pluto.jl rename to docs/tutorials_broken/temporal_graph_classification_pluto.jl diff --git a/docs/tutorials/introductory_tutorials/traffic_prediction.jl b/docs/tutorials_broken/traffic_prediction.jl similarity index 100% rename from docs/tutorials/introductory_tutorials/traffic_prediction.jl rename to docs/tutorials_broken/traffic_prediction.jl From 7b339fabd486bd55a79e796251198b34a7e936e0 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 10:25:34 +0200 Subject: [PATCH 04/10] polishing --- GNNlib/Project.toml | 6 ------ GNNlib/src/GNNlib.jl | 2 -- Project.toml | 20 ++++---------------- src/GraphNeuralNetworks.jl | 4 +--- src/layers/heteroconv.jl | 2 +- src/layers/pool.jl | 2 -- test/runtests.jl | 1 - 7 files changed, 6 insertions(+), 31 deletions(-) diff --git a/GNNlib/Project.toml b/GNNlib/Project.toml index 62f0cb4e2..2056c8110 100644 --- a/GNNlib/Project.toml +++ b/GNNlib/Project.toml @@ -12,14 +12,11 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" -MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" -NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" [weakdeps] CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" @@ -38,14 +35,11 @@ KrylovKit = "0.6, 0.7" LinearAlgebra = "1" MLDatasets = "0.7" MLUtils = "0.4" -MacroTools = "0.5" NNlib = "0.9" -NearestNeighbors = "0.4" Random = "1" Reexport = "1" SparseArrays = "1" Statistics = "1" -StatsBase = "0.34" cuDNN = "1" julia = "1.10" diff --git a/GNNlib/src/GNNlib.jl b/GNNlib/src/GNNlib.jl index 8905d8504..f8d47d512 100644 --- a/GNNlib/src/GNNlib.jl +++ b/GNNlib/src/GNNlib.jl @@ -2,8 +2,6 @@ module GNNlib using Statistics: mean using LinearAlgebra, Random -using Base: tail -using MacroTools: @forward using MLUtils using NNlib using NNlib: scatter, gather diff --git a/Project.toml b/Project.toml index 1b122bffd..5506b943a 100644 --- a/Project.toml +++ b/Project.toml @@ -4,24 +4,17 @@ authors = ["Carlo Lucibello and contributors"] version = "0.6.19" [deps] -Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" GNNGraphs = "aed8fd31-079b-4b5a-b342-a13352159b8c" -Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" -KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" -NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" [weakdeps] CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" @@ -30,26 +23,18 @@ CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" GraphNeuralNetworksCUDAExt = "CUDA" [compat] -Adapt = "3, 4" CUDA = "4, 5" ChainRulesCore = "1" DataStructures = "0.18" Flux = "0.14" Functors = "0.4.1" -Graphs = "1.4" GNNGraphs = "1.0" -KrylovKit = "0.6, 0.7, 0.8" LinearAlgebra = "1" -MLDatasets = "0.7" -MLUtils = "0.4" MacroTools = "0.5" NNlib = "0.9" -NearestNeighbors = "0.4" Random = "1" Reexport = "1" -SparseArrays = "1" Statistics = "1" -StatsBase = "0.34" cuDNN = "1" julia = "1.10" @@ -59,11 +44,14 @@ CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" InlineStrings = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458" +MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" cuDNN = "02a925ec-e4fe-4b08-9a7e-0d78e3d38ccd" [targets] -test = ["Test", "Adapt", "DataFrames", "InlineStrings", "Zygote", "FiniteDifferences", "ChainRulesTestUtils", "MLDatasets", "CUDA", "cuDNN"] +test = ["Test", "MLUtils", "Adapt", "DataFrames", "InlineStrings", "SparseArrays", "Graphs", "Zygote", "FiniteDifferences", "ChainRulesTestUtils", "MLDatasets", "CUDA", "cuDNN"] diff --git a/src/GraphNeuralNetworks.jl b/src/GraphNeuralNetworks.jl index 3d80b1aaf..0debce93e 100644 --- a/src/GraphNeuralNetworks.jl +++ b/src/GraphNeuralNetworks.jl @@ -2,16 +2,14 @@ module GraphNeuralNetworks using Statistics: mean using LinearAlgebra, Random -using Base: tail using Flux using Flux: glorot_uniform, leakyrelu, GRUCell, @functor, batch using MacroTools: @forward -using MLUtils using NNlib using NNlib: scatter, gather using ChainRulesCore using Reexport -using SparseArrays, Graphs # not needed but if removed Documenter will complain +using DataStructures: nlargest @reexport using GNNGraphs using GNNGraphs: COO_T, ADJMAT_T, SPARSE_T, diff --git a/src/layers/heteroconv.jl b/src/layers/heteroconv.jl index ec75c8922..b2603e455 100644 --- a/src/layers/heteroconv.jl +++ b/src/layers/heteroconv.jl @@ -78,7 +78,7 @@ function _reduceby_node_t(aggr, outs, ntypes) end # workaround to provide the aggregation once per unique node type, # gradient is not needed - unique_ntypes = Flux.ChainRulesCore.ignore_derivatives() do + unique_ntypes = ChainRulesCore.ignore_derivatives() do unique(ntypes) end vals = [_reduce(node_t) for node_t in unique_ntypes] diff --git a/src/layers/pool.jl b/src/layers/pool.jl index 9b208d0dc..4201a8d15 100644 --- a/src/layers/pool.jl +++ b/src/layers/pool.jl @@ -1,5 +1,3 @@ -using DataStructures: nlargest - @doc raw""" GlobalPool(aggr) diff --git a/test/runtests.jl b/test/runtests.jl index 866d7b2f1..bcb287967 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,7 +8,6 @@ using Flux: gpu, @functor using LinearAlgebra, Statistics, Random using NNlib import MLUtils -import StatsBase using SparseArrays using Graphs using Zygote From 5accc3e20e4c000c370878b63aa737e864a4f0ea Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 16:04:50 +0200 Subject: [PATCH 05/10] mlutils --- Project.toml | 5 +++-- src/GraphNeuralNetworks.jl | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 5506b943a..a8bdcf02f 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" GNNGraphs = "aed8fd31-079b-4b5a-b342-a13352159b8c" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" @@ -31,6 +32,7 @@ Functors = "0.4.1" GNNGraphs = "1.0" LinearAlgebra = "1" MacroTools = "0.5" +MLUtils = "0.4" NNlib = "0.9" Random = "1" Reexport = "1" @@ -47,11 +49,10 @@ FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" InlineStrings = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458" -MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" cuDNN = "02a925ec-e4fe-4b08-9a7e-0d78e3d38ccd" [targets] -test = ["Test", "MLUtils", "Adapt", "DataFrames", "InlineStrings", "SparseArrays", "Graphs", "Zygote", "FiniteDifferences", "ChainRulesTestUtils", "MLDatasets", "CUDA", "cuDNN"] +test = ["Test", "Adapt", "DataFrames", "InlineStrings", "SparseArrays", "Graphs", "Zygote", "FiniteDifferences", "ChainRulesTestUtils", "MLDatasets", "CUDA", "cuDNN"] diff --git a/src/GraphNeuralNetworks.jl b/src/GraphNeuralNetworks.jl index 0debce93e..c74032273 100644 --- a/src/GraphNeuralNetworks.jl +++ b/src/GraphNeuralNetworks.jl @@ -10,6 +10,7 @@ using NNlib: scatter, gather using ChainRulesCore using Reexport using DataStructures: nlargest +using MLUtils: zeros_like @reexport using GNNGraphs using GNNGraphs: COO_T, ADJMAT_T, SPARSE_T, From 0afda36a0213e3096d18b5bcd37c67c770ae605c Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 16:09:35 +0200 Subject: [PATCH 06/10] workflow --- .github/workflows/docs.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 68e3fda9b..49a32d776 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,16 +15,12 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: '1.10.4' - - name: Install dependencies - shell: julia --project=docs/ {0} - run: | - using Pkg - # dev mono repo versions - pkg"registry up" - Pkg.update() - pkg"dev ./GNNGraphs ." - - name: Build and deploy + - name: Install dependencies, build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key - run: julia --project=docs/ docs/make.jl + shell: julia --project=docs/ {0} + run: | + using Pkg + pkg"dev ./ ./GNNGraphs" + include("docs/make.jl") From 5d8944930a1720ee8b9fb579de59afc9160f3100 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 16:38:52 +0200 Subject: [PATCH 07/10] fixes --- docs/make.jl | 6 +++++- docs/src/api/gnngraph.md | 7 +++---- docs/src/api/heterograph.md | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 36314301f..e0a40ee13 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,5 +1,9 @@ -using Flux, NNlib, GraphNeuralNetworks, Graphs, SparseArrays +using GraphNeuralNetworks using GNNGraphs +using Flux +using NNlib +using Graphs +using SparseArrays using Pluto, PlutoStaticHTML # for tutorials using Documenter, DemoCards using DocumenterInterLinks diff --git a/docs/src/api/gnngraph.md b/docs/src/api/gnngraph.md index 61d35e9f5..de6fc1872 100644 --- a/docs/src/api/gnngraph.md +++ b/docs/src/api/gnngraph.md @@ -1,5 +1,5 @@ ```@meta -CurrentModule = GraphNeuralNetworks +CurrentModule = GNNGraphs ``` # GNNGraph @@ -41,8 +41,7 @@ Private = false ``` ```@docs -Graphs.neighbors -Graphs.has_edge +Graphs.neighbors(::GNNGraph, ::Integer) ``` ## Transform @@ -79,7 +78,7 @@ Private = false ``` ```@docs -Graphs.intersect +Base.intersect ``` ## Sampling diff --git a/docs/src/api/heterograph.md b/docs/src/api/heterograph.md index aac8d5101..db03c74a4 100644 --- a/docs/src/api/heterograph.md +++ b/docs/src/api/heterograph.md @@ -11,6 +11,10 @@ Pages = ["gnnheterograph.jl"] Private = false ``` +```@docs +Graphs.has_edge(::GNNHeteroGraph, ::Tuple{Symbol, Symbol, Symbol}, ::Integer, ::Integer) +``` + ## Heterogeneous Graph Convolutions Heterogeneous graph convolutions are implemented in the type [`HeteroGraphConv`](@ref). From 289c7f073bb2bf7dbce3a9e356a860190ca87829 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 16:58:27 +0200 Subject: [PATCH 08/10] workflow --- .github/workflows/docs.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 49a32d776..5a06f63dd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,12 +15,15 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: '1.10.4' - - name: Install dependencies, build and deploy - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key + - name: Install dependencies shell: julia --project=docs/ {0} run: | using Pkg - pkg"dev ./ ./GNNGraphs" - include("docs/make.jl") + Pkg.develop(path="./GNNGraphs") + Pkg.develop(path=".") + Pkg.instantiate() + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + run: julia --project=docs/ docs/make.jl \ No newline at end of file From 61f23b61893bead5b215664c85212fadd752cdb4 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 17:09:24 +0200 Subject: [PATCH 09/10] anouther attempt --- .github/workflows/docs.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5a06f63dd..7c07a6369 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,12 +16,10 @@ jobs: with: version: '1.10.4' - name: Install dependencies - shell: julia --project=docs/ {0} - run: | - using Pkg - Pkg.develop(path="./GNNGraphs") - Pkg.develop(path=".") - Pkg.instantiate() + run: julia --project=docs/ -e ' + using Pkg; + Pkg.develop([PackageSpec(path=joinpath(pwd(), "GNNGraphs"))]); + Pkg.instantiate();' - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token From 66a82f191a127750d86a554d88fb056227fb5517 Mon Sep 17 00:00:00 2001 From: Carlo Lucibello Date: Thu, 25 Jul 2024 17:24:53 +0200 Subject: [PATCH 10/10] try again --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7c07a6369..411533092 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: - name: Install dependencies run: julia --project=docs/ -e ' using Pkg; - Pkg.develop([PackageSpec(path=joinpath(pwd(), "GNNGraphs"))]); + Pkg.develop([PackageSpec(path=pwd()), PackageSpec(path=joinpath(pwd(), "GNNGraphs"))]); Pkg.instantiate();' - name: Build and deploy env: