Graph convolution / message passing with Geometric Vector Perceptrons. Takes in a graph with node and edge embeddings, and returns new node embeddings. This does NOT do residual updates and pointwise feedforward layers ---see `GVPConvLayer`. :param in_dims: input n
| 224 | |
| 225 | |
| 226 | class GVPConv(MessagePassing): |
| 227 | ''' |
| 228 | Graph convolution / message passing with Geometric Vector Perceptrons. |
| 229 | Takes in a graph with node and edge embeddings, |
| 230 | and returns new node embeddings. |
| 231 | |
| 232 | This does NOT do residual updates and pointwise feedforward layers |
| 233 | ---see `GVPConvLayer`. |
| 234 | |
| 235 | :param in_dims: input node embedding dimensions (n_scalar, n_vector) |
| 236 | :param out_dims: output node embedding dimensions (n_scalar, n_vector) |
| 237 | :param edge_dims: input edge embedding dimensions (n_scalar, n_vector) |
| 238 | :param n_layers: number of GVPs in the message function |
| 239 | :param module_list: preconstructed message function, overrides n_layers |
| 240 | :param aggr: should be "add" if some incoming edges are masked, as in |
| 241 | a masked autoregressive decoder architecture, otherwise "mean" |
| 242 | :param activations: tuple of functions (scalar_act, vector_act) to use in GVPs |
| 243 | :param vector_gate: whether to use vector gating. |
| 244 | (vector_act will be used as sigma^+ in vector gating if `True`) |
| 245 | ''' |
| 246 | def __init__(self, in_dims, out_dims, edge_dims, |
| 247 | n_layers=3, module_list=None, aggr="mean", |
| 248 | activations=(F.relu, torch.sigmoid), vector_gate=False): |
| 249 | super(GVPConv, self).__init__(aggr=aggr) |
| 250 | self.si, self.vi = in_dims |
| 251 | self.so, self.vo = out_dims |
| 252 | self.se, self.ve = edge_dims |
| 253 | |
| 254 | GVP_ = functools.partial(GVP, |
| 255 | activations=activations, vector_gate=vector_gate) |
| 256 | |
| 257 | module_list = module_list or [] |
| 258 | if not module_list: |
| 259 | if n_layers == 1: |
| 260 | module_list.append( |
| 261 | GVP_((2*self.si + self.se, 2*self.vi + self.ve), |
| 262 | (self.so, self.vo), activations=(None, None))) |
| 263 | else: |
| 264 | module_list.append( |
| 265 | GVP_((2*self.si + self.se, 2*self.vi + self.ve), out_dims) |
| 266 | ) |
| 267 | for i in range(n_layers - 2): |
| 268 | module_list.append(GVP_(out_dims, out_dims)) |
| 269 | module_list.append(GVP_(out_dims, out_dims, |
| 270 | activations=(None, None))) |
| 271 | self.message_func = nn.Sequential(*module_list) |
| 272 | |
| 273 | def forward(self, x, edge_index, edge_attr): |
| 274 | ''' |
| 275 | :param x: tuple (s, V) of `torch.Tensor` |
| 276 | :param edge_index: array of shape [2, n_edges] |
| 277 | :param edge_attr: tuple (s, V) of `torch.Tensor` |
| 278 | ''' |
| 279 | x_s, x_v = x |
| 280 | message = self.propagate(edge_index, |
| 281 | s=x_s, v=x_v.reshape(x_v.shape[0], 3*x_v.shape[1]), |
| 282 | edge_attr=edge_attr) |
| 283 | return _split(message, self.vo) |