Full graph convolution / message passing layer with Geometric Vector Perceptrons. Residually updates node embeddings with aggregated incoming messages, applies a pointwise feedforward network to node embeddings, and returns updated node embeddings. To only compute the agg
| 291 | |
| 292 | |
| 293 | class GVPConvLayer(nn.Module): |
| 294 | ''' |
| 295 | Full graph convolution / message passing layer with |
| 296 | Geometric Vector Perceptrons. Residually updates node embeddings with |
| 297 | aggregated incoming messages, applies a pointwise feedforward |
| 298 | network to node embeddings, and returns updated node embeddings. |
| 299 | |
| 300 | To only compute the aggregated messages, see `GVPConv`. |
| 301 | |
| 302 | :param node_dims: node embedding dimensions (n_scalar, n_vector) |
| 303 | :param edge_dims: input edge embedding dimensions (n_scalar, n_vector) |
| 304 | :param n_message: number of GVPs to use in message function |
| 305 | :param n_feedforward: number of GVPs to use in feedforward function |
| 306 | :param drop_rate: drop probability in all dropout layers |
| 307 | :param autoregressive: if `True`, this `GVPConvLayer` will be used |
| 308 | with a different set of input node embeddings for messages |
| 309 | where src >= dst |
| 310 | :param activations: tuple of functions (scalar_act, vector_act) to use in GVPs |
| 311 | :param vector_gate: whether to use vector gating. |
| 312 | (vector_act will be used as sigma^+ in vector gating if `True`) |
| 313 | ''' |
| 314 | def __init__(self, node_dims, edge_dims, |
| 315 | n_message=3, n_feedforward=2, drop_rate=.1, |
| 316 | autoregressive=False, |
| 317 | activations=(F.relu, torch.sigmoid), vector_gate=False): |
| 318 | |
| 319 | super(GVPConvLayer, self).__init__() |
| 320 | self.conv = GVPConv(node_dims, node_dims, edge_dims, n_message, |
| 321 | aggr="add" if autoregressive else "mean", |
| 322 | activations=activations, vector_gate=vector_gate) |
| 323 | GVP_ = functools.partial(GVP, |
| 324 | activations=activations, vector_gate=vector_gate) |
| 325 | self.norm = nn.ModuleList([GVPLayerNorm(node_dims) for _ in range(2)]) |
| 326 | self.dropout = nn.ModuleList([Dropout(drop_rate) for _ in range(2)]) |
| 327 | |
| 328 | ff_func = [] |
| 329 | if n_feedforward == 1: |
| 330 | ff_func.append(GVP_(node_dims, node_dims, activations=(None, None))) |
| 331 | else: |
| 332 | hid_dims = 4*node_dims[0], 2*node_dims[1] |
| 333 | ff_func.append(GVP_(node_dims, hid_dims)) |
| 334 | for i in range(n_feedforward-2): |
| 335 | ff_func.append(GVP_(hid_dims, hid_dims)) |
| 336 | ff_func.append(GVP_(hid_dims, node_dims, activations=(None, None))) |
| 337 | self.ff_func = nn.Sequential(*ff_func) |
| 338 | |
| 339 | def forward(self, x, edge_index, edge_attr, |
| 340 | autoregressive_x=None, node_mask=None): |
| 341 | ''' |
| 342 | :param x: tuple (s, V) of `torch.Tensor` |
| 343 | :param edge_index: array of shape [2, n_edges] |
| 344 | :param edge_attr: tuple (s, V) of `torch.Tensor` |
| 345 | :param autoregressive_x: tuple (s, V) of `torch.Tensor`. |
| 346 | If not `None`, will be used as src node embeddings |
| 347 | for forming messages where src >= dst. The corrent node |
| 348 | embeddings `x` will still be the base of the update and the |
| 349 | pointwise feedforward. |
| 350 | :param node_mask: array of type `bool` to index into the first |
nothing calls this directly
no outgoing calls
no test coverage detected