Skip to content

Commit c860586

Browse files
authored
Add Mooncake rule for propagate e_mul_xj fast path (#678)
1 parent fc579f4 commit c860586

2 files changed

Lines changed: 55 additions & 7 deletions

File tree

GNNlib/ext/GNNlibMooncakeExt.jl

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
module GNNlibMooncakeExt
22

3-
using GNNlib: GNNlib, propagate, copy_xj
4-
using GNNGraphs: GNNGraph, adjacency_matrix
3+
using GNNlib: GNNlib, propagate, copy_xj, e_mul_xj
4+
using GNNGraphs: GNNGraph, adjacency_matrix, edge_index, set_edge_weight
55
using LinearAlgebra: adjoint
66
using Base: IEEEFloat
77
import Mooncake
88
using Mooncake: CoDual, DefaultCtx, NoRData, @is_primitive
99

10-
# Mooncake reverse rule for the sparse message-passing fast path
11-
# propagate(copy_xj, g, +, xi, xj, e) == xj * adjacency_matrix(g)
12-
# `A` is constant w.r.t. the inputs, so the pullback is just `dxj = dy * A'`.
13-
# Without it Mooncake differentiates the generic sparse matmul, which is far
14-
# slower than Zygote.
10+
# Reverse rule for the fast path `propagate(copy_xj, g, +, xj) == xj * A`.
11+
# `A` is constant w.r.t. the inputs, so `dxj = dy * A'`.
1512

1613
@is_primitive DefaultCtx Tuple{
1714
typeof(propagate),
@@ -47,4 +44,45 @@ function Mooncake.rrule!!(
4744
return res, propagate_copy_xj_add_pullback!!
4845
end
4946

47+
# Reverse rule for the weighted fast path `propagate(e_mul_xj, g, +, xj, e) == xj * A(e)`.
48+
# `e` enters `A`, so we also return `de_k = Σ_f xj[f, s_k] * dy[f, t_k]`.
49+
50+
@is_primitive DefaultCtx Tuple{
51+
typeof(propagate),
52+
typeof(e_mul_xj),
53+
GNNGraph,
54+
typeof(+),
55+
Nothing,
56+
AbstractMatrix{P},
57+
AbstractVector{P},
58+
} where {P <: IEEEFloat}
59+
60+
function Mooncake.rrule!!(
61+
::CoDual{typeof(propagate)},
62+
::CoDual{typeof(e_mul_xj)},
63+
g::CoDual{<:GNNGraph},
64+
::CoDual{typeof(+)},
65+
::CoDual{Nothing},
66+
xj::CoDual{<:AbstractMatrix{P}},
67+
e::CoDual{<:AbstractVector{P}},
68+
) where {P <: IEEEFloat}
69+
pg = Mooncake.primal(g)
70+
pxj = Mooncake.primal(xj)
71+
pe = Mooncake.primal(e)
72+
s, t = edge_index(pg)
73+
A = adjacency_matrix(set_edge_weight(pg, pe), P; weighted = true)
74+
y = pxj * A
75+
res = Mooncake.zero_fcodual(y)
76+
function propagate_e_mul_xj_add_pullback!!(::NoRData)
77+
dy = Mooncake.tangent(res)
78+
dxj = Mooncake.tangent(xj)
79+
de = Mooncake.tangent(e)
80+
dxj .+= dy * adjoint(A)
81+
de .+= vec(sum(view(pxj, :, s) .* view(dy, :, t); dims = 1))
82+
return NoRData(), NoRData(), Mooncake.zero_rdata(pg),
83+
NoRData(), NoRData(), NoRData(), NoRData()
84+
end
85+
return res, propagate_e_mul_xj_add_pullback!!
86+
end
87+
5088
end # module

GNNlib/test/msgpass.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ end
151151
end
152152
end
153153

154+
@testset "e_mul_xj + (scalar edge weight)" begin
155+
# Scalar per-edge weights hit the spmm fast path that GNNlibMooncakeExt
156+
# has a dedicated Mooncake rule for (matrix `e` above stays generic).
157+
for g in TEST_GRAPHS
158+
e = rand(Float32, g.num_edges)
159+
f(g, x, e) = propagate(e_mul_xj, g, +; xj = x, e)
160+
test_gradients(f, g, g.x, e; test_grad_f=false, test_mooncake=TEST_MOONCAKE)
161+
end
162+
end
163+
154164
@testset "w_mul_xj +" begin
155165
for g in TEST_GRAPHS
156166
w = rand(Float32, g.num_edges)

0 commit comments

Comments
 (0)