Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions GNNLux/test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[sources]
GNNGraphs = {path = "../../GNNGraphs"}
GNNLux = {path = ".."}
GNNlib = {path = "../../GNNlib"}

[compat]
NNlib = "0.9.38"
Expand Down
50 changes: 21 additions & 29 deletions GNNlib/src/layers/temporalconv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ and the Lux (`GNNLux`) frontends build such a carrier and call into these
functions, so the recurrence math lives in a single place.
=#

"""
tgcn(l, cz, cr, ch, h)

GRU gating of the T-GCN cell. `cz`, `cr`, `ch` are the spatial-convolution
outputs of the three gates (computed by the frontend), `h` is the current
hidden state, and `l` carries the three gate `Dense` sub-modules
(`l.dense_z`, `l.dense_r`, `l.dense_h`, each exposing `weight`, `bias`, `σ`).
Returns the updated hidden state.
"""
# tgcn(l, cz, cr, ch, h)
#
# GRU gating of the T-GCN cell. `cz`, `cr`, `ch` are the spatial-convolution
# outputs of the three gates (computed by the frontend), `h` is the current
# hidden state, and `l` carries the three gate `Dense` sub-modules
# (`l.dense_z`, `l.dense_r`, `l.dense_h`, each exposing `weight`, `bias`, `σ`).
# Returns the updated hidden state.
function tgcn(l, cz, cr, ch, h)
z = l.dense_z.σ.(l.dense_z.weight * vcat(cz, h) .+ l.dense_z.bias)
r = l.dense_r.σ.(l.dense_r.weight * vcat(cr, h) .+ l.dense_r.bias)
Expand All @@ -25,13 +23,11 @@ function tgcn(l, cz, cr, ch, h)
return h
end

"""
gconv_gru(l, g, x, h)

Forward pass of the GConvGRU cell. `l` carries the six `ChebConv` sub-modules
(`conv_x_r`, `conv_h_r`, `conv_x_z`, `conv_h_z`, `conv_x_h`, `conv_h_h`).
Returns the updated hidden state.
"""
# gconv_gru(l, g, x, h)
#
# Forward pass of the GConvGRU cell. `l` carries the six `ChebConv` sub-modules
# (`conv_x_r`, `conv_h_r`, `conv_x_z`, `conv_h_z`, `conv_x_h`, `conv_h_h`).
# Returns the updated hidden state.
function gconv_gru(l, g::GNNGraph, x, h)
r = NNlib.sigmoid_fast.(cheb_conv(l.conv_x_r, g, x) .+ cheb_conv(l.conv_h_r, g, h))
z = NNlib.sigmoid_fast.(cheb_conv(l.conv_x_z, g, x) .+ cheb_conv(l.conv_h_z, g, h))
Expand All @@ -40,13 +36,11 @@ function gconv_gru(l, g::GNNGraph, x, h)
return h
end

"""
gconv_lstm(l, g, x, h, c)

Forward pass of the GConvLSTM cell. `l` carries the eight `ChebConv` sub-modules
and the four peephole scalings/biases (`w_i`, `b_i`, `w_f`, `b_f`, `w_c`, `b_c`,
`w_o`, `b_o`). Returns the updated `(h, c)` state.
"""
# gconv_lstm(l, g, x, h, c)
#
# Forward pass of the GConvLSTM cell. `l` carries the eight `ChebConv` sub-modules
# and the four peephole scalings/biases (`w_i`, `b_i`, `w_f`, `b_f`, `w_c`, `b_c`,
# `w_o`, `b_o`). Returns the updated `(h, c)` state.
function gconv_lstm(l, g::GNNGraph, x, h, c)
# input gate
i = cheb_conv(l.conv_x_i, g, x) .+ cheb_conv(l.conv_h_i, g, h) .+ l.w_i .* c .+ l.b_i
Expand All @@ -63,12 +57,10 @@ function gconv_lstm(l, g::GNNGraph, x, h, c)
return h, c
end

"""
dcgru(l, g, x, h)

Forward pass of the DCGRU cell. `l` carries the three `DConv` sub-modules
(`dconv_u`, `dconv_r`, `dconv_c`). Returns the updated hidden state.
"""
# dcgru(l, g, x, h)
#
# Forward pass of the DCGRU cell. `l` carries the three `DConv` sub-modules
# (`dconv_u`, `dconv_r`, `dconv_c`). Returns the updated hidden state.
function dcgru(l, g::GNNGraph, x, h)
h̃ = vcat(x, h)
z = NNlib.sigmoid_fast.(d_conv(l.dconv_u, g, h̃))
Expand Down
1 change: 0 additions & 1 deletion GNNlib/test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[sources]
GNNGraphs = {path = "../../GNNGraphs"}
GNNlib = {path = ".."}

