(self, graph: GraphComponentToTensor)
| 134 | # batch_size = edge_feats_old.shape[0] |
| 135 | |
| 136 | def forward(self, graph: GraphComponentToTensor) -> GraphComponentToTensor: |
| 137 | if self.use_edge_features: |
| 138 | edge_feats_old = graph[EDGES] # edge_feats_old have shape (b, #edges, #edge-feats) |
| 139 | if edge_feats_old.shape[gn_constants.SPATIAL_DIM] != self._n_edges: |
| 140 | raise ValueError(f"Edge features imply {edge_feats_old.shape[gn_constants.SPATIAL_DIM]} edges, " |
| 141 | f"while sender and receiver lists imply {self._n_edges} edges.") |
| 142 | node_feats_old = graph[NODES] # node_feats_old have shape (b, #nodes, #node-feats) |
| 143 | n_nodes = node_feats_old.shape[gn_constants.SPATIAL_DIM] |
| 144 | if self.use_global_features: |
| 145 | global_feats_old = graph[GLOBALS] # global_feats_old have shape (b, #global-feats) |
| 146 | batch_size, n_glob_feats = global_feats_old.shape |
| 147 | |
| 148 | out = {c: None for c in self.components} |
| 149 | |
| 150 | # ----------------------- Update edges |
| 151 | # self.senders and self.receivers are a sequence of indices with #edges elements |
| 152 | sender_feats = node_feats_old.index_select(index=self._senders, dim=gn_constants.SPATIAL_DIM) |
| 153 | receiver_feats = node_feats_old.index_select(index=self._receivers, dim=gn_constants.SPATIAL_DIM) |
| 154 | |
| 155 | # print('E:', edge_feats_old.shape, 'V:', node_feats_old.shape, 'U:', global_feats_old.shape) |
| 156 | # print('Senders', sender_feats.shape, 'Recvs', receiver_feats.shape, global_feats_unsqueezed.shape) |
| 157 | mlp_input_e = torch.cat([ |
| 158 | edge_feats_old, # (b, #edges, #edge-feats) |
| 159 | sender_feats, # (b, #edges, #node-feats) |
| 160 | receiver_feats, # (b, #edges, #node-feats) |
| 161 | repeat(global_feats_old, 'b g -> b e g', e=self._n_edges) # (1, self.n_edges, 1) (b, 1, #global-feats) |
| 162 | ], dim=-1) |
| 163 | |
| 164 | mlp_input_e = rearrange(mlp_input_e, 'b e d1 -> (b e) d1') |
| 165 | |
| 166 | out[EDGES] = self.update_funcs[EDGES](mlp_input_e) |
| 167 | out[EDGES] = rearrange(out[EDGES], '(b e) d2 -> b e d2', b=batch_size, e=self._n_edges) |
| 168 | if self.residual[EDGES]: |
| 169 | out[EDGES] += edge_feats_old |
| 170 | # ----------------------- Update nodes |
| 171 | aggregated_edge_feats_for_node = self.aggregator_funcs[AggregationTypes.AGG_E_TO_N]( |
| 172 | out[EDGES], index=self._receivers |
| 173 | ) |
| 174 | |
| 175 | mlp_input_n = torch.cat([ |
| 176 | aggregated_edge_feats_for_node, # (b, #nodes, #edge-feats) |
| 177 | node_feats_old, # (b, #nodes, #node-feats) |
| 178 | repeat(global_feats_old, 'b g -> b n g', n=n_nodes) # (b, #nodes, #global-feats) |
| 179 | ], dim=-1) |
| 180 | mlp_input_n = rearrange(mlp_input_n, 'b n d1 -> (b n) d1') |
| 181 | |
| 182 | # print('Agg E:', aggregated_edge_feats_for_node.shape, 'V:', node_feats_old.shape, mlp_input_n.shape) |
| 183 | out[NODES] = self.update_funcs[NODES](mlp_input_n) |
| 184 | out[NODES] = rearrange(out[NODES], '(b n) d2 -> b n d2', b=batch_size, n=n_nodes) |
| 185 | if self.residual[NODES]: |
| 186 | out[NODES] += node_feats_old |
| 187 | # ----------------------- Update global features |
| 188 | aggregated_edge_feats_for_global = self.aggregator_funcs[AggregationTypes.AGG_E_TO_U](out[EDGES]) |
| 189 | aggregated_node_feats_for_global = self.aggregator_funcs[AggregationTypes.AGG_N_TO_U](out[NODES]) |
| 190 | # print('Agg EU:', aggregated_edge_feats_for_global.shape, 'VU:', aggregated_node_feats_for_global.shape) |
| 191 | |
| 192 | mlp_input_u = torch.cat([ |
| 193 | aggregated_edge_feats_for_global, # (b, #edge-feats) |
nothing calls this directly
no outgoing calls
no test coverage detected