From c9a5200a807e16b8cef6fbf3ec28a6c6803874f4 Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Thu, 5 Nov 2026 10:05:37 +0530 Subject: [PATCH 1/6] Fix Zygote gradient through add_edges --- GNNGraphs/src/utils.jl | 9 ++++++--- GNNGraphs/test/transform.jl | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/GNNGraphs/src/utils.jl b/GNNGraphs/src/utils.jl index 9b12f43f1..e75e53e35 100644 --- a/GNNGraphs/src/utils.jl +++ b/GNNGraphs/src/utils.jl @@ -65,10 +65,13 @@ function cat_features(x1::NamedTuple, x2::NamedTuple) end function cat_features(x1::Dict{Symbol, T}, x2::Dict{Symbol, T}) where {T} - sort(collect(keys(x1))) == sort(collect(keys(x2))) || + keys1, keys2 = CRC.ignore_derivatives() do + sort(collect(keys(x1))), sort(collect(keys(x2))) + end + keys1 == keys2 || @error "cannot concatenate feature data with different keys" - return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys(x1)]...) + return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys1]...) end function cat_features(x::Dict) @@ -391,4 +394,4 @@ function color_refinement(g::GNNGraph, x0::AbstractVector{<:Integer}) end num_colors = length(union(x)) return x, num_colors, niters -end \ No newline at end of file +end diff --git a/GNNGraphs/test/transform.jl b/GNNGraphs/test/transform.jl index 508cc6b16..5a6fc4d6f 100644 --- a/GNNGraphs/test/transform.jl +++ b/GNNGraphs/test/transform.jl @@ -295,6 +295,24 @@ end gnew = add_edges(g, (snew, tnew, wnew)) @test get_edge_weight(gnew) == [w; wnew] end + + @testset "differentiate through added edge weights" begin + s = [1, 2] + t = [2, 3] + w = Float32[0.5, 0.7] + g = GNNGraph((s, t, w)) + snew = [1, 2] + tnew = [2, 3] + wnew = Float32[0.1, 0.2] + + loss_fn(wnew) = sum(get_edge_weight(add_edges(g, (snew, tnew, wnew)))) + gnew = add_edges(g, (snew, tnew, wnew)) + + @test gnew.num_edges == 4 + @test edge_index(gnew) == ([1, 2, 1, 2], [2, 3, 2, 3]) + @test get_edge_weight(gnew) == Float32[0.5, 0.7, 0.1, 0.2] + @test gradient(loss_fn, wnew)[1] == ones(Float32, 2) + end end end end From 8db2a76b52689b1148b0f18f713b53162123b9e9 Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Thu, 5 Nov 2026 12:48:33 +0530 Subject: [PATCH 2/6] Fix sampling and edge-weight differentiation tests --- GNNGraphs/src/sampling.jl | 1 + GNNGraphs/src/utils.jl | 13 +++++++++---- GNNGraphs/test/sampling.jl | 22 ++++++++++++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/GNNGraphs/src/sampling.jl b/GNNGraphs/src/sampling.jl index 88a7b2fea..4f1e568a6 100644 --- a/GNNGraphs/src/sampling.jl +++ b/GNNGraphs/src/sampling.jl @@ -177,6 +177,7 @@ function sample_neighbors(g::GNNGraph{<:COO_T}, nodes, K = -1; @assert dir ∈ (:in, :out) _, eidlist = adjacency_list(g, nodes; dir, with_eid = true) for i in 1:length(eidlist) + isempty(eidlist[i]) && continue if replace k = K > 0 ? K : length(eidlist[i]) else diff --git a/GNNGraphs/src/utils.jl b/GNNGraphs/src/utils.jl index e75e53e35..2a9554ae1 100644 --- a/GNNGraphs/src/utils.jl +++ b/GNNGraphs/src/utils.jl @@ -65,13 +65,18 @@ function cat_features(x1::NamedTuple, x2::NamedTuple) end function cat_features(x1::Dict{Symbol, T}, x2::Dict{Symbol, T}) where {T} - keys1, keys2 = CRC.ignore_derivatives() do - sort(collect(keys(x1))), sort(collect(keys(x2))) + if CRC.ignore_derivatives() do + isempty(x1) && isempty(x2) end - keys1 == keys2 || + return CRC.ignore_derivatives() do + Dict{Symbol, T}() + end + end + + sort(collect(keys(x1))) == sort(collect(keys(x2))) || @error "cannot concatenate feature data with different keys" - return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys1]...) + return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys(x1)]...) end function cat_features(x::Dict) diff --git a/GNNGraphs/test/sampling.jl b/GNNGraphs/test/sampling.jl index 557a8ae89..92a6d1522 100644 --- a/GNNGraphs/test/sampling.jl +++ b/GNNGraphs/test/sampling.jl @@ -25,9 +25,9 @@ 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) + @test sg.num_edges == sum(isempty(adjlist[i]) ? 0 : K for i in nodes) + @test size(sg.edata.EID) == (sg.num_edges,) s, t = edge_index(sg) @test all(s .∈ Ref(nodes)) for i in nodes @@ -47,6 +47,24 @@ @test sg.ndata.x1 == g.ndata.x1[sg.ndata.NID] @test length(union(sg.ndata.NID)) == length(sg.ndata.NID) end + + @testset "zero-degree nodes with replacement" begin + g = GNNGraph(([1], [2]); num_nodes = 3) + + for (dir, node) in ((:in, 1), (:out, 2)) + sg = sample_neighbors(g, [node], 2; dir, replace = true) + @test sg.num_nodes == 3 + @test sg.num_edges == 0 + @test isempty(sg.edata.EID) + @test edge_index(sg) == (Int[], Int[]) + end + + for (dir, node) in ((:in, 2), (:out, 1)) + sg = sample_neighbors(g, [node], 2; dir, replace = true) + @test sg.num_edges == 2 + @test sg.edata.EID == [1, 1] + end + end end @testitem "induced_subgraph" setup=[GraphsTestModule] begin From f34a5865d8924a6ed9414b0b517ee38e67c07955 Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Thu, 5 Nov 2026 13:03:48 +0530 Subject: [PATCH 3/6] Avoid differentiating empty graph metadata --- GNNGraphs/src/datastore.jl | 12 +++++++++++- GNNGraphs/src/utils.jl | 13 ++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/GNNGraphs/src/datastore.jl b/GNNGraphs/src/datastore.jl index b38ef0acc..bad7ae2db 100644 --- a/GNNGraphs/src/datastore.jl +++ b/GNNGraphs/src/datastore.jl @@ -172,7 +172,17 @@ function cat_features(ds1::DataStore, ds2::DataStore) n1, n2 = getn(ds1), getn(ds2) n1 = n1 >= 0 ? n1 : 1 n2 = n2 >= 0 ? n2 : 1 - return DataStore(n1 + n2, cat_features(getdata(ds1), getdata(ds2))) + data1, data2 = getdata(ds1), getdata(ds2) + if CRC.ignore_derivatives() do + isempty(data1) && isempty(data2) + end + # Empty stores have no differentiable values; keep their structural + # reconstruction out of automatic differentiation. + return CRC.ignore_derivatives() do + DataStore(n1 + n2, Dict{Symbol, Any}()) + end + end + return DataStore(n1 + n2, cat_features(data1, data2)) end function cat_features(dss::AbstractVector{DataStore}; kws...) diff --git a/GNNGraphs/src/utils.jl b/GNNGraphs/src/utils.jl index 2a9554ae1..e75e53e35 100644 --- a/GNNGraphs/src/utils.jl +++ b/GNNGraphs/src/utils.jl @@ -65,18 +65,13 @@ function cat_features(x1::NamedTuple, x2::NamedTuple) end function cat_features(x1::Dict{Symbol, T}, x2::Dict{Symbol, T}) where {T} - if CRC.ignore_derivatives() do - isempty(x1) && isempty(x2) + keys1, keys2 = CRC.ignore_derivatives() do + sort(collect(keys(x1))), sort(collect(keys(x2))) end - return CRC.ignore_derivatives() do - Dict{Symbol, T}() - end - end - - sort(collect(keys(x1))) == sort(collect(keys(x2))) || + keys1 == keys2 || @error "cannot concatenate feature data with different keys" - return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys(x1)]...) + return Dict{Symbol, T}([k => cat_features(x1[k], x2[k]) for k in keys1]...) end function cat_features(x::Dict) From 2333d5a9395e982d0ae491de33cf78e85a03789e Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Sat, 7 Nov 2026 22:50:19 +0530 Subject: [PATCH 4/6] Test gradients through added edge metadata --- GNNGraphs/src/datastore.jl | 10 ++-------- GNNGraphs/test/transform.jl | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/GNNGraphs/src/datastore.jl b/GNNGraphs/src/datastore.jl index bad7ae2db..7604c5631 100644 --- a/GNNGraphs/src/datastore.jl +++ b/GNNGraphs/src/datastore.jl @@ -173,14 +173,8 @@ function cat_features(ds1::DataStore, ds2::DataStore) n1 = n1 >= 0 ? n1 : 1 n2 = n2 >= 0 ? n2 : 1 data1, data2 = getdata(ds1), getdata(ds2) - if CRC.ignore_derivatives() do - isempty(data1) && isempty(data2) - end - # Empty stores have no differentiable values; keep their structural - # reconstruction out of automatic differentiation. - return CRC.ignore_derivatives() do - DataStore(n1 + n2, Dict{Symbol, Any}()) - end + if isempty(data1) && isempty(data2) + return CRC.ignore_derivatives(() -> DataStore(n1 + n2, Dict{Symbol, Any}())) end return DataStore(n1 + n2, cat_features(data1, data2)) end diff --git a/GNNGraphs/test/transform.jl b/GNNGraphs/test/transform.jl index 5a6fc4d6f..64723fb8e 100644 --- a/GNNGraphs/test/transform.jl +++ b/GNNGraphs/test/transform.jl @@ -313,6 +313,20 @@ end @test get_edge_weight(gnew) == Float32[0.5, 0.7, 0.1, 0.2] @test gradient(loss_fn, wnew)[1] == ones(Float32, 2) end + + @testset "differentiate through added edge data" begin + s = [1, 2] + t = [2, 3] + edata = (x = reshape(Float32[0.5, 0.7], 1, :),) + g = GNNGraph((s, t); edata) + snew = [1, 2] + tnew = [2, 3] + xnew = reshape(Float32[0.1, 0.2], 1, :) + + loss_fn(xnew) = sum(add_edges(g, (snew, tnew); edata = (x = xnew,)).edata.x) + + @test gradient(loss_fn, xnew)[1] == ones(Float32, size(xnew)) + end end end end From 19136f30abec05681cd5d493cea9ad420dda98a1 Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Sat, 7 Nov 2026 23:08:43 +0530 Subject: [PATCH 5/6] Document pending GNNGraphs fixes --- CHANGELOG.md | 6 ++++++ GNNGraphs/Project.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffdbc7993..7ffdaa0a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the packages adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries link to the pull request that introduced them. +## GNNGraphs.jl 1.6.1-DEV + +**Fixed** +- Fixed Zygote differentiation through `add_edges` with non-empty edge metadata by iterating over a collected vector of metadata keys. `sample_neighbors(...; replace=true)` now skips zero-degree nodes instead of trying to sample from an empty neighbor list ([#729]). + ## GNNGraphs.jl 1.6.0 - 2026-11-04 **Added** @@ -247,4 +252,5 @@ Lux implementations of the graph convolutional, pooling, and temporal layers [#706]: https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/706 [#722]: https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/722 [#718]: https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/718 +[#729]: https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/729 [FluxML/Zygote.jl#1662]: https://github.com/FluxML/Zygote.jl/issues/1662 diff --git a/GNNGraphs/Project.toml b/GNNGraphs/Project.toml index 29c2d2272..804f057a5 100644 --- a/GNNGraphs/Project.toml +++ b/GNNGraphs/Project.toml @@ -1,6 +1,6 @@ name = "GNNGraphs" uuid = "aed8fd31-079b-4b5a-b342-a13352159b8c" -version = "1.6.0" +version = "1.6.1-DEV" authors = ["Carlo Lucibello and contributors"] [workspace] From a7749908dfd5fa004c5899dffd65259dfbfb66d8 Mon Sep 17 00:00:00 2001 From: Rahul dhaked Date: Sun, 8 Nov 2026 13:04:49 +0530 Subject: [PATCH 6/6] Fix GNNLux MegNetConv state propagation --- .gitignore | 2 + DEVELOPMENT.md | 27 ++ GNNLux/reproduce_issue_732.jl | 27 ++ GNNLux/src/layers/conv.jl | 6 +- GNNLux/test/layers/conv.jl | 11 + docs/CONTRIBUTION_ROADMAP.md | 100 ++++++ docs/CONTRIBUTOR_RESEARCH_REPORT.md | 482 ++++++++++++++++++++++++++++ docs/CURRENT_ISSUE_REGISTER.md | 45 +++ docs/PR_READY_AUDIT_BACKLOG.md | 144 +++++++++ scripts/julia.ps1 | 10 + scripts/setup-dev.ps1 | 52 +++ 11 files changed, 904 insertions(+), 2 deletions(-) create mode 100644 DEVELOPMENT.md create mode 100644 GNNLux/reproduce_issue_732.jl create mode 100644 docs/CONTRIBUTION_ROADMAP.md create mode 100644 docs/CONTRIBUTOR_RESEARCH_REPORT.md create mode 100644 docs/CURRENT_ISSUE_REGISTER.md create mode 100644 docs/PR_READY_AUDIT_BACKLOG.md create mode 100644 scripts/julia.ps1 create mode 100644 scripts/setup-dev.ps1 diff --git a/.gitignore b/.gitignore index e0c3ad5d4..ac4d91f1b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ *.swo Manifest.toml .vscode +.tools/ +.julia-depot/ LocalPreferences.toml .DS_Store test.jl diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..341c51116 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,27 @@ +# Local development setup + +Run PowerShell from the repository root: + +```powershell +.\scripts\setup-dev.ps1 +``` + +The setup installs Julia 1.13.3 as a portable copy under `.tools`, verifies its +SHA-256 checksum, then installs the workspace, package, test, docs, and +JuliaFormatter environments. Julia packages, compiled caches, and artifacts +stay in `.julia-depot`. Neither setup nor the launcher edits the machine PATH or +uses the global Julia depot. Both directories are ignored by Git. + +Use the repo-local Julia launcher for subsequent work: + +```powershell +.\scripts\julia.ps1 --version +.\scripts\julia.ps1 --project=GNNGraphs -e 'using Pkg; Pkg.test("GNNGraphs")' +.\scripts\julia.ps1 --project=GraphNeuralNetworks -e 'using Pkg; Pkg.test("GraphNeuralNetworks")' +.\scripts\julia.ps1 --project=GNNGraphs/docs GNNGraphs/docs/make.jl +.\scripts\julia.ps1 --project=.tools/devtools -e 'using JuliaFormatter; format(".")' +``` + +The full package test environments are installed, but setup does not execute +the test suites. GPU backends remain opt-in; install and select the backend +required by the machine before running GPU-tagged tests. diff --git a/GNNLux/reproduce_issue_732.jl b/GNNLux/reproduce_issue_732.jl new file mode 100644 index 000000000..1b9b340dd --- /dev/null +++ b/GNNLux/reproduce_issue_732.jl @@ -0,0 +1,27 @@ +using Random +using GNNLux +using Lux +using LuxCore + +# Small directed graph with two nodes and two edges. +g = GNNGraph([1, 2], [2, 1]) +in_dims, out_dims = 3, 4 +x = randn(MersenneTwister(1), Float32, in_dims, g.num_nodes) +e = randn(MersenneTwister(2), Float32, in_dims, g.num_edges) + +# MEGNet's edge update receives source node, destination node, and edge +# features (3 * in_dims); BatchNorm keeps running statistics in its state. +ϕe = Chain(Dense(3in_dims => out_dims), BatchNorm(out_dims)) +ϕv = Chain(Dense(in_dims + out_dims => out_dims), Dense(out_dims => out_dims)) +l = MEGNetConv(in_dims, out_dims, ϕe, ϕv) + +ps, st = LuxCore.setup(MersenneTwister(3), l) +edge_state_before = st.ϕe.layer_2 +(x_new, e_new), st_new = l(g, x, e, ps, st) +edge_state_after = st_new.ϕe.layer_2 + +println("Output shapes: nodes=$(size(x_new)), edges=$(size(e_new))") +println("Input BatchNorm running_mean: ", edge_state_before.running_mean) +println("Returned BatchNorm running_mean: ", edge_state_after.running_mean) +println("State changed: ", edge_state_after.running_mean != edge_state_before.running_mean) +println("Returned full state equals input state: ", st_new == st) diff --git a/GNNLux/src/layers/conv.jl b/GNNLux/src/layers/conv.jl index 236b9dfc6..f7b8ee925 100644 --- a/GNNLux/src/layers/conv.jl +++ b/GNNLux/src/layers/conv.jl @@ -1541,9 +1541,11 @@ end function (l::MEGNetConv)(g, x, e, ps, st) ϕe = StatefulLuxLayer{true}(l.ϕe, ps.ϕe, _getstate(st, :ϕe)) - ϕv = StatefulLuxLayer{true}(l.ϕv, ps.ϕv, _getstate(st, :ϕv)) + ϕv = StatefulLuxLayer{true}(l.ϕv, ps.ϕv, _getstate(st, :ϕv)) m = (; ϕe, ϕv, aggr=l.aggr) - return GNNlib.megnet_conv(m, g, x, e), st + y = GNNlib.megnet_conv(m, g, x, e) + stnew = (; ϕe = _getstate(ϕe), ϕv = _getstate(ϕv)) + return y, stnew end diff --git a/GNNLux/test/layers/conv.jl b/GNNLux/test/layers/conv.jl index e9a93cfb6..4e997ecb1 100644 --- a/GNNLux/test/layers/conv.jl +++ b/GNNLux/test/layers/conv.jl @@ -107,6 +107,17 @@ @test size(x_new) == (out_dims, g.num_nodes) @test size(e_new) == (out_dims, g.num_edges) + + ϕe = Chain(Dense(3in_dims => out_dims), BatchNorm(out_dims)) + ϕv = Chain(Dense(in_dims + out_dims => out_dims), Dense(out_dims => out_dims)) + l = MEGNetConv(in_dims, out_dims, ϕe, ϕv) + ps, st = LuxCore.setup(rng, l) + _, st_new = l(g, x, e, ps, st) + + @test typeof(st_new) == typeof(st) + @test st_new.ϕe.layer_2.running_mean != st.ϕe.layer_2.running_mean + _, st_newer = l(g, x, e, ps, st_new) + @test typeof(st_newer) == typeof(st) end @testset "NNConv" begin diff --git a/docs/CONTRIBUTION_ROADMAP.md b/docs/CONTRIBUTION_ROADMAP.md new file mode 100644 index 000000000..17a15050c --- /dev/null +++ b/docs/CONTRIBUTION_ROADMAP.md @@ -0,0 +1,100 @@ +# 30 / 60 / 90-Day Contribution Roadmap + +This plan assumes about 6–8 focused hours each week alongside study/work. Adjust the pace to your schedule. The goal is to learn by making small, reviewed changes; GSoC 2027 opportunities and mentor availability are not yet known. + +## Days 1–30: Build a working mental model + +### Week 1 — Julia and local workflow + +- Read `AGENTS.md`, `README.md`, and `DEVELOPMENT.md`. +- Use `scripts/julia.ps1` to inspect `GNNGraph`, `DataStore`, and the Julia package environment. +- Learn multiple dispatch, parametric structs, keyword arguments, broadcasting, named tuples, and Julia package extensions from the Julia manual. +- Run one focused `@testitem`; then run the `GNNGraphs` CPU suite when data/network requirements are available. + +**Deliverable:** personal notes explaining `features × nodes`, COO source/target arrays, and how a test item is launched. + +### Week 2 — Graph representation and operations + +- Trace `GNNGraph` construction from `GNNGraphs/src/gnngraph.jl` into `to_coo`, `to_sparse`, `to_dense`, and `normalize_graphdata`. +- Read `query.jl` for `edge_index`, `degree`, and adjacency semantics. +- Read `transform.jl` batch/unbatch, self-loop, remove-edge, and `getgraph` functions. +- Run the matching graph tests and inspect any `broken` annotations before interpreting results. + +**Deliverable:** a one-page graph API map with one homogeneous and one heterogeneous example. + +### Week 3 — Message passing and one layer + +- Read `GNNlib/src/msgpass.jl`; step through `apply_edges` → message function → `aggregate_neighbors`. +- Compare a generic message closure with `copy_xj` and identify when GNNlib can use a fast path. +- Follow GCN from Flux and Lux frontend calls to `GNNlib.gcn_conv`, then to GNNGraphs `degree`. +- Compare the source code against layer docstrings and hand-calculate a tiny graph result. + +**Deliverable:** a trace diagram for GCN and a small manual reference calculation. + +### Week 4 — First small contribution + +- Read an open issue page and check for current branches/PRs before choosing work. +- Recommended starting scope: a missing expected-value test, a documentation correction, or a reproducible error-message improvement. +- Ask maintainers on the issue if the task is still wanted; do not duplicate an existing PR. +- Write the regression test first, make the smallest change, run the package suite, and open a draft PR if appropriate. + +**Deliverable:** one small reviewable PR or a maintainer-confirmed test plan. + +## Days 31–60: Own one subsystem + +### Weeks 5–6 — Learn the test and AD model + +- Study `test_module.jl` for finite differences, Zygote, Mooncake and device selection. +- Pick one layer and inspect primal output tests plus gradient tests. Add a hand-computed reference to your local learning branch if a formula is otherwise only shape/gradient checked. +- Learn how `@testitem` setup modules and `GNN_TEST_*` flags select CPU/GPU execution. +- Read relevant Mooncake/Enzyme extensions and the changelog entries for prior AD fixes. + +**Deliverable:** a backend/layer/graph-format test matrix for one operation. + +### Weeks 7–8 — Reproduce and scope a real issue + +- Check current issue #710 or #640 status and linked PRs; verify the versions/OS/devices in the issue before running it. +- Build a minimal script outside production source. Capture Julia/package versions and stack trace. +- Split graph data, message passing, frontend and backend responsibilities. Use `@which`, `methods`, and `@code_warntype` as appropriate; do not treat type instability output as proof of a bug by itself. +- Present reproduction and test plan to maintainers before proposing a backend-wide change. + +**Deliverable:** a minimal reproducible example, root-cause evidence, and scoped implementation plan. + +## Days 61–90: Deliver a medium contribution and prepare a proposal + +### Weeks 9–10 — Implement with regression coverage + +- Implement in the lowest package that owns the behavior: graph semantics in GNNGraphs, shared math in GNNlib, training/state wrapper in Flux/Lux frontend, backend-specialized behavior in an extension. +- Keep CPU behavior and optional backend behavior tested separately. +- Add exact outputs/gradients or reference results, including isolated node, empty-edge, directed graph and multiple representation cases when relevant. +- Benchmark only if performance is the stated goal; measure warm and steady-state behavior, allocations and device synchronization. + +**Deliverable:** focused PR with reproduction, tests, review notes and accurate limitations. + +### Weeks 11–12 — GSoC exploration + +- Identify one problem theme and ask a maintainer whether it fits the project's likely priorities; do not assume a 2027 slot exists. +- Write a short design note: user impact, package ownership, API surface, baseline, test plan, milestones, risks, fallback and mentor touchpoints. +- Read current GSoC/Julia organization dates and application guidance once published. +- Contribute a second small PR or review an existing PR to demonstrate collaboration and follow-through. + +**Deliverable:** proposal draft grounded in a merged/under-review contribution and current maintainer feedback. + +## 90-day success criteria + +- Can trace a graph operation from public API through the owning package and tests. +- Can explain the graph source/target orientation and feature axis convention. +- Has run a package CPU suite and at least one focused test item, recording any environment failures honestly. +- Has one or more small PRs or a clear maintainer-approved implementation plan. +- Can distinguish a confirmed bug from a report, a TODO, a backend gap, and a performance hypothesis. +- Has a GSoC project outline with scoped milestones, but no assumption of acceptance or project availability. + +## Weekly routine + +1. Sync your fork with upstream `master` and create a focused branch. +2. Read the owning package's source, matching test, docs and recent changelog/history. +3. Reproduce before changing code. +4. Make one minimal change and run the smallest relevant test first. +5. Run the full package CPU suite and any available optional backend suite. +6. Review the diff for accidental generated files, summarize evidence, then ask maintainers for review. + diff --git a/docs/CONTRIBUTOR_RESEARCH_REPORT.md b/docs/CONTRIBUTOR_RESEARCH_REPORT.md new file mode 100644 index 000000000..b36b0e208 --- /dev/null +++ b/docs/CONTRIBUTOR_RESEARCH_REPORT.md @@ -0,0 +1,482 @@ +# GraphNeuralNetworks.jl Contributor Research Report + +**Audit date:** 2026-11-03 (Asia/Calcutta) +**Checkout audited:** `master`, `aa2926c` (`Use the registered LuxTestUtils in the GNNLux tests`), tracking `origin/master` at audit start. +**Scope:** source, package projects, tests, documentation, CI, changelog, and a read-only review of publicly visible GitHub pages. Source files were not edited as part of this audit. +**Environment:** Windows x64, repo-local Julia 1.13.3. This checkout is a development monorepo: package projects are `-DEV` and the local workspace resolves sibling packages by path. + +## 1. Executive summary + +This is a four-package Julia monorepo. `GNNGraphs` owns graph structures and data operations; `GNNlib` owns message passing and functional layer math; `GraphNeuralNetworks` provides stateful Flux layers; and `GNNLux` provides stateless Lux layers. The frontends re-export the graph and message-passing APIs so most users need only select Flux or Lux. + +The best first contribution is a small test or documentation change in one subsystem, then an edge-case fix with a regression test. The repository has a meaningful test suite based on `TestItemRunner`, separate CPU and accelerator filters, and an established three-layer implementation pattern for new convolutions. Do not begin by editing all four layers at once: first reproduce the behavior in a test, find which package owns it, then follow the repository's layer workflow in `GraphNeuralNetworks/docs/src/dev.md` and `AGENTS.md`. + +One current, concrete investigation is [issue #710](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710): a reported Reactant compilation error for Lux `GCNConv`, traced in the issue to degree/scatter handling. The report's stack and the local source path agree, but this audit did **not** independently reproduce it: Reactant is not in the repository's test environments and the issue was reported on Apple Silicon while this machine is Windows x64. Treat the reported trace as a high-quality reproduction from the reporter, not as a local run. + +No other new correctness bug was established by static inspection. Several explicit limitations and test gaps are recorded below as opportunities, not defects. All four package CPU suites passed; the initial GNNGraphs run hit a sandbox-blocked Cora data download, then passed on retry with network access. See §12 for exact counts and broken-item notes. + +## 2. Repository overview + +The four package versions in the checkout are `GNNGraphs 1.6.0-DEV`, `GNNlib 1.4.1-DEV`, `GNNLux 0.3.0-DEV`, and `GraphNeuralNetworks 1.1.1-DEV`, taken from their package `Project.toml` files. The root `Project.toml` is a Julia workspace manifest, not a fifth registered package. Each package has its own dependency and compatibility declarations. `Manifest.toml` is ignored by Git; the local setup created an ignored manifest and package cache, neither of which is part of the source audit. + +The isolated test environment used for the audit resolved Julia 1.13.3, Graphs 1.15.0, Flux 0.16.15, Lux 1.31.5, NNlib 0.9.57, MLUtils 0.4.15, Zygote 0.7.15, Enzyme 0.13.216, Mooncake 0.5.67, and LuxTestUtils 2.3.2. These are this machine's resolved test versions, not package compat minima. Searching the checked-in source/projects finds no Reactant dependency or extension, which is why #710 was not directly executable in the repository test environment. + +Top-level source/configuration areas are `.buildkite`, `.github`, `docs`, `GNNGraphs`, `GNNlib`, `GNNLux`, and `GraphNeuralNetworks`. Repository guidance is in `AGENTS.md`, `CLAUDE.md`, `README.md`, `CHANGELOG.md`, and `.JuliaFormatter.toml`. The checkout also has local setup additions from the preceding development-tools task (`DEVELOPMENT.md`, `scripts/`, `.tools/`, `.julia-depot/`); the latter two are ignored, generated local dependencies and are excluded from the codebase tree below. + +## 3. Complete source tree (generated files and binaries omitted) + +```text +. +├── .buildkite/pipeline.yml +├── .github +│ ├── dependabot.yml +│ └── workflows +│ ├── multidocs.yml +│ ├── TagBot.yml +│ ├── test_GNNGraphs.yml +│ ├── test_GNNlib.yml +│ ├── test_GNNLux.yml +│ └── test_GraphNeuralNetworks.yml +├── docs +│ ├── Project.toml +│ ├── logo.svg +│ └── make-multi.jl +├── GNNGraphs +│ ├── Project.toml, README.md, LICENSE +│ ├── docs/{Project.toml,make.jl,src/{index.md,api/{datasets,gnngraph,heterograph,temporalgraph}.md,guides/{datasets,gnngraph,heterograph,temporalgraph}.md}} +│ ├── ext/{GNNGraphsCUDAExt,GNNGraphsEnzymeCoreExt,GNNGraphsMooncakeExt,GNNGraphsSimpleWeightedGraphsExt}.jl +│ ├── src/{GNNGraphs.jl,abstracttypes.jl,chainrules.jl,convert.jl,datastore.jl,deprecations.jl,gatherscatter.jl,generate.jl,gnngraph.jl,mldatasets.jl,operators.jl,query.jl,sampling.jl,temporalsnapshotsgnngraph.jl,transform.jl,utils.jl} +│ │ └── gnnheterograph/{generate,gnnheterograph,query,transform,utils}.jl +│ └── test/{Project.toml,runtests.jl,test_module.jl,chainrules.jl,convert.jl,datastore.jl,enzyme.jl,generate.jl,gnngraph.jl,gnnheterograph.jl,mldatasets.jl,operators.jl,query.jl,sampling.jl,temporalsnapshotsgnngraph.jl,transform.jl,utils.jl,ext/{Mooncake,SimpleWeightedGraphs}.jl} +├── GNNlib +│ ├── Project.toml, README.md, LICENSE +│ ├── docs/{Project.toml,make.jl,src/{index.md,api/{messagepassing,utils}.md,guides/messagepassing.md}} +│ ├── ext/{GNNlibAMDGPUExt,GNNlibCUDAExt,GNNlibMetalExt,GNNlibMooncakeExt}.jl +│ ├── src/{GNNlib.jl,msgpass.jl,utils.jl,layers/{basic,conv,pool,temporalconv}.jl} +│ └── test/{Project.toml,runtests.jl,test_module.jl,msgpass.jl,utils.jl} +├── GNNLux +│ ├── Project.toml, README.md, LICENSE +│ ├── docs/{Project.toml,make.jl,make_tutorials.jl,src/{index.md,api/{basic,conv,pool,temporalconv}.md,guides/models.md,tutorials/{beginner_tutorials/{gnn_intro,graph_classification,node_classification}.md,intermediate_tutorials/traffic_prediction.md},assets/{logo.svg,schema.png}},src_tutorials/{gnn_intro,graph_classification,node_classification,traffic_prediction}.jl} +│ ├── src/{GNNLux.jl,layers/{basic,conv,pool,temporalconv}.jl} +│ └── test/{Project.toml,runtests.jl,test_module.jl,layers/{basic,conv,pool,temporalconv}.jl} +├── GraphNeuralNetworks +│ ├── Project.toml, README.md, LICENSE +│ ├── docs/{Project.toml,make.jl,synthetic_datasets.jl,old_tutorials/{make_tutorials_literate,make_tutorials_pluto,temporal_graph_classification}.jl,src/{index.md,dev.md,api/{basic,conv,heteroconv,pool,temporalconv}.md,guides/models.md,tutorials/{beginner_tutorials/{gnn_intro,graph_classification,node_classification}.{md},intermediate_tutorials/traffic_prediction.md},assets/{logo.svg,logo.png,schema.png,logo_alternatives/V1.svg…V10.svg}}} +│ ├── examples/{Project.toml,graph_classification_temporalbrains.jl,graph_classification_tudataset.jl,link_prediction_pubmed.jl,neural_ode_cora.jl,node_classification_cora.jl,traffic_prediction.jl} +│ ├── notebooks/{gnn_intro,graph_classification,graph_classification_solved}.ipynb +│ ├── perf/{Project.toml,bench_gnn.jl,neural_ode_mnist.jl,node_classification_cora_geometricflux.jl,perf.jl,sparse_propagate_cuda.jl,perf_master_20210830_carlodsba.jld2} +│ ├── src/{GraphNeuralNetworks.jl,deprecations.jl,layers/{basic,conv,heteroconv,pool,temporalconv}.jl} +│ └── test/{Project.toml,runtests.jl,test_module.jl,examples/node_classification_cora.jl,layers/{basic,conv,heteroconv,pool,temporalconv}.jl} +├── .JuliaFormatter.toml, .git-blame-ignore-revs, .gitignore, AGENTS.md, CHANGELOG.md, CLAUDE.md, codecov.yml, LICENSE, Project.toml, README.md +└── Local development additions: DEVELOPMENT.md, scripts/{julia,setup-dev}.ps1 +``` + +The tree groups files with repeated naming into braces for readability; each listed group corresponds to the files actually present at the audited checkout. Documentation images, notebooks, and the `.jld2` benchmark snapshot are assets/data, not Julia source. `docs/build`, local manifests, binary downloads, and Julia caches are generated/ignored content and deliberately omitted. + +## 4. Folder-by-folder explanation + +| Folder | Responsibility and dependencies | Main entry points | +|---|---|---| +| `.github/workflows` | CPU package test workflows, docs builds, release tagging. These define CI behavior, not package runtime. | `test_*.yml`, `multidocs.yml`, `TagBot.yml` | +| `.buildkite` | Accelerator jobs executed on CUDA/ROCm hardware queues. Depends on Buildkite agents and JuliaCI plugins. | `pipeline.yml` | +| `docs` | Builds the combined documentation landing site after each package docs site. | `make-multi.jl`, `Project.toml` | +| `GNNGraphs/src` | Graph types, feature stores, graph queries, transformations, generation, sampling, conversion. Used by all other packages. | `GNNGraph`, `GNNHeteroGraph`, `degree`, `edge_index`, `batch`, `propagate` helpers | +| `GNNGraphs/ext` | Optional CUDA, Enzyme, Mooncake, and SimpleWeightedGraphs integrations loaded through Julia package extensions. Depends on weak dependencies rather than making them mandatory. | extension modules in `Project.toml` | +| `GNNGraphs/test` | Graph representation, algorithms, transforms, AD, optional backend tests. Provides shared fixtures and tagged test items. | `runtests.jl`, `test_module.jl` | +| `GNNGraphs/docs` | Public API and guides for homogeneous, heterogeneous, and temporal graph features. | `make.jl`, `src/api`, `src/guides` | +| `GNNlib/src` | Framework-neutral message passing, functional convolution/pooling/recurrent math, graph and tensor utilities. Depends on GNNGraphs, NNlib, MLUtils, and numeric utility packages. | `propagate`, `apply_edges`, `aggregate_neighbors`, `layers/*.jl` | +| `GNNlib/ext` | Optional backend-specific message passing and Mooncake rules. | `GNNlibCUDAExt`, `GNNlibAMDGPUExt`, `GNNlibMetalExt`, `GNNlibMooncakeExt` | +| `GNNlib/test` | Tests functional propagation, utilities, graph types and AD paths. | `msgpass.jl`, `utils.jl`, `test_module.jl` | +| `GNNLux/src` | Lux's stateless layer structs, parameter/state initialization, calls into GNNlib. Depends on both lower layers and Lux APIs. | `GNNLux.jl`, `layers/*.jl` | +| `GNNLux/test` | Layer output, shape, and AD tests using LuxTestUtils; current `runtests.jl` explicitly says GPU test coverage is not yet added. | `layers/*.jl`, `test_module.jl` | +| `GNNLux/docs` | Lux API pages and authored beginner/intermediate tutorials; `src_tutorials` are source examples used to generate tutorial pages. | `make_tutorials.jl`, `make.jl` | +| `GraphNeuralNetworks/src` | Flux frontend. Layer structs, trainable parameter definitions, recurrence/container behavior, hetero layer support. | `GraphNeuralNetworks.jl`, `layers/*.jl` | +| `GraphNeuralNetworks/test` | Flux layer, graph-to-layer integration, CPU/GPU and AD tests, one example smoke/training item. | `layers/*.jl`, `test_module.jl` | +| `GraphNeuralNetworks/examples`, `notebooks` | Runnable user examples and interactive workflows. Example environments have their own projects. | node/graph/link classification, temporal traffic, neural ODE | +| `GraphNeuralNetworks/perf` | Benchmarks and historical comparison artifacts. Not part of the package's runtime path. | `perf.jl`, `bench_gnn.jl`, `sparse_propagate_cuda.jl` | +| `GraphNeuralNetworks/docs` | Flux API, developer guide, tutorials, datasets used for documentation, historical tutorial-generation scripts. | `make.jl`, `src/dev.md` | +| `scripts` | Local Windows PowerShell bootstrap and Julia launcher added during the prior setup task. Not part of upstream package runtime. | `setup-dev.ps1`, `julia.ps1` | + +## 5. Important file-by-file explanation + +### Repository-level files + +| File | Role | +|---|---| +| `Project.toml` | Root Julia workspace; lists child projects. It has no package UUID or root library module. | +| `AGENTS.md` | Repository architecture, supported Julia version, test commands, test item conventions, GPU backend settings, layer development steps, changelog/version rules. Read before coding. | +| `CLAUDE.md` | Agent-specific contributor notes. | +| `README.md` | User-facing overview, package selection, install examples, citation. | +| `CHANGELOG.md` | Single chronological release history for four independently versioned packages; useful for checking already-completed bug fixes. | +| `.JuliaFormatter.toml` | Selects SciML formatter style. | +| `.gitignore` | Excludes local manifests, build outputs and local development environments; also excludes `.tools/` and `.julia-depot/` added during setup. | +| `codecov.yml` | Coverage reporting configuration. | +| `.git-blame-ignore-revs` | Lists formatting-only revisions to omit during blame. | +| `DEVELOPMENT.md`, `scripts/*.ps1` | Repo-local Windows Julia setup; they do not affect package source. | + +### Package files + +| Path(s) | Key responsibilities | +|---|---| +| `GNNGraphs/src/GNNGraphs.jl` | Module imports/includes and public exports. Good starting point for the graph package API. | +| `GNNGraphs/src/abstracttypes.jl` | `AbstractGNNGraph{T}` subtype of Graphs.jl's `AbstractGraph{Int}` and representation type aliases. | +| `GNNGraphs/src/gnngraph.jl` | `GNNGraph` constructor, metadata, graph storage, `ndata`/`edata`/`gdata`, `num_nodes`, `num_edges`, `num_graphs`. | +| `GNNGraphs/src/datastore.jl` | `DataStore` dictionary-like feature storage and property/indexing interface. It stores arbitrary feature arrays in a `Dict{Symbol,Any}`, so callers must respect observation count conventions. | +| `GNNGraphs/src/gnnheterograph/gnnheterograph.jl` | Heterogeneous relation storage keyed by `(source type, relation, destination type)` and per-node-type features. | +| `GNNGraphs/src/temporalsnapshotsgnngraph.jl` | `TemporalSnapshotsGNNGraph`, sequence of homogeneous graph snapshots plus temporal graph data. | +| `GNNGraphs/src/query.jl`, `gnnheterograph/query.jl` | Edge access, degree, adjacency/Laplacian, type, batch indicator, directedness and heterograph queries. | +| `GNNGraphs/src/transform.jl`, `gnnheterograph/transform.jl` | Self-loop and edge transforms, coalescing, batching/unbatching, node removal, graph extraction, diffusion and edge splitting. | +| `GNNGraphs/src/generate.jl`, `gnnheterograph/generate.jl` | Random, bipartite, k-nearest-neighbor, radius, and temporal graph generation. | +| `GNNGraphs/src/convert.jl` | Conversion among COO, adjacency matrix, graph library and sparse forms. | +| `GNNGraphs/src/gatherscatter.jl` | Internal recursive gather/scatter adapters that map over tuple/named-tuple/dict feature payloads and delegate arrays to NNlib. | +| `GNNGraphs/src/sampling.jl` | Neighbor sampling and `NeighborLoader` integration. | +| `GNNGraphs/src/mldatasets.jl` | Conversion helpers from MLDatasets graph data. | +| `GNNGraphs/src/chainrules.jl` | ChainRules custom derivatives and non-differentiable declarations for graph operations. | +| `GNNGraphs/src/operators.jl`, `utils.jl`, `deprecations.jl` | Graph operators, feature normalization/checking, helper algorithms, and compatibility aliases. | +| `GNNlib/src/GNNlib.jl` | Module exports and include order for utilities, message passing, layers and pooling. | +| `GNNlib/src/msgpass.jl` | `propagate` pipeline plus message helpers and graph-specialized fast paths. | +| `GNNlib/src/utils.jl` | Node/edge reduction, graph-wise and neighbor-wise softmax, graph/edge broadcasting. | +| `GNNlib/src/layers/conv.jl` | Functional math for spatial convolutions; this is where shared forward-pass correctness should be fixed. | +| `GNNlib/src/layers/basic.jl` | Basic decoder math and layer primitives. | +| `GNNlib/src/layers/pool.jl` | Graph-level pooling functions and Set2Set/TopK helpers. | +| `GNNlib/src/layers/temporalconv.jl` | Shared cell math for temporal graph convolution/recurrent models. | +| `GraphNeuralNetworks/src/GraphNeuralNetworks.jl` | Flux imports, re-exports GNNGraphs/GNNlib, includes and exports Flux frontend layers. | +| `GraphNeuralNetworks/src/layers/basic.jl` | `GNNLayer`, `GNNChain`, `WithGraph`, decoder, Flux chain integration. | +| `GraphNeuralNetworks/src/layers/conv.jl` | Mutable/trainable Flux layer structs, constructors, display, calls into `GNNlib.*_conv`. | +| `GraphNeuralNetworks/src/layers/heteroconv.jl` | Relation-specific modules and aggregation into heterogeneous destination node types. | +| `GraphNeuralNetworks/src/layers/pool.jl` | Flux graph pooling layer wrappers. | +| `GraphNeuralNetworks/src/layers/temporalconv.jl` | Flux temporal sequence/recurrent wrappers calling shared GNNlib math. | +| `GNNLux/src/GNNLux.jl` | Lux imports, re-export of GNNGraphs, include/export list. | +| `GNNLux/src/layers/basic.jl` | Lux container and chain abstractions. | +| `GNNLux/src/layers/conv.jl` | `@concrete` layer definitions, `initialparameters`, parameter length, Lux `(y, st)` calls into GNNlib. | +| `GNNLux/src/layers/pool.jl`, `temporalconv.jl` | Lux pooling and sequence/cell layers. | +| `*/ext/*.jl` | Optional package extensions activated by weak dependencies; backend code belongs here where it is backend-specific. | +| `*/test/runtests.jl` | `TestItemRunner` runner; untagged items are CPU, `:gpu` items are selected by environment variables. | +| `*/test/test_module.jl` | Shared fixtures and backend/device/gradient test helpers. Review these before writing a new test. | +| `*/docs/make.jl`, `*/docs/Project.toml` | Package documentation dependency environment and Documenter build entrypoint. | +| `GraphNeuralNetworks/examples/Project.toml`, `perf/Project.toml` | Isolated dependency environments for examples and benchmark scripts. | + +### Configuration and dependency files + +There are 16 checked-in `Project.toml` files: root workspace; four package projects; four package test environments; four package docs environments; the combined docs project; and the examples and performance environments. Each package project declares `[deps]`, `[weakdeps]`, `[extensions]`, and `[compat]`; test-only packages such as Enzyme, Mooncake, TestItemRunner and LuxTestUtils belong in test environments, not in core runtime dependencies. `Manifest.toml` is not a tracked lockfile here. + +`.github/dependabot.yml` updates GitHub Actions and Julia dependencies weekly. The four `test_*.yml` workflows test each package on Ubuntu with Julia 1.10 and latest stable Julia. `.buildkite/pipeline.yml` runs CUDA and AMDGPU jobs on hosted hardware queues. `.github/workflows/multidocs.yml` builds each package's docs plus the combined site; `.github/workflows/TagBot.yml` tags each registered package separately, required because the root is a workspace without a package UUID. + +## 6. Architecture and package responsibilities + +```mermaid +graph TD + GraphData[GNNGraphs: graph storage, queries, transforms] --> MP[GNNlib: message passing and shared math] + GraphData --> Flux[GraphNeuralNetworks: Flux layers] + MP --> Flux + GraphData --> Lux[GNNLux: Lux layers] + MP --> Lux + Flux --> UserFlux[Flux model / training] + Lux --> UserLux[Lux model / training] +``` + +| Package | Responsibility and public surface | Why separate it / where to change | +|---|---|---| +| **GNNGraphs** | `GNNGraph`, `GNNHeteroGraph`, `TemporalSnapshotsGNNGraph`, `DataStore`; graph query, convert, batch, transform, sampling, generation, feature gather/scatter. | Keeps graph structures usable without choosing Flux or Lux. Put graph representation, degree, graph mutation or batching changes here. Public exports are in `GNNGraphs/src/GNNGraphs.jl:21-125`. | +| **GNNlib** | `propagate`, `apply_edges`, `aggregate_neighbors`, built-in message functions; functional convolution, temporal and pool functions; reductions/softmax. | Shared math avoids duplicate forward formulas across frontends. Put backend-agnostic layer math and message-passing optimization here. Public exports are in `GNNlib/src/GNNlib.jl:17-74`. | +| **GraphNeuralNetworks** | Flux trainable layers and containers; re-exports GNNGraphs/GNNlib. | Flux stores trainable arrays in layer structs. Update its wrapper/constructor, trainability and tests when exposing a shared functional layer to Flux. | +| **GNNLux** | Lux layers implementing parameter/state initialization and Lux call convention; re-exports GNNGraphs. | Lux passes explicit `ps`/`st`, unlike Flux's stateful layer object. Update this wrapper plus tests/docs when a layer should be available to Lux. | + +The function crossing the package boundary is typically `GNNlib.gcn_conv(layer, graph, x, ...)`: the frontend owns the layer shape, trainable parameter representation, activation and state convention; GNNlib owns feature propagation and output math; GNNGraphs owns topology and degree/edge queries; NNlib supplies dense/scatter/sparse tensor primitives; extensions specialize implementation for optional accelerators/AD systems. + +## 7. Important functions, types and algorithms + +### Graph representations and dimensions + +`GNNGraph` (see `GNNGraphs/src/gnngraph.jl:107+`) represents edges as source and target node indices `(s,t)` in COO, or a dense/sparse adjacency matrix. Node features have nodes in the **last** axis, usually `features × nodes`; edge features use `features × edges`. Edge index values are one-based Julia indices. Adjacency matrices in this repository follow `A[source,target]` for directed edges, so distinguish source and target orientation when changing a reduction. An optional third COO tuple entry stores edge weights. The `is_coalesced` bit records canonical target-then-source ordering and no duplicate edges; optimized paths may depend on it. + +`GNNHeteroGraph` stores relation-specific edge-index/weight data and per-type feature stores, keyed with symbols. Its `num_nodes`/`num_edges` values are dictionaries, not scalar counts. `TemporalSnapshotsGNNGraph` is a vector of `GNNGraph` snapshots, not a single graph with a time axis; the model decides how recurrent state flows over snapshots. + +`DataStore` provides `g.ndata.x`, `g.edata.e`, and `g.gdata.u` access. The store uses `Dict{Symbol,Any}` because a feature store may contain unrelated array shapes and names. Construction/set operations check observation count when a count is known; tests are in `GNNGraphs/test/datastore.jl`. + +### Message passing + +`apply_edges` (`GNNlib/src/msgpass.jl:117-128`) checks feature dimensions, gets `(s,t)`, gathers target/root features `xi` with `t` and source/neighbour features `xj` with `s`, then calls a user message function `f(xi,xj,e)`. `aggregate_neighbors` (`:145-156`) scatters edge messages back to destination nodes (`t`). `propagate` (`:71-79`) composes these operations. Named tuples/tuples/dicts of arrays are recursively mapped by GNNGraphs' `_gather` and `_scatter` adapters. + +For `E` edges and `F` feature values per edge, generic gather/message/scatter work is approximately `O(EF)` and materializes edge-sized gathered arrays (space `O(EF)`, before the output). GNNlib specializes common message patterns such as `copy_xj`, `e_mul_xj`, and `w_mul_xj` to matrix multiplication/sparse propagation where supported, avoiding some per-edge intermediate storage. For performance changes, benchmark both coalesced and uncoalesced graphs and account for backend-specific sparse support. + +### Degree and scatter + +`Graphs.degree(::GNNGraph{<:COO_T}, ...)` in `GNNGraphs/src/query.jl:337-353` resolves weights, determines a result element type, and delegates to `_degree`. COO `_degree` at `:378-392` builds unweighted ones if needed, zeros of length `num_nodes`, then calls `NNlib.scatter(+, edge_weight, s/t)` for out/in directions. Work is linear in edges plus nodes; memory is `O(V)` output plus backend scatter temporaries. Matrix `_degree` at `:394-402` sums adjacency rows/columns. The `:both` path sums both directions and therefore counts a self-loop in each direction; this is the documented degree convention in tests. + +The `degree` tests in `GNNGraphs/test/query.jl:66-207` cover unweighted/weighted directed graphs, graph representations, gradients, and GPU tagged paths. Existing `broken = ...` annotations document known representation-specific expectations. Metal explicitly skips gather/scatter degree GPU tests at `:178` pending support. Do not infer a defect from those documented skips. + +### Convolution layers + +| Layer | Math/shape behavior and code location | Test location | +|---|---|---| +| `GCNConv` | Shared function `GNNlib/src/layers/conv.jl:14-79`; frontend structs/calls `GraphNeuralNetworks/src/layers/conv.jl:77-105`, `GNNLux/src/layers/conv.jl:84-141`. Adds optional self-loops, degree-normalizes source and destination features, sums transformed neighbour messages, applies weight/bias/activation. It may multiply by `W` before propagation when `out < in` to reduce work, otherwise after. External edge weights require COO; adjacency external weights explicitly throw. Default normalization is inverse square-root degree. | `GraphNeuralNetworks/test/layers/conv.jl:7-75`; `GNNLux/test/layers/conv.jl:10-24`; degree tests above. | +| `GATConv` | `GNNlib/src/layers/conv.jl:112-167`; Flux struct starts `GraphNeuralNetworks/src/layers/conv.jl:309`; Lux starts `GNNLux/src/layers/conv.jl:860`. Projects node/optional edge features per head, computes LeakyReLU attention scores, neighbor softmax, attention-weighted aggregation, then head concatenation/averaging and activation. | Flux tests `:154-192`; Lux `GNNLux/test/layers/conv.jl:59-70`. | +| `GATv2Conv` | GNNlib `:171-218`; parameterized attention order makes attention dynamic with features. Frontends begin Flux `:413`, Lux `:1000`. | Flux `:194-232`; Lux `:71-81`. | +| `NNConv` | GNNlib `:258-278`; edge network emits a per-edge matrix, reshaped to `(out,in,edges)`, applied by `NNlib.batched_mul`, then aggregated. Cost includes the per-edge generated matrices: roughly `O(E·in·out)` storage/work plus the edge MLP. | Flux `:301-325`; Lux `:112-127`. | +| `MEGNetConv` | GNNlib `:362+`; edge MLP updates using `(xi,xj,e)`, edge messages aggregate to nodes, node MLP updates `(x, aggregated edge features)`. Returns updated node and edge features in the Flux interface. | Flux `:427-458`; Lux `:99-110`. | +| `GraphConv`, `GINConv`, `SAGEConv`, `EdgeConv`, `CGConv`, `GMMConv`, `EGNNConv`, `ResGatedGraphConv`, `AGNNConv`, `ChebConv`, `DConv`, `SGConv`, `TAGConv`, `TransformerConv`, `GatedGraphConv` | Functional math is grouped by layer heading in GNNlib `src/layers/conv.jl`; wrapper definitions are similarly headed in the two frontend `conv.jl` files. Read the docstring/formula and the matching `@testitem` before changing a layer. | Flux `GraphNeuralNetworks/test/layers/conv.jl`; Lux `GNNLux/test/layers/conv.jl`. | + +Complexity depends on graph format and backend. Generic message passing scales with edges; dense adjacency multiplication may scale with `V²`; sparse matrix multiplication scales with nonzeros plus feature width. Do not claim an optimization without benchmarking representative graph sizes and `:coo`, `:dense`, `:sparse` where supported. + +### Pooling, batch, temporal and heterogeneous functions + +- `global_pool` and `global_attention_pool` aggregate node features using `graph_indicator`; graph-wise output has one column per graph. `topk_index`/`topk_pool` select nodes by learned scores. `set2set_pool` uses a recurrent query/readout. Functional implementations are in `GNNlib/src/layers/pool.jl`; wrappers/tests are in each frontend `layers/pool.jl` and `test/layers/pool.jl`. +- `MLUtils.batch` combines graph index sets with node-index offsets and a graph-indicator vector; `unbatch` reconstructs per-graph pieces. See `GNNGraphs/src/transform.jl:675-799` and test roundtrips in `GNNGraphs/test/transform.jl:23-130`. Zero-edge cases matter; a historical bug was fixed in PR #652 and is called out in the release notes. +- `HeteroGraphConv` applies typed relation submodules and reduces their outputs by destination node type (`GraphNeuralNetworks/src/layers/heteroconv.jl:42-90`). Relation and node type dictionary keys must be preserved through batching and transfer. +- Temporal layers split a cell (one time step) from a recurrence (sequence), so test both cell state and full sequence. Flux recurrent layers are in `GraphNeuralNetworks/src/layers/temporalconv.jl`; shared formula functions are in GNNlib temporalconv. Lux's temporal wrappers live in `GNNLux/src/layers/temporalconv.jl`. `TGCN` activation support was already added (CHANGELOG entry for PR #596), so it is not a new contribution target. + +### Multiple dispatch, extensions and autodiff + +Julia chooses a method from argument types, not just a method name. This repo relies on that for graph representation dispatch (`GNNGraph{<:COO_T}` vs adjacency matrices), scalar/vector edge weights, feature payload containers, Flux/Lux layer calls, and backend arrays. Search for all methods of a shared function before editing it. Package extensions are declared in `[weakdeps]` and `[extensions]`: they let the package specialize CUDA/AMDGPU/Metal or AD behavior only when that package is loaded. + +AD support is part of the API surface: ChainRules rules live in GNNGraphs, Mooncake specializations in `GNNGraphs/ext` and `GNNlib/ext`, and EnzymeCore extension rules in GNNGraphs. Flux tests compare AD gradients to finite differences/Zygote/Mooncake in helper code; Lux tests use LuxTestUtils. Shape tests plus gradient consistency do not prove a forward formula is mathematically correct—there are explicit numeric/reference tests for selected formulas, such as DConv in `GraphNeuralNetworks/test/layers/conv.jl:622-661`. + +## 8. End-to-end GNN execution flow + +```mermaid +sequenceDiagram + participant User + participant G as GNNGraphs + participant F as Flux or Lux layer + participant L as GNNlib + participant N as NNlib / backend extension + User->>G: construct topology, node/edge features + User->>F: model(g, x, parameters/state) + F->>L: shared functional conv (e.g. gcn_conv) + L->>G: edge_index, degree, graph metadata + L->>N: gather or SpMM, message function, scatter/aggregation + N-->>L: destination-node messages / normalized features + L-->>F: output features + F-->>User: output (and Lux state where applicable) + User->>F: loss + AD backend + F->>L: trace/reverse through shared math and extensions +``` + +For a simple sum-aggregation layer, with source `j` sending to target `i`, the conceptual operations are `m[j→i] = f(x_i,x_j,e_{ji})` followed by `x'_i = φ(□_{j∈N(i)} m[j→i])`. In GCN, each message is degree-normalized and linear feature transform, then summed. Graph construction fixes edge direction and counts; GNNlib relies on GNNGraphs' `edge_index` and `degree`; frontend code supplies parameters and activation; AD differentiates the exact path chosen by NNlib/backend methods. + +## 9. Confirmed bugs + +### C-710 — Reactant `GCNConv` compilation fails in reported environment + +- **Severity:** Medium (backend-specific failure; normal CPU and Zygote path reportedly work). +- **Status:** Confirmed as a current public issue with a minimal reporter-provided reproduction; **not reproduced on this audit machine**. +- **Evidence:** [Issue #710](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710) remains open in the page opened on 2026-11-03, is labeled `gnnlib`/`lux`, reports GNNLux 0.2.0, GNNGraphs 1.5.1, Reactant 0.2.285, Julia 1.12.6 on Apple Silicon CPU, and shows `Ops.scatter` receiving `Vector{Vector{Reactant.TracedRNumber{Float32}}}` where its nearest methods expect vectors of `TracedRArray`. The page says no linked branch/PR. +- **Local path:** `GNNLux/src/layers/conv.jl:128-140` delegates to `GNNlib.gcn_conv`; `GNNlib/src/layers/conv.jl:42-57` asks GNNGraphs for degree; `GNNGraphs/src/query.jl:337-353` dispatches COO degree; `:378-390` creates unweighted edge values and uses `NNlib.scatter` for incoming/outgoing degrees; `GNNGraphs/src/gatherscatter.jl:12-18` is the separate graph message scatter adapter. The failing operation named in the issue is the degree scatter (`NNlib.scatter` / Reactant `Ops.scatter`), not the neighbor-message scatter in GNNlib's message-passing stage. +- **Why package ownership matters:** graph degree and construction of degree inputs are GNNGraphs responsibilities; shared convolution calls are GNNlib; Lux wrapper only enters the shared path. A generic, array-type-preserving graph fix belongs first in GNNGraphs if confirmed; a Reactant-only dispatch/rule could belong in a separate extension if core arrays already preserve types correctly. Do not patch the Lux layer just to hide a lower-level traced scalar representation. +- **Why the vector element type appears:** the report states the runtime has materialized edge weights/updates as ordinary nested vectors of traced scalar values, while the scatter method accepts array-valued traced updates. The local `_degree` source routes edge values through `ones_like(s,T)` and NNlib scatter. Static inspection establishes this path but does not establish exactly which `ones_like`/Reactant lowering step creates the nested vectors; determine that with a debugger or minimal instrumented Reactant reproducer before proposing a code change. +- **Edge weights:** COO stores optional weights as the third component of `g.graph`; `get_edge_weight(g)` returns it (`GNNGraphs/src/query.jl:16`). For default unweighted GCN, `use_edge_weight=false` asks degree for unweighted counts; edge weights can also be supplied or stored in the graph. The issue's stack indicates a traced scalar vector even in its no-external-edge-weight example. +- **Reproduction/expectation:** use the issue's code verbatim in the same package versions/device. Expected: `@compile model(g,x_d,ps_d,st_d)` returns a compiled function and executes; reported actual: `MethodError` in scatter. Local attempt was not run because Reactant is absent from all repo test projects and platform differs. +- **Regression tests:** graph-level `degree` on Reactant arrays for unweighted COO and weighted COO; test `dir=:in`, `:out`, and `:both`; test isolated node and empty edge set if supported; then Lux `GCNConv` compilation with default normalization and self-loops. Add separate tests for graph-stored and explicit edge weights. Keep CPU result and Zygote gradient behavior covered. Any Reactant test should be an explicit optional/extension CI job, not a hard dependency of default CPU test suites. +- **Possible solutions/tradeoffs:** (1) make core degree's identity updates/zeros stay array-shaped under traced arrays (best if the minimal reproducer shows `ones_like`/`zeros_like` is the source; benefits any backend, but must preserve CPU/GPU AD); (2) add a narrow optional Reactant extension method or adaptation for scatter (isolates dependency, but adds extension maintenance and version-compat burden); (3) alter the convolution to avoid the degree scatter (may duplicate graph logic and not solve `degree` generally). The first two require a minimal proof and maintainer agreement; no speculative fix was applied. + +## 10. Potential bugs and hypotheses + +No independent static-review finding met the evidence bar for a separate bug. The issue page #710 is the only bug report in this audit with a specific trace and local source path. + +These are **investigation leads**, not verified failures: + +1. Check Reactant traced arrays through all graph constructors and `GNNGraphs.degree` overloads, not only GCN; issue #710 stack establishes the GCN case only. +2. Check zero-edge and empty-relation behavior for heterograph `GATConv` gradients, corresponding to public issue #637 (its current state must be checked directly before work; search results can be stale). +3. Check AD through `GNNGraphs.add_edges` after issue #671 reports a Zygote segfault; do not infer it is still open/failing merely from an indexed issue list. +4. Check SGConv repeated-training runtime against a benchmark and memory profile before calling it a leak; issue #603 is a report, not proof of current failure. + +For each lead, use a minimal test, reproduce on a clean environment, inspect matching tests/history/PRs and then record actual/expected behavior. Do not classify an issue label or TODO as an independently confirmed bug. + +## 11. Performance, compatibility and quality observations + +| Category | Verified observation | Interpretation | +|---|---|---| +| Performance | Generic GNNlib message passing gathers `xi/xj` per edge, computes messages and scatters (`GNNlib/src/msgpass.jl:121-155`). | `O(EF)` edge data is expected for the generic path. Fast paths and graph-format tradeoffs should be measured; this is not itself a defect. | +| Performance | `Graphs.degree(g, i)` currently computes a whole degree vector before indexing and is tagged `# TODO:: Make efficient` (`GNNGraphs/src/query.jl:356-357`). | Concrete local optimization candidate; benchmark repeated/sparse selections and preserve exact semantics. | +| Compatibility | External GCN edge-weight vectors are explicitly rejected for adjacency-matrix graph types (`GNNlib/src/layers/conv.jl:3-4`). | Documented implementation limit; not a bug. A feature request would need define duplicate-edge semantics and conversion cost. | +| Accelerator | Metal degree test skips gather/scatter pending backend support (`GNNGraphs/test/query.jl:174-179`). | Known backend gap; confirm current Metal extension capabilities before claiming unsupported operations beyond this path. | +| Lux coverage | `GNNLux/test/runtests.jl:17` says no GPU tests are added; CPU tests use LuxTestUtils. | Coverage gap/opportunity, not evidence all Lux GPU code fails. | +| AD coverage | Flux helpers compare AD output and finite differences; comments record selective Mooncake skips and upstream issues in temporal tests. | AD compatibility needs be reported per backend/layer/graph representation. A passing test on one backend is not universal proof. | +| Source parity | Flux exports `TAGConv` and `TransformerConv`; current GNNLux export list omits them (and the file comments them out at `GNNLux/src/GNNLux.jl`). | API parity opportunity only. First inspect implementation state and design intent before porting. | +| Type flexibility | `DataStore` stores arbitrary arrays as `Any` values in a symbol dictionary. | Deliberate heterogeneous feature-store design. Profile before proposing a specialized container. | + +Prior fixes in `CHANGELOG.md` are valuable regression context: zero-edge batch unbatch (#652), empty edge-neighbor softmax (#636), graph mutation/self-loop bugs (#659), GPU scalar indexing (#691/#672), GMMConv formula (#645), and DConv formula (#693). Check changelog and blame before reopening similar work. + +## 12. Test and CI analysis + +### How tests are structured + +Tests use `@testitem` and `TestItemRunner`, not only nested `@testset`. `test/runtests.jl` filters out `:gpu` items when `GNN_TEST_CPU` defaults to `true`; GPU variables select those tagged items. Graph fixture setup lives in package `test_module.jl`. In the GNNGraphs/Flux/GNNlib harnesses, CUDA is paired with cuDNN and scalar indexing is disabled; AMDGPU and Metal are loaded on demand. Lux has its own `TestModuleLux` and LuxTestUtils gradient helper. + +The ordinary package commands and per-package coverage are: + +```powershell +.\scripts\julia.ps1 --project=GNNGraphs -e 'using Pkg; Pkg.test("GNNGraphs")' +.\scripts\julia.ps1 --project=GNNlib -e 'using Pkg; Pkg.test("GNNlib")' +.\scripts\julia.ps1 --project=GraphNeuralNetworks -e 'using Pkg; Pkg.test("GraphNeuralNetworks")' +.\scripts\julia.ps1 --project=GNNLux -e 'using Pkg; Pkg.test("GNNLux")' +``` + +CI runs these on Ubuntu with Julia 1.10 and current stable; tests for Flux/GNNlib/GNNGraphs/GNNLux are separate. Buildkite is the GPU path: CUDA and AMDGPU queues for GNNGraphs, GNNlib, and Flux frontend. Metal tests are locally selectable by environment variable but are not in the checked-in Buildkite matrix. The docs workflow builds four package sites and then the combined site. The Julia version in the docs workflow is pinned separately (currently 1.12.2 in this checkout, with an inline note about moving it after a Pkg fix). + +Coverage exists across graph constructors, transforms, queries, message passing, convolution, pooling, temporal layers and AD. Gaps/opportunities: Reactant isn't included in a project/test environment; Lux's GPU suite is not implemented; no CI matrix for Metal; a few generic paths are intentionally skipped/broken by graph type/backend. Those facts should be stated per operation and backend, not summarized as “GPU unsupported.” + +### Audit test run + +**Initial command:** `.\scripts\julia.ps1 --project=GNNGraphs -e 'using Pkg; Pkg.test("GNNGraphs")'` on Julia 1.13.3, repo-local environment. Result: **1,348 passed, 1 error, 8 broken/expected-broken**, total 1,357; runtime 5m28.7s. The sole error was `mldataset2gnngraph` in `GNNGraphs/test/mldatasets.jl`: it could not fetch `https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.x` because the sandbox blocked GitHub. + +**Network-enabled retry:** same command with authorized network access. Result: **1,352 passed, 8 broken/expected-broken**, total 1,360; runtime 4m16s; `Pkg.test` reported the suite passed. The extra four checks came from the formerly blocked dataset item. + +The eight `Broken` outcomes are TestItems' explicitly declared broken cases, not eight newly diagnosed defects. No GPU tests were requested/executed. Tests were run because the audit request explicitly asked for appropriate non-destructive checks. No source edits were made by test execution. + +**GNNlib:** `.\scripts\julia.ps1 --project=GNNlib -e 'using Pkg; Pkg.test("GNNlib")'` on Julia 1.13.3. Result: **86 passed, 0 failures/errors**, runtime 1m29.6s; `Pkg.test` reported success. + +**GraphNeuralNetworks (Flux):** `.\scripts\julia.ps1 --project=GraphNeuralNetworks -e 'using Pkg; Pkg.test("GraphNeuralNetworks")'` on Julia 1.13.3. Result: **928 passed, 0 failures/errors**, runtime 14m48.2s; `Pkg.test` reported success. The full run includes the CPU Cora training example. + +**GNNLux:** `.\scripts\julia.ps1 --project=GNNLux -e 'using Pkg; Pkg.test("GNNLux")'` on Julia 1.13.3. Result: **177 passed, 2 broken/expected-broken**, total 179; `Pkg.test` reported success. LuxTestUtils emitted “No backends to test. Skipping test...” warnings for autodiff checks where no backend is configured; these were warnings, not suite failures. + +The four package CPU suites passed. GPU suites and documentation builds were not run. Tests were run because the audit request explicitly asked for appropriate non-destructive checks. No source edits were made by test execution. + +### Running a single test item + +The repo instructions recommend the Julia VS Code test-item UI. From the repository root, TestItemRunner also supports a name filter without editing a tracked runner: + +```powershell +.\scripts\julia.ps1 --project=GNNGraphs/test -e 'using TestItemRunner; TestItemRunner.run_tests("GNNGraphs/test"; filter = ti -> ti.name == "degree")' +.\scripts\julia.ps1 --project=GraphNeuralNetworks/test -e 'using TestItemRunner; TestItemRunner.run_tests("GraphNeuralNetworks/test"; filter = ti -> ti.name == "GCNConv")' +``` + +Search exact names/paths first with `rg -n '@testitem' GNNGraphs/test GraphNeuralNetworks/test`. The filter name is the string in `@testitem`, not the filename. Shared fixture modules are discovered by TestItemRunner. + +## 13. Existing issues, pull requests and opportunities + +Web access was available, but GitHub's repository issue and pull-request list pages returned stale snapshots (roughly two to three months old), while direct issue #710 and #669 pages were current through 2026-11-02. The PR list snapshot showed 23 open/444 closed and included PR #703 as open, but the local changelog/release notes record #703 as merged; GitHub refused to load the individual PR pages due cache misses. This directly demonstrates why the list cannot be represented as current. Consequently this is a curated issue/PR discovery snapshot, **not a guaranteed exhaustive live list**. The issue #710 page says no development branch or linked PR; the repo's current local `CHANGELOG.md` records already merged fixes through PRs #722, #718, #707, #706, #704 and others. Always refresh each issue/PR page and `git log` before adopting it. + +| Type | Candidate | Evidence and contribution guidance | +|---|---|---| +| Public open issue | [#710 Reactant GCNConv compile error](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710) | Current direct issue page, no linked PR. Strong intermediate/advanced reproducer and test opportunity; see §9. | +| Public newcomer label | [#640 Mooncake CPU compatibility](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/640) | Direct page still says open/good-first-issue and records 16/20 spatial conv layers checked at an earlier point. Status is a useful entry point, but its scope may have changed since replies; ask maintainers what remains. | +| Public issue lead | [#702 Mooncake CUDA issue tracker](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/702) | Appears open in indexed results; inspect current issue and active PR before selecting CUDA work. | +| Public issue lead | [#637 GATConv on heterographs with empty edges/backprop](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/637) | Appears open in indexed results; candidate regression tests around empty relations and gradient paths. | +| Public issue lead | [#671 `add_edges` Zygote segfault](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/671) | Appears open in indexed results; verify current Julia/Zygote version and reproduce first. | +| Public issue lead | [#603 SGConv runtime growth](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/603) | Appears open in indexed results; profile across training iterations and allocator behavior before proposing a fix. | +| Public issue lead | [#600 batching gradients](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/600) | Appears open in indexed results; tests should isolate batch/unbatch and Zygote rather than assume graph construction itself fails. | +| Public feature lead | [#609 COO matrix support](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/609) | Appears open in indexed results; define supported COO semantics/operations and compare sparse/COO backend behavior. | +| Resolved/closed; do not repeat | [#669 Link Prediction tutorial proposal](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/669) | Direct page shows closed Nov 2, 2026 (one completed/not-planned activity); not an open contribution slot. Existing `examples/link_prediction_pubmed.jl` also covers link prediction. | +| Resolved in code; do not repeat | [#591 TGCN activation](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/591) | Search page status is stale, but current changelog records TGCN nonlinear activations merged in #596 and implementation accepts `act`; verify GitHub status before referencing it. | + +Recent public release notes list PRs #701–#722 era fixes/features, including #703 Enzyme graph conversion, #704 GNNGraphs Mooncake extension, #706 Enzyme scaled-Laplacian rule, #707 recurrence inputs for Enzyme, #718 GPU edge-weight propagation, and #722 AMDGPU scaled Laplacian fix. These entries demonstrate active backend/AD work; they are not unmerged contribution opportunities. The public 2026 GSoC project “Improving Automatic Differentiation Support and Performance for GraphNeuralNetworks.jl” already addressed Mooncake and Enzyme, so avoid proposing that same work as novel without identifying a remaining scope. + +### Fifteen+ proposed contribution opportunities + +These are **my proposed work**, not maintainer-approved issues. Check for duplicate PRs first. + +| Level / idea | Relevant code; skills and effort | Tests, impact and GSoC fit | +|---|---|---| +| Beginner: add exact expected-value tests for an under-covered graph query | `GNNGraphs/src/query.jl`, `GNNGraphs/test/query.jl`; Julia `@testitem`, directed graph semantics; low. | Small hand-constructed COO/dense/sparse graphs, isolated node and duplicate-edge cases. Good first PR; not GSoC alone. | +| Beginner: improve graph direction and feature-dimension guide | `GNNGraphs/docs/src/guides/gnngraph.md`, `GNNlib/docs/src/guides/messagepassing.md`; Markdown, tensor axes; low. | Run docs build. Helps prevent common source/target/last-axis mistakes. | +| Beginner: document graph batch/unbatch zero-edge behavior | `GNNGraphs/src/transform.jl`, tests and API guide; graph batching; low. | Roundtrip checks for zero-edge/empty graph in mixed batches. Good first PR if a precise doc/test gap remains. | +| Beginner: add a minimal Lux link-prediction example | `GNNLux/docs`, examples or new tutorial source; Lux state, losses and edge split; low-medium. | Use deterministic tiny graph, disjoint train/eval edges; build docs. GSoC contribution component, not proposal alone. | +| Beginner: clearer argument validation/error tests for feature counts | `GNNGraphs/src/utils.jl`, `gnnheterograph/utils.jl`, matching tests; Julia exceptions/dispatch; low. | Assert stable exception type/message for node/edge mismatch. Useful maintenance. | +| Beginner: document optional GPU setup and backend coverage | package READMEs/docs + `.buildkite/pipeline.yml`; Julia extensions; low. | Check CPU/default and optional backend paths. Clarify per-backend support. | +| Intermediate: resolve remaining CPU Mooncake compatibility gaps | `GNNlib/src/layers/*.jl`, extensions and layer tests; reverse-mode AD, chain rules; medium. | Per-layer finite-difference comparison across COO/sparse then AD. Coordinate with #640/current maintainers. | +| Intermediate: Reactant GCN degree/scatter compatibility | `GNNGraphs/src/query.jl`, maybe new extension, GNNLux GCN tests; array wrappers/compiler tracing; medium-high. | Minimal degree reproducer then full Lux GCN compile, weighted/unweighted. Directly related to #710, GSoC subproject. | +| Intermediate: add GNNLux GPU tests for representative layers | `GNNLux/test`, `test_module.jl`, CI backend matrix; Lux state and GPU memory movement; medium. | Start GCN + pooling on one supported device, no scalar indexing. Broader CI reliability. | +| Intermediate: benchmark and optimize indexed `degree(g, nodes)` | `GNNGraphs/src/query.jl:356-357`; scatter and allocation profiling; medium. | Compare against full degree for repeated/single indices, all semantics/representations; performance PR. | +| Intermediate: port a missing Flux layer to Lux after confirming API intent | `GNNLux/src/layers/conv.jl`, `GNNLux/src/GNNLux.jl`, tests/docs; Lux containers and parameter trees; medium. | Match Flux behavior and gradient expectations; check commented exports and parity discussion first. | +| Intermediate: heterograph empty-edge attention regression fixes | `GNNlib/src/layers/conv.jl`, GNNGraphs hetero query, frontend tests; typed relation reductions; medium. | Zero-edge relation plus reverse-mode; coordinate with #637. | +| Advanced: design a general Reactant graph-operation extension | `GNNGraphs/ext`, `GNNlib/ext`, CI; Reactant tracing/StableHLO/compiler constraints; high. | Multi-operation device test matrix and CPU correctness; possible GSoC-scale theme if maintainers agree. | +| Advanced: sparse message-passing performance study | `GNNlib/src/msgpass.jl`, CUDA/AMDGPU extensions, `GraphNeuralNetworks/perf`; sparse linear algebra/GPU profiling; high. | Benchmark graph density, coalescing, feature width and gradients, report memory too. Could expand into research project. | +| Advanced: AD rules for additional optimized SpMM paths | `GNNlib/ext/GNNlibMooncakeExt.jl`, message-pass tests; reverse rules and sparse matrix calculus; high. | Compare primal and gradients to generic edge path and finite differences on CPU/CUDA. Relevant to active AD area. | +| Advanced: cross-backend empty graph and heterogeneous operator contract | GNNGraphs, GNNlib, extensions, CI; dispatch and accelerator kernels; high. | Backend-specific edge cases and type invariants; broad reliability/GSoC candidate only after scoped proposal. | +| Intermediate: add a benchmark baseline for GNNLux stateful training | `GraphNeuralNetworks/perf`, Lux examples; BenchmarkTools, model state; medium. | Compare identical Flux/Lux model, include warmup and allocations, avoid claiming speed without repeatable data. | + +For each proposal, the actual “expected impact” is a hypothesis until users/maintainers validate it. Avoid changing compatibility bounds, public API or package version without following the repo's SemVer/changelog practices. + +## 14. Contributor workflow (Windows PowerShell) + +This checkout already has local, isolated Julia setup scripts. For a fresh fork/clone: + +```powershell +git clone https://github.com//GraphNeuralNetworks.jl.git +cd GraphNeuralNetworks.jl +git remote add upstream https://github.com/JuliaGraphs/GraphNeuralNetworks.jl.git +git remote -v +.\scripts\setup-dev.ps1 +``` + +For an existing fork, add `upstream` once and sync before a task: + +```powershell +git fetch upstream +git switch master +git pull --ff-only upstream master +git switch -c fix/short-description +``` + +Use the repo-local launcher and package tests: + +```powershell +.\scripts\julia.ps1 --project=GNNGraphs -e 'using Pkg; Pkg.test("GNNGraphs")' +.\scripts\julia.ps1 --project=GNNlib -e 'using Pkg; Pkg.test("GNNlib")' +.\scripts\julia.ps1 --project=GraphNeuralNetworks -e 'using Pkg; Pkg.test("GraphNeuralNetworks")' +.\scripts\julia.ps1 --project=GNNLux -e 'using Pkg; Pkg.test("GNNLux")' +# Optional: CUDA tests after a GPU backend is available/configured +$env:GNN_TEST_CPU = 'false'; $env:GNN_TEST_CUDA = 'true' +.\scripts\julia.ps1 --project=GraphNeuralNetworks -e 'using Pkg; Pkg.test("GraphNeuralNetworks")' +# Docs and formatter +.\scripts\julia.ps1 --project=GraphNeuralNetworks/docs GraphNeuralNetworks/docs/make.jl +.\scripts\julia.ps1 --project=.tools/devtools -e 'using JuliaFormatter; format(".")' +``` + +Before editing, use `rg -n "name_or_symbol"` to find implementation, exports, tests, docs, and similar frontend copies. For a new convolution, the repo guide's sequence is: prototype/test Flux layer; move backend-independent forward math into GNNlib; add Lux wrapper; add docs table entry. The graph/layer feature axis is last, which is often different from dense neural-network examples in other frameworks. + +For a bug PR, include the smallest failing test first, explain expected and observed behavior, include test command/results in the PR, and note CPU/GPU/AD versions. Use focused commits with meaningful messages; no checked-in contribution guide adds a special commit convention. The repository changelog asks user-facing fixes/features to be added under the package's latest/Unreleased heading with the PR reference; bump affected package versions/compat when SemVer requires it. Push your branch to your fork, open a PR against upstream `master`, describe package(s), tests, behavior change and limitations, then respond to review with follow-up commits and re-run relevant suites. Avoid rewriting shared history after review unless maintainers request it. Before finalizing, `git status`, `git diff --check`, and inspect your diff for accidental generated files. + +## 15. GSoC 2027 preparation + +GSoC 2027 project availability, organization acceptance, mentor availability and selection are unknown. The Julia organization accepted a 2026 graph-neural-network project around AD performance and compatibility, which is useful historical context but does not promise the same project will exist in 2027. The official Julia GSoC page is [here](https://www.julialang.org/jsoc/gsoc/gnn/); the 2026 project description is on [Google's archive](https://summerofcode.withgoogle.com/programs/2026/projects/ZEaHtnWu). + +### Good starting areas + +Begin with GNNGraphs constructors/query/transform or a narrow test/documentation task; then learn GNNlib's message passing and Flux/Lux wrappers. Existing AD tests and changelog entries provide concrete examples of mature contribution work. Public maintainer names visible in issues include Carlo Lucibello and recent contributors in the corresponding package areas; use issue discussions/PR reviews to identify who is active for a specific topic, and ask on the public issue rather than assuming mentor assignment. + +### Three possible project directions + +| Proposal direction | Difficulty / impact | Prerequisites and milestones | +|---|---|---| +| **Reactant support for graph message passing** | Advanced; could enable compiler-oriented execution across an important GNN path. | Learn Julia dispatch, Reactant arrays/ops and graph gather/scatter. Milestones: reproduce #710; map operation compatibility; implement one minimal array-shape fix or extension; add degree + GCN tests; broaden to GAT/pooling only if feasible; document unsupported operations and benchmark. | +| **AD correctness/coverage matrix for Flux and Lux** | Intermediate-to-advanced; gives contributors/users reliable layer/backend expectations. | Understand finite differences and reverse rules. Milestones: inventory layer × graph type × AD backend; add expected-value checks where gradient consistency is insufficient; resolve one concrete issue (e.g. remaining #640 scope); add optional CI selection; publish a compatibility table. | +| **Scalable sparse message-passing performance** | Advanced; potential memory/throughput improvements on sparse and accelerator graphs. | Sparse linear algebra, GPU profiling, algorithmic complexity. Milestones: establish reproducible baselines; identify dominant path; optimize one kernel/fast path; add exact parity/gradient checks; document crossover conditions and limitations. | + +### Proposal preparation checklist and timeline + +Use the 30/60/90-day plan in `docs/CONTRIBUTION_ROADMAP.md`. A strong proposal should identify a maintainer-confirmed scope, cite current issues/PRs, show a working small PR, state baseline measurements or reproducer, break work into testable milestones, describe risks/fallbacks and specify mentorship/check-in cadence. Do not claim a proposal is accepted or available until the organization publishes its 2027 project list. + +## 16. Learning resources + +- Repository architecture and commands: `AGENTS.md`, `GraphNeuralNetworks/docs/src/dev.md`, package READMEs. +- Graph APIs and representations: `GNNGraphs/docs/src/guides/gnngraph.md`, `heterograph.md`, `temporalgraph.md`. +- Message passing: `GNNlib/docs/src/guides/messagepassing.md` and `api/messagepassing.md`. +- Layer API/tutorials: `GraphNeuralNetworks/docs/src/api/`, `GNNLux/docs/src/api/`, beginner tutorials. +- Julia package and dispatch basics: [Julia manual](https://docs.julialang.org/en/v1/), [Pkg documentation](https://pkgdocs.julialang.org/), and Julia package extension docs linked from Julia's manual. +- Scientific graph neural networks: repository's JMLR paper, [GraphNeuralNetworks.jl: Deep Learning on Graphs with Julia](https://www.jmlr.org/papers/v26/24-2130.html). +- Current open-source context: issue pages linked in §13; always reload before proposing work because public search snapshots can lag. + +## 17. Glossary + +| Term | Meaning here | +|---|---| +| COO | Coordinate graph representation: source and target integer vectors, optionally followed by edge weights. | +| Sparse/dense graph | Adjacency matrix representation, with different operation and duplicate-edge behavior from COO. | +| `xi` / `xj` | Target/root-node and source/neighbour features, respectively, after edge materialization. | +| Message passing | Compute a per-edge message, then aggregate incoming messages to destination nodes. | +| Scatter | Add/update edge-aligned values into an output indexed by destination node (or graph). | +| Gather | Select node/graph values using edge/node indices. | +| Coalesced | COO edges are canonicalized and duplicates removed/combined, enabling certain fast paths. | +| Batch graph | Multiple independent graphs represented as one disconnected graph plus graph membership indicators. | +| Heterograph | Graph with typed node sets and typed relations; features and counts are per type. | +| Lux `ps`, `st` | Explicit model parameters and non-parameter state passed into/out of a Lux layer. | +| Weak dependency / extension | Optional package dependency that activates specialized methods only when loaded. | +| Multiple dispatch | Julia selects a method from the types of all arguments, allowing graph/backend-specific implementations. | +| AD | Automatic differentiation; gradient support depends on the exact operation and backend path. | +| Reactant | Julia package that traces/compiles numerical Julia programs to accelerator/compiler execution; its traced array/scalar types affect dispatch. | +| `@testitem` | Independent named test block discovered and run by TestItemRunner. | +| DEV version | Unreleased development version suffix used by this monorepo before a package is tagged. | + diff --git a/docs/CURRENT_ISSUE_REGISTER.md b/docs/CURRENT_ISSUE_REGISTER.md new file mode 100644 index 000000000..90b266573 --- /dev/null +++ b/docs/CURRENT_ISSUE_REGISTER.md @@ -0,0 +1,45 @@ +# Current upstream issue register + +**Checked:** 2026-11-04 (Asia/Calcutta) +**Repository:** upstream `JuliaGraphs/GraphNeuralNetworks.jl`; local working copy is the fork `DHAKED440/GraphNeuralNetworks.jl`. +**Local checkout:** `master` at `aa2926c`; the existing audit notes below are from 2026-11-03. The local checkout was not fetched during this pass. + +This register reflects a current spot-check of public GitHub pages plus the repository audit in [`CONTRIBUTOR_RESEARCH_REPORT.md`](CONTRIBUTOR_RESEARCH_REPORT.md). It is not a complete record of all historical issues, PRs, comments, or commits: `gh` is unavailable, GitHub's list pages are dynamically loaded, and the API endpoints could not be read in this environment. As of the page snapshots, GitHub showed 54 open issues and 23 open PRs (444 closed PRs shown on the PR page). Treat counts and statuses as a snapshot, not a full export. Issue #710 was opened directly and its reproduction/details were readable. No source changes are included in this register. + +## Findings + +| ID | Category | Status | Location | Difficulty | Evidence and next step | +|---|---|---|---|---|---| +| BUG-001 | Backend | Reported, reproduction provided; not locally reproduced | `GNNGraphs/src/query.jl` (`degree`); called by `GNNlib/src/layers/conv.jl` | Advanced | [Upstream #710](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710) reports `GCNConv` failing in `Reactant.@compile`: scatter receives `Vector{Vector{TracedRNumber}}` while available methods expect traced arrays. The issue is open, has `gnnlib` and `lux` labels, and no linked branch/PR on the issue page. A local code-path analysis is in the research report. Reproduce on the stated package/device versions before changing graph degree or adding a Reactant-specific extension. | +| BUG-002 | Backend / AD | Reported; current behavior not verified | `GNNGraphs/src/transform.jl` (`add_edges`) | Advanced | [Upstream #671](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/671) reports Zygote segfault when differentiating across `add_edges`. The current issue index shows it open. No new independent reproduction was run. Minimize the reporter's case and check current upstream implementation/linked work before proposing a fix. | +| BUG-003 | Backend / AD | Reported; current behavior not verified | Heterogeneous GAT path; `GraphNeuralNetworks/src/layers/heteroconv.jl`, `GNNlib/src/layers/conv.jl` | Advanced | [Upstream #637](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/637) reports empty edge arrays and backpropagation failures for `GATConv` on heterographs. A related open PR, [#638](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/638), is titled “Gate empty result in GATConv”. Inspect the PR diff/checks and run the minimal empty-relation case before selecting a contribution. | +| BUG-004 | Performance | Reported; not reproduced | `GNNlib/src/layers/conv.jl` (`SGConv`) | Intermediate | [Upstream #600](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/600) reports progressively increasing runtime in a training loop. The report is not proof of a leak. Profile repeated forward/backward iterations, allocations, and compilation separately before assigning cause. | +| BUG-005 | AD / compatibility | Reported; current compatibility not verified | `GNNGraphs/ext/`, `GNNGraphs/test/ext/` | Advanced | [Upstream #702](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/702) is “Mooncake on CUDA issue tracker”. Inspect the individual reports and current extension support; do not treat it as one confirmed defect without a reproducible case. | +| TEST-001 | Testing | Missing coverage | `GNNLux/test/` | Beginner | Existing audit found the Lux test runner states GPU coverage has not been added. Add backend-tagged coverage for supported operations once a reliable CI/device strategy is chosen; this is a coverage opportunity, not evidence of a runtime failure. | +| PERF-001 | Performance | Improvement opportunity | `GNNGraphs/src/query.jl` (`Graphs.degree(g, i)`) | Intermediate | Existing audit found this overload computes the full degree vector before selecting node `i` and has a TODO to make it efficient. Benchmark repeated single-node queries and preserve directedness/representation semantics before optimizing. | +| DOC-001 | Documentation | New opportunity | `GNNLux/docs/`, `GraphNeuralNetworks/docs/` | Beginner | Upstream has open documentation proposals including [#669](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/669) (link prediction tutorial) and [#603](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/603) (reinstate TemporalBrains tutorial). Check current pages and issue discussion before taking either. | +| API-001 | Feature parity | Improvement opportunity | `GNNLux/src/GNNLux.jl`, `GNNLux/src/layers/` | Advanced | Existing audit noted Flux exports `TAGConv` and `TransformerConv` while GNNLux does not. Upstream has an open WIP TransformerConv PR [#501](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/501). Review its present state and design intent before duplicating or extending it. | + +## Active PRs worth triaging + +The public PR listing snapshot showed 23 open PRs. These are directly relevant to the findings/opportunities above; the list is not exhaustive. + +| PR | Topic | Register relation | Recommended check | +|---|---|---|---| +| [#703](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/703) | Fix Enzyme crash on dense/sparse adjacency graphs | AD/backend | Inspect changed methods, review status and checks; compare with issue #609 and current Enzyme extension. | +| [#697](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/697) | Learnable epsilon in GIN | Feature | Check whether it is active and whether API/tests/docs are complete before proposing overlapping work. | +| [#638](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/638) | Gate empty result in GATConv | BUG-003 | Verify it resolves the heterograph empty-edge and gradient case, not just the reported forward path. | +| [#501](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/501) | WIP GNNLux TransformerConv | API-001 | Inspect latest activity and implementation/tests; avoid parallel implementation until its disposition is clear. | +| [#657](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/657), [#654](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/654), [#653](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/653), [#649](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/649) | Heterogeneous convolution support | Feature coverage | Check current review state and API consistency across Flux/Lux/GNNlib before choosing a narrowly scoped contribution. | + +## Audit stages and limits + +1. **Repository audit:** The prior research report maps the four-package workspace, source, tests, docs, examples, extensions, CI and benchmarks. It records CPU test results for all four package suites. No GPU suite or documentation build was run in that audit. +2. **GitHub history:** Current issue/PR counts and selected current pages were inspected. A complete upstream export and full comments/reviews/commit-by-commit review were not available because GitHub CLI is absent and the API could not be accessed through the browsing tool. +3. **Cross-reference:** The report traces #710 into the current local source path. Other issue/PR links above remain leads requiring their full discussion and diff to be checked against a fresh upstream checkout. +4. **Independent bug hunt:** The prior report found no separate bug established by static review. The listed performance and coverage items are not classified as bugs. +5. **Verification:** #710 has a reporter-provided minimal reproduction but was not reproduced locally; Reactant is not in the test environment. No bug in this register is newly marked “confirmed unfixed” based solely on its issue being open. + +## Suggested next contribution + +Start with TEST-001 or a narrowly scoped documentation improvement. For a bug fix, #710 has the clearest technical trace, but first reproduce it with the stated Reactant setup and establish whether the traced scalar materialization is caused by GNNGraphs or Reactant's scatter API. For all work, check the live issue/PR state and latest upstream source immediately before opening an issue or branch. diff --git a/docs/PR_READY_AUDIT_BACKLOG.md b/docs/PR_READY_AUDIT_BACKLOG.md new file mode 100644 index 000000000..cf30f31ba --- /dev/null +++ b/docs/PR_READY_AUDIT_BACKLOG.md @@ -0,0 +1,144 @@ +# Contribution audit backlog + +**Audited checkout:** fork `DHAKED440/GraphNeuralNetworks.jl`, branch `master`, commit `aa2926c` (local checkout; not refreshed from upstream during this pass). +**Audit date:** 2026-11-05 (Asia/Calcutta). +**Scope:** inspected repository map and prior file-by-file audit, then rechecked the source/test paths cited below. Julia reproductions were run only for the three sampling/loader items. No tracked source or tests were changed. + +This backlog has 24 independently scoped contribution candidates. Only AUDIT-001 through AUDIT-003 are reproduced bugs in this checkout. AUDIT-004 is a strong static backend hypothesis that still needs CUDA execution. AUDIT-005 through AUDIT-008 are existing upstream reports and must be checked against their linked work before taking them on. The rest are test, performance, documentation, or feature contributions, not claims that the current implementation is defective. A PR-ready scope means the work has a bounded change and acceptance criteria; it does not mean every item is already confirmed as an upstream issue. + +## Findings and contribution scopes + +| ID | Category | Status | Location | Difficulty | Suggested PR scope / title | +|---|---|---|---|---|---| +| AUDIT-001 | Bug | **Confirmed, reproduced** | `GNNGraphs/src/sampling.jl:36-38` | Beginner | Make `NeighborLoader`'s documented all-nodes default usable; regression test constructing it without `input_nodes`. Title: `Fix NeighborLoader default input nodes`. | +| AUDIT-002 | Bug | **Confirmed, reproduced** | `GNNGraphs/src/sampling.jl:175-185` | Beginner | Interpret `K=0` as sampling zero edges; retain `K=-1` as the documented all-neighbors sentinel. Test both `dir` values and replacement modes. Title: `Respect zero neighbor sample count`. | +| AUDIT-003 | Bug | **Confirmed, reproduced** | `GNNGraphs/src/sampling.jl:175-190` | Beginner | Return a valid empty sampled graph for `nodes=[]` (or explicitly validate/document otherwise); test weighted and feature-bearing inputs if empty sampling is supported. Title: `Handle empty node selections in sample_neighbors`. | +| AUDIT-004 | Backend bug candidate | **Strong source evidence; CUDA reproduction unavailable** | `GNNGraphs/ext/GNNGraphsCUDAExt.jl:26-29`; called by `GNNGraphs/src/query.jl:394-398` | Intermediate | Preserve requested element type `T` while creating sparse CUDA binarization values. Verify `eltype(binarize(A, Bool))` and `degree(g, edge_weight=false)` on CUDA. Title: `Preserve output type in CUDA sparse binarize`. | +| AUDIT-005 | Backend bug | **Reported upstream; not locally reproduced** | `GNNGraphs/src/query.jl:378-390`; `GNNlib/src/layers/conv.jl:52-57`; `GNNLux/src/layers/conv.jl:128-138` | Advanced | Reproduce Reactant issue [#710](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710) and establish where traced scalar edge values arise before changing generic degree behavior. No linked PR was shown on the issue page when checked. Title: `Fix Reactant compilation of COO degree in GCNConv`. | +| AUDIT-006 | Bug / AD | **Reported upstream; needs reproduction** | `GNNGraphs/src/transform.jl` (`add_edges`) | Advanced | Investigate Zygote segfault report [#671](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/671); reduce it to a graph and feature case and add a regression test before proposing an AD rule. Title: `Fix Zygote differentiation through add_edges`. | +| AUDIT-007 | Bug / backend / AD | **Reported upstream; active PR overlap** | Heterogeneous GAT path; `GNNlib/src/layers/conv.jl`, `GraphNeuralNetworks/src/layers/heteroconv.jl` | Advanced | Coordinate with open report [#637](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/637) and related PR [#638](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/638). Only take a gap the PR does not cover (empty relation forward pass vs. gradient). Title: `Cover empty-relation GATConv behavior`. | +| AUDIT-008 | Performance / bug investigation | **Reported upstream; no profiling performed** | `GNNlib/src/layers/conv.jl` (`SGConv`) | Intermediate | Profile the repeated-runtime report [#600](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/600) across warm-up, forward, backward, allocations, and compilation. Do not open a leak fix until a cause is measured. Title: `Avoid repeated SGConv work during training` (only if profiling confirms it). | +| AUDIT-009 | Performance | **Optimization opportunity; benchmark required** | `GNNGraphs/src/query.jl:356-357` | Intermediate | Replace full-vector degree materialization for `degree(g, i)` / selected indices with a measured path that preserves direction and graph representation behavior. Title: `Compute selected GNNGraph degrees without full materialization`. | +| AUDIT-010 | Performance / backend | **Implementation opportunity; benchmark required** | `GNNGraphs/ext/GNNGraphsCUDAExt.jl:32-40` | Advanced | Remove the CUDA→CPU→CUDA round trip in `sort_edge_index` if a device sort is competitive and preserves the package's edge ordering. Add CUDA equality and no-scalar-index tests. Title: `Sort CUDA edge indices on device`. | +| AUDIT-011 | Feature / testing | **Representation limitation explicitly reflected in tests** | `GNNGraphs/src/sampling.jl:168-175`; `GNNGraphs/test/sampling.jl:1-5` | Advanced | Extend `sample_neighbors` to supported adjacency representations, or clearly specify COO-only behavior and give users a conversion path. Current tests skip all non-COO graph types. Title: `Support neighbor sampling across graph representations`. | +| AUDIT-012 | Feature | **Explicit restriction in implementation** | `GNNGraphs/src/gnnheterograph/transform.jl:165-203` | Advanced | Support batching heterogeneous graphs that are themselves batches, with per-type indicators and offsets; add two-input cases with different type/relation sets. Current method asserts each input has one graph. Title: `Batch already-batched heterogeneous graphs`. | +| AUDIT-013 | Feature | **Explicitly unimplemented option** | `GraphNeuralNetworks/src/layers/pool.jl:151-156` | Intermediate | Implement `Set2Set(..., n_layers>1)` or remove the misleading accepted argument and document the one-layer API. Test output and gradients for multiple layers. Title: `Support multilayer Set2Set pooling`. | +| AUDIT-014 | Feature | **Explicit limitation enforced in constructors** | `GNNlib/src/layers/conv.jl:116-121,175-180`; corresponding Flux/Lux constructors | Advanced | Define edge-feature values for inserted self-loops and implement the policy consistently across GAT/GATv2 and Flux/Lux. Current constructors reject edge features when `add_self_loops=true`. Title: `Define self-loop edge features for attention convolutions`. | +| AUDIT-015 | Testing | **Concrete coverage gap marked in tests** | `GNNLux/test/layers/conv.jl:59-81` | Beginner | Add Lux GAT/GATv2 edge-feature tests using `test_lux_layer(...; e=...)`, checking numeric output, gradients, heads and `concat=false`. The two blocks currently end with `TODO test edge`. Title: `Test edge-feature paths in Lux attention layers`. | +| AUDIT-016 | Testing / backend | **Coverage gap explicitly acknowledged** | `GNNLux/test/runtests.jl:17-30`; `GNNLux/test/test_module.jl` | Intermediate | Add tagged Lux GPU checks for a small representative set of layers and run only on configured backends; keep optional dependency setup consistent with existing harness. Title: `Add GPU coverage for GNNLux layers`. | +| AUDIT-017 | Testing | **Coverage gap explicitly marked in test file** | `GNNlib/test/msgpass.jl:1-4` | Intermediate | Exercise message passing over each graph representation for supported message fast paths, with hand-computed results and empty-edge cases. Current test file states it tests only COO. Title: `Cover graph representations in GNNlib message-passing tests`. | +| AUDIT-018 | Testing | **Coverage gap explicitly marked in test file** | `GNNlib/test/utils.jl:1-6` | Intermediate | Extend graph utility tests beyond COO for reductions, softmax, and graph/edge broadcasting where those methods support the representation. Keep unsupported behavior explicit. Title: `Cover graph representations in GNNlib utility tests`. | +| AUDIT-019 | Testing | **Coverage gap explicitly marked in test file** | `GNNGraphs/test/temporalsnapshotsgnngraph.jl:1` | Beginner | Parameterize temporal snapshot constructor tests over supported graph types and assert per-snapshot representation, node/edge features, and graph metadata. Title: `Test temporal snapshots across graph types`. | +| AUDIT-020 | Testing | **Coverage gap explicitly marked in test file** | `GNNGraphs/test/gnngraph.jl:1` | Beginner | Add constructor/conversion tests that assert requested graph representation is preserved, including copy construction and GPU/device paths if supported. Title: `Verify GNNGraph representation preservation`. | +| AUDIT-021 | Architecture / AD | **Known TODO; behavior and desired derivative need design** | `GNNGraphs/src/transform.jl:1031-1032`; related `GNNGraphs/test/ext/Mooncake.jl` | Advanced | Specify and test gradients for graphs carrying feature arrays through `add_self_loops` / `remove_self_loops`; then replace blanket non-differentiability only with correct rules. Title: `Define AD rules for self-loop graph transforms`. | +| AUDIT-022 | Performance / representation | **Optimization opportunity explicitly noted** | `GNNGraphs/src/transform.jl:572-581` | Intermediate | Preserve adjacency-matrix representation in `set_edge_weight` where semantics allow, or document why COO conversion is necessary. Add parity tests for dense/sparse weighted degree and graph conversion. Title: `Preserve graph representation in set_edge_weight`. | +| AUDIT-023 | Documentation | **Open upstream proposal; check current activity** | `GraphNeuralNetworks/docs/`, `GNNLux/docs/` | Beginner | Work with the existing link-prediction documentation proposal [#669](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/669): build an executable tutorial covering negative sampling, edge splits, batching and evaluation without leakage. Title: `Add a link-prediction tutorial`. | +| AUDIT-024 | API / feature parity | **Open WIP PR overlap; not a duplicate target yet** | `GNNLux/src/GNNLux.jl`, `GNNLux/src/layers/` | Advanced | Review the current disposition of [PR #501](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/501) before work. If abandoned, scope Lux `TransformerConv` with Flux parity, initialization/state tests and API docs. Title: `Add TransformerConv to GNNLux`. | + +## Individually reproduced bugs + +### AUDIT-001 — `NeighborLoader` default `input_nodes` cannot be used + +**Evidence:** The keyword is declared `input_nodes::Vector{Int}=nothing` at `GNNGraphs/src/sampling.jl:36-38`, despite the following expression attempting to support `nothing`. Julia dispatch rejects the default value before the constructor body runs. + +**Reproduction run:** + +```julia +using GNNGraphs +g = GNNGraph([1], [2]) +NeighborLoader(g; num_neighbors=[1], num_layers=1) +``` + +**Actual:** `MethodError: no method matching #NeighborLoader#...(::Vector{Int64}, ::Nothing, ...)`, pointing to `sampling.jl:37`. **Expected:** a loader over all graph nodes, as the constructor's own fallback indicates. Existing tests in `GNNGraphs/test/sampling.jl:86-205` always provide `input_nodes`, so they miss the advertised default. + +**Minimal fix direction:** accept `nothing` in the keyword type (or remove its restrictive annotation), resolve `nodes = input_nodes === nothing ? collect(1:graph.num_nodes) : input_nodes`, and derive the default batch size from `nodes`. Regression test should instantiate with omitted `input_nodes`, iterate, and verify all nodes are covered. Risk is low if explicit `input_nodes` and explicit `batch_size` behavior stay unchanged. This is suitable as a standalone PR. + +### AUDIT-002 — `sample_neighbors(..., K=0)` samples edges + +**Evidence:** At `GNNGraphs/src/sampling.jl:180-185`, `K > 0 ? ... : length(eidlist[i])` treats zero like the documented `-1` sentinel. The docstring says `K=-1` means all neighbors and describes `K` as the maximum sampled count. + +**Reproduction run:** + +```julia +using GNNGraphs +g = GNNGraph([1, 2, 3], [2, 3, 1]) +sg = sample_neighbors(g, [1], 0; dir=:out) +(g.num_edges, sg.num_edges) +``` + +**Actual:** `(3, 1)`. **Expected:** `sg.num_edges == 0` for `K=0`; `K=-1` should continue to select all neighbors. Existing sampling tests cover positive K and the default, but not zero. Add tests for `replace=false/true` and both directions. A tight change to distinguish `K == -1` from `K == 0` is low-risk; decide whether values below `-1` should error or retain legacy behavior. + +### AUDIT-003 — empty `nodes` fails in `sample_neighbors` + +**Evidence:** At `GNNGraphs/src/sampling.jl:187`, `reduce(vcat, eidlist)` has no initial value. For an empty input node collection, `eidlist` is empty and Julia throws before a graph can be returned. + +**Reproduction run:** + +```julia +using GNNGraphs +g = GNNGraph([1], [2]) +sample_neighbors(g, Int[]) +``` + +**Actual:** `ArgumentError: reducing over an empty collection is not allowed`, from `sampling.jl:187`. **Expected:** an empty sampled graph with stable node/edge metadata, or a clear argument validation error if empty selections are intentionally forbidden. Existing tests do not cover it. The minimal PR should establish the desired contract and test empty input with `dropnodes` both false and true. Risk centers on preserving graph feature-store dimensions and graph count for the empty result. + +## Draft GitHub issue titles and descriptions + +Use these as drafts, not as a recommendation to open 24 separate issues. For small test/documentation changes, a focused PR may be more useful than an issue. For rows linked to an existing issue/PR, comment or contribute there instead of filing a duplicate. Rows marked **verify first** need reproduction, profiling, hardware, or design confirmation before they are filed. + +| ID | Create a new issue? | Suggested issue title | Draft description | Where / root cause | User impact | +|---|---|---|---|---|---| +| AUDIT-001 | **Yes, after checking the live issue search** | `NeighborLoader errors when input_nodes is omitted` | `NeighborLoader` declares `input_nodes::Vector{Int}=nothing`, so constructing a loader without that keyword fails with a MethodError before its intended all-nodes fallback runs. Reproducer: `g = GNNGraph([1], [2]); NeighborLoader(g; num_neighbors=[1], num_layers=1)`. Expected: use all graph nodes as input nodes; actual: MethodError at `GNNGraphs/src/sampling.jl:37`. | `GNNGraphs/src/sampling.jl:36-38`; restrictive keyword type conflicts with its `nothing` default. | Users cannot use the constructor's apparent default; they must pass an explicit node vector. | +| AUDIT-002 | **Yes, after checking the live issue search** | `sample_neighbors treats K=0 as sample all neighbors` | `sample_neighbors(g, nodes, K)` documents `K=-1` as the all-neighbors sentinel, but the current `K > 0 ? ... : all` branch also treats `K=0` as all. Reproducer: `sample_neighbors(GNNGraph([1,2,3],[2,3,1]), [1], 0; dir=:out)` returns one edge. Expected: zero sampled edges for `K=0`; `K=-1` should retain current all-neighbor behavior. | `GNNGraphs/src/sampling.jl:180-185`; zero and negative sentinel values share one branch. | Callers cannot request an empty neighbor sample and may silently sample more edges than their configured budget. | +| AUDIT-003 | **Yes, after checking the live issue search** | `sample_neighbors errors for an empty node selection` | `sample_neighbors(g, Int[])` currently throws `ArgumentError: reducing over an empty collection is not allowed` at `reduce(vcat, eidlist)`. Expected behavior should be either a well-formed empty sampled graph or an intentional, documented input validation error. Please clarify the expected metadata/features behavior for `dropnodes=false` and `dropnodes=true`. | `GNNGraphs/src/sampling.jl:175-190`; `reduce(vcat, ...)` has no empty identity here. | Empty minibatches or dynamically filtered node sets fail unexpectedly instead of producing a defined result. | +| AUDIT-004 | **No, verify first** | `CUDA sparse binarize may ignore requested output type` | The CUDA extension allocates values with `similar(nonzeros(Mat))`, which appears to retain the input element type even when a different `T` is requested. Could someone with CUDA verify `eltype(binarize(A, Bool))` and `degree(g, edge_weight=false)` for Float32 sparse adjacency? This is a static suspicion only; I have not run it on CUDA. | `GNNGraphs/ext/GNNGraphsCUDAExt.jl:26-29`; likely allocation element type is inherited from input values. | If confirmed, callers asking for Boolean/integer results may receive Float32 values or unexpected downstream result types on CUDA. | +| AUDIT-005 | **No** | Existing issue: [#710](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/710) | Do not open a duplicate. The existing report describes Reactant compilation of Lux `GCNConv` failing in degree scatter and includes a reproducer and stack trace. Reproduce with the reported versions, identify where traced scalar weights arise, and add a regression test once the failure path is established. | `GNNGraphs/src/query.jl:378-390`, `GNNlib/src/layers/conv.jl:52-57`; reporter traces into COO degree scatter. | Reactant users cannot compile this GCN forward path in the reported setup. | +| AUDIT-006 | **No** | Existing issue: [#671](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/671) | Do not open a duplicate. The issue reports a Zygote segfault when differentiating across `add_edges`. Please reduce the reproducer to the smallest graph/feature case and confirm it against the current supported Julia/Zygote versions before proposing an AD fix. | `GNNGraphs/src/transform.jl` (`add_edges`); reported AD failure. | Affected users may be unable to differentiate training code that constructs or mutates graph topology inside a differentiated function. | +| AUDIT-007 | **No** | Existing issue [#637](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/637), related PR [#638](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/638) | Do not file a duplicate. The issue reports heterogeneous `GATConv` failures with empty edge arrays/backpropagation, and PR #638 appears to address empty results. Check the latest PR diff and tests; report or implement only a remaining uncovered case. | `GNNlib/src/layers/conv.jl`; `GraphNeuralNetworks/src/layers/heteroconv.jl`. | Empty heterogeneous relations can block inference or training for otherwise valid heterographs. | +| AUDIT-008 | **No** | Existing issue: [#600](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/600) | Do not open a duplicate or call this a memory leak yet. The report describes runtime increasing through a training loop. Profile warm-up, compilation, forward/backward time, and allocations separately; share measurements and versions on the issue before proposing a fix. | `GNNlib/src/layers/conv.jl` (`SGConv`); cause unknown without profiling. | If confirmed, training throughput may degrade over time, making SGConv impractical for longer runs. | +| AUDIT-009 | **Usually direct PR; issue optional** | `Avoid materializing all node degrees for indexed degree queries` | The indexed `degree(g, i)` overload computes the full degree vector and then indexes it. Would a specialized selected-node path be welcome? A PR should include benchmarks for single-node and sparse-index queries and exact parity tests for `:in`, `:out`, and supported graph representations. | `GNNGraphs/src/query.jl:356-357`; implementation contains an explicit efficiency TODO. | Repeated local degree queries do unnecessary work proportional to the whole graph rather than the requested nodes. | +| AUDIT-010 | **Usually direct PR; issue optional** | `Avoid host round-trips when sorting CUDA edge indices` | CUDA `sort_edge_index` currently copies both endpoint arrays to CPU, invokes the CPU sorter, then copies results back. Would maintainers accept a device-side implementation? Please preserve ordering exactly and benchmark representative graph sizes before changing the path. | `GNNGraphs/ext/GNNGraphsCUDAExt.jl:32-40`; explicit CPU transfer in CUDA method. | Graph preprocessing can incur device synchronization, transfer overhead, and temporary host memory. | +| AUDIT-011 | **Discuss scope first; likely issue before feature PR** | `Support sample_neighbors for adjacency-matrix graph representations` | `sample_neighbors` is currently dispatched only for COO graphs, and the test loop skips non-COO graph types. Should the API support dense/sparse adjacency graphs directly, or should the COO-only restriction be documented as intentional? A feature proposal should define weighted edges, duplicate edges, and edge-ID behavior. | `GNNGraphs/src/sampling.jl:168-175`; tests skip non-COO at `GNNGraphs/test/sampling.jl:1-5`. | Users with dense/sparse graphs must convert representation or cannot use neighbor sampling through this API. | +| AUDIT-012 | **Discuss scope first** | `Allow batching already-batched heterogeneous graphs` | `MLUtils.batch` for heterographs asserts that every input has `num_graphs == 1`. Would support for batching existing heterograph batches be useful? A design should define per-node-type offsets and graph indicators when input graphs have different node/relation type sets. | `GNNGraphs/src/gnnheterograph/transform.jl:165-203`; explicit assertion restricts the input. | Existing batches cannot be combined into larger batches without unbatching first. | +| AUDIT-013 | **Issue optional; feature scope should be agreed** | `Implement the n_layers option in Set2Set` | `Set2Set(n_in, n_iters, n_layers)` accepts `n_layers`, but asserts that it equals one. Would maintainers like true multilayer LSTM support, or should the argument be removed from the public constructor? A feature PR should include output-shape, state, gradient, and Flux serialization tests. | `GraphNeuralNetworks/src/layers/pool.jl:151-156`; current constructor rejects values above one. | Users cannot configure the multilayer Set2Set architecture suggested by the public signature/docstring. | +| AUDIT-014 | **Feature issue recommended before broad implementation** | `Define edge-feature behavior for GAT self-loops` | GAT/GATv2 constructors reject `add_self_loops=true` when edge features are configured. Could the API define a policy for self-loop edge attributes (for example, explicit user-provided values or a documented default)? Please specify consistent behavior across Flux and Lux before implementation. | `GNNlib/src/layers/conv.jl:116-121,175-180` and frontend constructors; self-loop/edge-feature combination is explicitly rejected. | Models needing both edge attributes and self-loops must disable one capability or preprocess graph data externally. | +| AUDIT-015 | **Direct test PR; issue usually unnecessary** | `Add Lux GAT and GATv2 edge-feature tests` | Lux GAT and GATv2 tests cover node-only inputs but leave `TODO test edge`. Please add edge-feature cases using the existing `test_lux_layer` helper, including output size, gradient checks, multiple heads, and `concat=false`. | `GNNLux/test/layers/conv.jl:59-81`; edge-test TODOs. | Regressions in Lux edge-attention paths may not be caught even though corresponding layer functionality exists. | +| AUDIT-016 | **Discuss CI/hardware scope first; issue optional** | `Add backend-tagged GPU tests for GNNLux` | GNNLux's test runner notes that GPU tests are not yet added. Would maintainers like a small tagged matrix for supported CUDA/AMDGPU/Metal paths? Please identify available CI hardware and test a representative layer on each backend before expanding coverage. | `GNNLux/test/runtests.jl:17-30`; no Lux GPU-tagged items identified in the test setup reviewed. | Lux-specific wrapper, parameter, or state behavior can regress on accelerators without being exercised by the Flux GPU suite. | +| AUDIT-017 | **Direct test PR; issue usually unnecessary** | `Expand GNNlib message-passing tests across graph types` | `GNNlib/test/msgpass.jl` contains a TODO to test all graph types and currently selects COO. Please add hand-computed parity cases for the message-passing paths that claim support for dense/sparse/COO, including empty-edge behavior where defined. | `GNNlib/test/msgpass.jl:1-4`; graph-type test coverage is explicitly limited. | Representation-specific message-passing regressions can pass the current COO-only tests unnoticed. | +| AUDIT-018 | **Direct test PR; issue usually unnecessary** | `Expand GNNlib utility tests across graph types` | `GNNlib/test/utils.jl` contains a TODO to test all graph types and constructs a COO batch. Please add supported-representation tests for reductions, softmax, and graph/edge broadcasting, documenting unsupported combinations explicitly. | `GNNlib/test/utils.jl:1-6`; existing setup uses COO. | Utility behavior may differ by representation without a regression test to reveal it. | +| AUDIT-019 | **Direct test PR; issue usually unnecessary** | `Test temporal snapshot constructors across supported graph types` | `GNNGraphs/test/temporalsnapshotsgnngraph.jl` notes that graph-type arguments are missing from constructor tests. Please parameterize those cases over supported representations and verify snapshot topology, features, and metadata are preserved. | `GNNGraphs/test/temporalsnapshotsgnngraph.jl:1`; explicit coverage TODO. | Constructor/conversion regressions could affect only one representation and remain undetected. | +| AUDIT-020 | **Direct test PR; issue usually unnecessary** | `Test GNNGraph representation preservation during construction and copy` | `GNNGraphs/test/gnngraph.jl` has a TODO to test graph-type preservation. Please add tests for explicit `graph_type` requests and copy construction, asserting the actual representation with `get_graph_type` and preserving graph counts/features. | `GNNGraphs/test/gnngraph.jl:1`; explicit coverage TODO. | Users may request or copy a representation and not have a focused test that catches accidental conversion. | +| AUDIT-021 | **Design discussion first; not a simple issue** | `Specify AD behavior for self-loop graph transforms` | The source marks blanket non-differentiability for `add_self_loops` and `remove_self_loops` as potentially incorrect because graphs carry feature arrays. What derivative should topology-only transforms provide for node/edge/graph features and edge weights? Please agree on semantics before adding rules. | `GNNGraphs/src/transform.jl:1031-1032`; current blanket AD declarations. | Differentiated code that consumes graph features or edge weights after these transforms may lose gradients or encounter backend-specific failures. | +| AUDIT-022 | **Optimization proposal; benchmark first** | `Preserve adjacency representation in set_edge_weight` | `set_edge_weight` obtains COO edge indices and reconstructs a COO graph even when its input is dense/sparse adjacency. Would preserving the original representation be desirable? Please benchmark and specify duplicate-edge/weighted-adjacency semantics before proposing the change. | `GNNGraphs/src/transform.jl:572-581`; explicit representation-preservation TODO. | Callers may incur conversion and storage overhead or lose the representation they selected. | +| AUDIT-023 | **No** | Existing proposal: [#669](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/issues/669) | Do not duplicate the existing link-prediction tutorial proposal. Offer a documentation PR or ask on that issue whether work is still wanted. Suggested content: edge splitting without leakage, negative sampling, training/evaluation, and runnable examples using current APIs. | `GraphNeuralNetworks/docs/` and `GNNLux/docs/`; existing proposal tracks this contribution. | New users lack a guided, reproducible path through link-prediction workflows. | +| AUDIT-024 | **No** | Existing WIP PR: [#501](https://github.com/JuliaGraphs/GraphNeuralNetworks.jl/pull/501) | Do not open a duplicate issue or PR while the WIP's status is unknown. Check latest activity and ask whether the work is active; if it is abandoned, offer to continue with Lux `TransformerConv`, tests, exports, and docs. | `GNNLux/src/GNNLux.jl`, `GNNLux/src/layers/`; existing PR overlap. | Lux users may lack an equivalent layer available in the Flux frontend. | + +### Filing guidance + +- The best candidates for a new bug report are **AUDIT-001 to AUDIT-003**, after a final search of current open and closed issues. Include the reproducer and actual error/output exactly as recorded above. +- **AUDIT-004** should not be filed as a confirmed bug until a CUDA run verifies the returned value type and affected degree behavior. +- **AUDIT-005 to AUDIT-008, AUDIT-023 and AUDIT-024** already have upstream tracking; contribute there rather than opening duplicates. +- For **AUDIT-015, AUDIT-017 to AUDIT-020**, a small test-only PR is likely more actionable than an issue, provided the tested behavior is intended and supported. +- For feature or architecture proposals, ask maintainers about design before implementing a public API change. + +## Static/backend candidate needing hardware verification + +### AUDIT-004 — CUDA `binarize` appears to ignore requested element type + +The CUDA extension allocates `bin_vals = similar(nonzeros(Mat))`, which keeps the input nonzero element type, then fills it with `one(T)`. For Float32 input and `T=Bool`, the values remain Float32, unlike the generic implementation in `GNNGraphs/src/utils.jl:298`, which uses `ifelse` with `one(T)` and `zero(T)`. Since degree on adjacency matrices calls `binarize(A,T)` when `edge_weight=false`, an integer degree request may also retain an unintended float element type on CUDA. This is static evidence only: no CUDA device/backend was available, so keep it **Potential** until an accelerator reproduction confirms the returned `eltype` and downstream degree behavior. + +## Full-repository coverage and limits + +The prior `CONTRIBUTOR_RESEARCH_REPORT.md` contains the package architecture, file/folder map, function-flow overview, CI/test conventions, and prior CPU suite results. This pass used that map and directly re-read cited source/test files. It did not enumerate every one of the 204 repository files again, inspect every GitHub comment/review, or fetch upstream. The local `git log` and prior report are useful history, but no current upstream pull was performed. GitHub pages previously inspected showed 54–55 open issues and 23 open PRs depending on the live snapshot; do not use those changing counts as an exhaustive history. No dedicated security finding was established; no security issue is invented to fill a category. + +Existing upstream overlap checked in the prior pass: #710 (Reactant GCN), #671 (Zygote `add_edges`), #637/#638 (heterogeneous GAT empty edges), #600 (SGConv runtime), #669 (link-prediction docs), and #501 (Lux TransformerConv WIP). For every item with an upstream link, re-check its live status and latest commits before opening a new issue. The performance and feature items without links need maintainer interest checked before implementation. + +## First five PRs to consider + +1. **AUDIT-001** — clear constructor failure, minimal test and fix. +2. **AUDIT-002** — small sampling boundary correction with an exact failing reproduction. +3. **AUDIT-003** — empty-input behavior, after confirming the expected return contract. +4. **AUDIT-015** — Lux attention edge-feature tests, a contained test-only contribution. +5. **AUDIT-019** — temporal snapshot graph-type coverage, a contained test-only contribution. + +Do not start fixes for the remaining items from this audit list without verifying the relevant upstream state and, for bugs, running a focused reproduction. No fix implementation is included here. diff --git a/scripts/julia.ps1 b/scripts/julia.ps1 new file mode 100644 index 000000000..88b28be1a --- /dev/null +++ b/scripts/julia.ps1 @@ -0,0 +1,10 @@ +$ErrorActionPreference = 'Stop' +$repoRoot = Split-Path -Parent $PSScriptRoot +$julia = Join-Path $repoRoot '.tools\julia-1.13.3\bin\julia.exe' +if (-not (Test-Path -LiteralPath $julia)) { + throw "Repo-local Julia was not found at '$julia'. Run .\scripts\setup-dev.ps1 first." +} + +$env:JULIA_DEPOT_PATH = Join-Path $repoRoot '.julia-depot' +& $julia @args +exit $LASTEXITCODE diff --git a/scripts/setup-dev.ps1 b/scripts/setup-dev.ps1 new file mode 100644 index 000000000..0c778f654 --- /dev/null +++ b/scripts/setup-dev.ps1 @@ -0,0 +1,52 @@ +$ErrorActionPreference = 'Stop' +$repoRoot = Split-Path -Parent $PSScriptRoot +$toolsDir = Join-Path $repoRoot '.tools' +$juliaDir = Join-Path $toolsDir 'julia-1.13.3' +$julia = Join-Path $juliaDir 'bin\julia.exe' +$archive = Join-Path $toolsDir 'julia-1.13.3-win64.zip' +$expectedSha256 = '1fae8fc95e6199b3d8f6d7a3791810e45e1b3c480eb4b9f5bb211789badfc729' + +if (-not (Test-Path -LiteralPath $julia)) { + New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null + if (-not (Test-Path -LiteralPath $archive)) { + Write-Host 'Downloading the official Julia 1.13.3 portable archive' + Invoke-WebRequest -Uri 'https://julialang-s3.julialang.org/bin/winnt/x64/1.13/julia-1.13.3-win64.zip' -OutFile $archive + } + $actualSha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $archive).Hash.ToLowerInvariant() + if ($actualSha256 -ne $expectedSha256) { + throw "Julia archive SHA-256 mismatch: expected $expectedSha256, got $actualSha256. Remove '$archive' and retry." + } + Write-Host 'Extracting Julia into the ignored .tools folder' + Expand-Archive -LiteralPath $archive -DestinationPath $toolsDir -Force +} +if (-not (Test-Path -LiteralPath $julia)) { + throw "Julia executable not found at '$julia' after archive extraction." +} + +$env:JULIA_DEPOT_PATH = Join-Path $repoRoot '.julia-depot' +$environments = @( + '.', + 'GNNGraphs', 'GNNGraphs/test', + 'GNNlib', 'GNNlib/test', + 'GraphNeuralNetworks', 'GraphNeuralNetworks/test', + 'GNNLux', 'GNNLux/test', + 'docs' +) + +foreach ($environment in $environments) { + $project = Join-Path $repoRoot $environment + Write-Host "Instantiating Julia environment: $environment" + & $julia "--project=$project" -e 'using Pkg; Pkg.instantiate()' + if ($LASTEXITCODE -ne 0) { + throw "Pkg.instantiate failed for '$environment' (exit $LASTEXITCODE)." + } +} + +$toolingProject = Join-Path $repoRoot '.tools\devtools' +Write-Host 'Installing JuliaFormatter into the repo-local tooling environment' +& $julia "--project=$toolingProject" -e 'using Pkg; Pkg.add("JuliaFormatter")' +if ($LASTEXITCODE -ne 0) { + throw "Installing JuliaFormatter failed (exit $LASTEXITCODE)." +} + +Write-Host "Development environments are installed in $($env:JULIA_DEPOT_PATH)."