[compat]
Expand Down
2 changes: 0 additions & 2 deletions GraphNeuralNetworks/test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,4 @@ TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[sources]
GNNGraphs = {path = "../../GNNGraphs"}
GNNlib = {path = "../../GNNlib"}
GraphNeuralNetworks = {path = ".."}
36 changes: 24 additions & 12 deletions GraphNeuralNetworks/test/layers/temporalconv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,24 @@ end

@testitem "TGCNCell" setup=[TemporalConvTestModule, TestModule] begin
using .TemporalConvTestModule, .TestModule


# Mooncake is skipped here: it returns wrong gradients through the cell's
# sigmoid gates (`Dense(_, sigmoid)`) on a fraction of inputs, so the check is
# flaky (Zygote and finite differences agree). Upstream Mooncake bug with
# NNlib.sigmoid: https://github.com/chalk-lab/Mooncake.jl/issues/1257.
# Reference against Zygote only, matching the other temporal cells.
ad_backends = [Flux.AutoZygote()]

# Test with default activation function
cell = GraphNeuralNetworks.TGCNCell(in_channel => out_channel)
y, h = cell(g, g.x)
@test y === h
@test size(h) == (out_channel, g.num_nodes)
# with no initial state
test_gradients(cell, g, g.x, loss=cell_loss, rtol=RTOL_HIGH)
test_gradients(cell, g, g.x, loss=cell_loss, rtol=RTOL_HIGH; ad_backends)
# with initial state
test_gradients(cell, g, g.x, h, loss=cell_loss, rtol=RTOL_HIGH)
test_gradients(cell, g, g.x, h, loss=cell_loss, rtol=RTOL_HIGH; ad_backends)

# Test with custom activation function
custom_activation = tanh
cell_custom = GraphNeuralNetworks.TGCNCell(in_channel => out_channel, act = custom_activation)
Expand All @@ -45,14 +52,19 @@ end
# Test that outputs differ when using different activation functions
@test !isapprox(y, y_custom, rtol=RTOL_HIGH)
# with no initial state
test_gradients(cell_custom, g, g.x, loss=cell_loss, rtol=RTOL_HIGH)
test_gradients(cell_custom, g, g.x, loss=cell_loss, rtol=RTOL_HIGH; ad_backends)
# with initial state
test_gradients(cell_custom, g, g.x, h_custom, loss=cell_loss, rtol=RTOL_HIGH)
test_gradients(cell_custom, g, g.x, h_custom, loss=cell_loss, rtol=RTOL_HIGH; ad_backends)
end

@testitem "TGCN" setup=[TemporalConvTestModule, TestModule] begin
using .TemporalConvTestModule, .TestModule


# Mooncake is skipped here (see the TGCNCell test item and
# https://github.com/chalk-lab/Mooncake.jl/issues/1257): it returns wrong
# gradients through the cell's sigmoid gates on some inputs. Zygote only.
ad_backends = [Flux.AutoZygote()]

# Test with default activation function
layer = TGCN(in_channel => out_channel)
x = rand(Float32, in_channel, timesteps, g.num_nodes)
Expand All @@ -61,9 +73,9 @@ end
@test layer isa GNNRecurrence
@test size(y) == (out_channel, timesteps, g.num_nodes)
# with no initial state
test_gradients(layer, g, x, rtol = RTOL_HIGH)
test_gradients(layer, g, x, rtol = RTOL_HIGH; ad_backends)
# with initial state
test_gradients(layer, g, x, state0, rtol = RTOL_HIGH)
test_gradients(layer, g, x, state0, rtol = RTOL_HIGH; ad_backends)

# Test with custom activation function
custom_activation = tanh
Expand All @@ -74,15 +86,15 @@ end
# Test that outputs differ when using different activation functions
@test !isapprox(y, y_custom, rtol = RTOL_HIGH)
# with no initial state
test_gradients(layer_custom, g, x, rtol = RTOL_HIGH)
test_gradients(layer_custom, g, x, rtol = RTOL_HIGH; ad_backends)
# with initial state
test_gradients(layer_custom, g, x, state0, rtol = RTOL_HIGH)
test_gradients(layer_custom, g, x, state0, rtol = RTOL_HIGH; ad_backends)

# interplay with GNNChain
model = GNNChain(TGCN(in_channel => out_channel), Dense(out_channel, 1))
y = model(g, x)
@test size(y) == (1, timesteps, g.num_nodes)
test_gradients(model, g, x, rtol = RTOL_HIGH, atol = ATOL_LOW)
test_gradients(model, g, x, rtol = RTOL_HIGH, atol = ATOL_LOW; ad_backends)
end

@testitem "GConvLSTMCell" setup=[TemporalConvTestModule, TestModule] begin
Expand Down