def __init__(self,
                 d_in,
                 d_degree,
                 degree_as_tag,
                 retain_features,
                 d_msg,
                 d_up,
                 d_h,
                 seed,
                 activation_name,
                 bn,
                 aggr='add',
                 msg_kind='general',
                 eps=0,
                 train_eps=False,
                 flow='source_to_target',
                 **kwargs):

        super(MPNN_sparse, self).__init__()

        d_msg = d_in if d_msg is None else d_msg

        self.flow = flow
        self.aggr = aggr
        self.msg_kind = msg_kind

        self.degree_as_tag = degree_as_tag
        self.retain_features = retain_features

        if degree_as_tag:
            d_in = d_in + d_degree if retain_features else d_degree

        if msg_kind == 'gin':
            msg_input_dim = None
            self.initial_eps = eps
            if train_eps:
                self.eps = torch.nn.Parameter(torch.Tensor([eps]))
            else:
                self.register_buffer('eps', torch.Tensor([eps]))
            self.eps.data.fill_(self.initial_eps)
            self.msg_fn = None
            update_input_dim = d_in

        elif msg_kind == 'general':
            msg_input_dim = 2 * d_in
            self.msg_fn = mlp(msg_input_dim, d_msg, d_h, seed, activation_name,
                              bn)
            update_input_dim = d_in + d_msg

        self.update_fn = mlp(update_input_dim, d_up, d_h, seed,
                             activation_name, bn)

        return
Ejemplo n.º 2
0
    def __init__(self,
                 d_in,
                 d_ef,
                 d_id,
                 d_degree,
                 degree_as_tag,
                 retain_features,
                 id_scope,
                 d_msg,
                 d_up,
                 d_h,
                 seed,
                 activation_name,
                 bn,
                 aggr='add',
                 msg_kind='ogb',
                 eps=0,
                 train_eps=False,
                 flow='source_to_target',
                 **kwargs):

        super(GSN_edge_sparse_ogb, self).__init__()
        
        self.flow = flow
        self.aggr = aggr
        self.msg_kind = msg_kind
        self.id_scope = id_scope
        
        self.degree_as_tag = degree_as_tag
        self.retain_features = retain_features

        if degree_as_tag:
            d_in = d_in + d_degree if retain_features else d_degree

        # INPUT_DIMS
        if msg_kind == 'ogb':
            
            self.initial_eps = eps
            if train_eps:
                self.eps = torch.nn.Parameter(torch.Tensor([eps]))
            else:
                self.register_buffer('eps', torch.Tensor([eps]))
            self.eps.data.fill_(self.initial_eps)
            
            update_input_dim = d_in
            
        else:
            raise NotImplementedError('msg kind {} is not currently supported.'.format(msg_kind))
        self.update_fn = mlp(update_input_dim, d_up, d_h, seed, activation_name, bn)       

        return
    def __init__(self,
                 d_in,
                 d_ef,
                 d_id,
                 d_degree,
                 degree_as_tag,
                 retain_features,
                 id_scope,
                 d_msg,
                 d_up,
                 d_h,
                 seed,
                 activation_name,
                 bn,
                 aggr='add',
                 msg_kind='general',
                 eps=0,
                 train_eps=False,
                 flow='source_to_target',
                 **kwargs):

        super(GSN_edge_sparse, self).__init__()

        d_msg = d_in if d_msg is None else d_msg

        self.flow = flow
        self.aggr = aggr
        self.msg_kind = msg_kind
        self.id_scope = id_scope

        self.degree_as_tag = degree_as_tag
        self.retain_features = retain_features

        if degree_as_tag:
            d_in = d_in + d_degree if retain_features else d_degree

        # INPUT_DIMS
        if msg_kind == 'gin':

            # dummy variable for self loop edge features
            self.central_node_edge_encoder = central_encoder(
                kwargs['edge_embedding'], d_ef, extend=kwargs['extend_dims'])
            d_ef = self.central_node_edge_encoder.d_out

            if self.id_scope == 'local':
                # dummy variable for self loop edge counts
                self.central_node_id_encoder = central_encoder(
                    kwargs['id_embedding'], d_id, extend=kwargs['extend_dims'])
                d_id = self.central_node_id_encoder.d_out

            msg_input_dim = None
            self.initial_eps = eps
            if train_eps:
                self.eps = torch.nn.Parameter(torch.Tensor([eps]))
            else:
                self.register_buffer('eps', torch.Tensor([eps]))
            self.eps.data.fill_(self.initial_eps)

            self.msg_fn = None
            update_input_dim = d_in + d_id + d_ef

        elif msg_kind == 'general':
            msg_input_dim = 2 * d_in + d_id + d_ef if id_scope == 'local' else 2 * (
                d_in + d_id) + d_ef
            # MSG_FUN
            self.msg_fn = mlp(msg_input_dim, d_msg, d_h, seed, activation_name,
                              bn)
            update_input_dim = d_in + d_msg

        else:
            raise NotImplementedError(
                'msg kind {} is not currently supported.'.format(msg_kind))

        self.update_fn = mlp(update_input_dim, d_up, d_h, seed,
                             activation_name, bn)

        return
    def __init__(self,
                 in_features,
                 out_features,
                 encoder_ids,
                 d_in_id,
                 in_edge_features=None,
                 d_in_node_encoder=None,
                 d_in_edge_encoder=None,
                 encoder_degrees=None,
                 d_degree=None,
                 **kwargs):

        super(GNN_OGB, self).__init__()

        seed = kwargs['seed']

        #-------------- Initializations

        self.model_name = kwargs['model_name']
        self.readout = kwargs['readout'] if kwargs[
            'readout'] is not None else 'sum'
        self.dropout_features = kwargs['dropout_features']
        self.bn = kwargs['bn']
        self.final_projection = kwargs['final_projection']
        self.residual = kwargs['residual']
        self.inject_ids = kwargs['inject_ids']
        self.vn = kwargs['vn']

        id_scope = kwargs['id_scope']
        d_msg = kwargs['d_msg']
        d_out = kwargs['d_out']
        d_h = kwargs['d_h']
        aggr = kwargs['aggr'] if kwargs['aggr'] is not None else 'add'
        flow = kwargs['flow'] if kwargs[
            'flow'] is not None else 'target_to_source'
        msg_kind = kwargs['msg_kind'] if kwargs[
            'msg_kind'] is not None else 'general'
        train_eps = kwargs['train_eps'] if kwargs[
            'train_eps'] is not None else [False for _ in range(len(d_out))]
        activation_mlp = kwargs['activation_mlp']
        bn_mlp = kwargs['bn_mlp']
        jk_mlp = kwargs['jk_mlp']
        degree_embedding = kwargs['degree_embedding'] if kwargs[
            'degree_as_tag'][0] else 'None'
        degree_as_tag = kwargs['degree_as_tag']
        retain_features = kwargs['retain_features']

        encoders_kwargs = {
            'seed': seed,
            'activation_mlp': activation_mlp,
            'bn_mlp': bn_mlp,
            'aggr': kwargs['multi_embedding_aggr'],
            'features_scope': kwargs['features_scope']
        }

        #-------------- Input node embedding
        self.input_node_encoder = DiscreteEmbedding(
            kwargs['input_node_encoder'], in_features, d_in_node_encoder,
            kwargs['d_out_node_encoder'], **encoders_kwargs)
        d_in = self.input_node_encoder.d_out

        #-------------- Virtual node embedding
        if self.vn:
            vn_encoder_kwargs = copy.deepcopy(encoders_kwargs)
            vn_encoder_kwargs['init'] = 'zeros'
            self.vn_encoder = DiscreteEmbedding(kwargs['input_vn_encoder'], 1,
                                                [1],
                                                kwargs['d_out_vn_encoder'],
                                                **vn_encoder_kwargs)
            d_in_vn = self.vn_encoder.d_out

        #-------------- Edge embedding (for each GNN layer)
        self.edge_encoder = []
        d_ef = []
        for i in range(len(d_out)):
            edge_encoder_layer = DiscreteEmbedding(
                kwargs['edge_encoder'], in_edge_features, d_in_edge_encoder,
                kwargs['d_out_edge_encoder'][i], **encoders_kwargs)
            self.edge_encoder.append(edge_encoder_layer)
            d_ef.append(edge_encoder_layer.d_out)

        self.edge_encoder = nn.ModuleList(self.edge_encoder)

        # -------------- Identifier embedding (for each GNN layer)
        self.id_encoder = []
        d_id = []
        num_id_encoders = len(d_out) if kwargs['inject_ids'] else 1
        for i in range(num_id_encoders):
            id_encoder_layer = DiscreteEmbedding(kwargs['id_embedding'],
                                                 len(d_in_id), d_in_id,
                                                 kwargs['d_out_id_embedding'],
                                                 **encoders_kwargs)
            self.id_encoder.append(id_encoder_layer)
            d_id.append(id_encoder_layer.d_out)

        self.id_encoder = nn.ModuleList(self.id_encoder)

        #-------------- Degree embedding
        self.degree_encoder = DiscreteEmbedding(
            degree_embedding, 1, d_degree, kwargs['d_out_degree_embedding'],
            **encoders_kwargs)
        d_degree = self.degree_encoder.d_out

        #-------------- GNN layers w/ bn
        self.conv = []
        self.batch_norms = []
        self.mlp_vn = []
        for i in range(len(d_out)):

            if i > 0 and self.vn:
                #-------------- vn msg function
                mlp_vn_temp = mlp(d_in_vn, kwargs['d_out_vn'][i - 1], d_h[i],
                                  seed, activation_mlp, bn_mlp)
                self.mlp_vn.append(mlp_vn_temp)
                d_in_vn = kwargs['d_out_vn'][i - 1]

            kwargs_filter = {
                'd_in': d_in,
                'd_degree': d_degree,
                'degree_as_tag': degree_as_tag[i],
                'retain_features': retain_features[i],
                'd_msg': d_msg[i],
                'd_up': d_out[i],
                'd_h': d_h[i],
                'seed': seed,
                'activation_name': activation_mlp,
                'bn': bn_mlp,
                'aggr': aggr,
                'msg_kind': msg_kind,
                'eps': 0,
                'train_eps': train_eps[i],
                'flow': flow,
                'd_ef': d_ef[i],
                'edge_embedding': kwargs['edge_encoder'],
                'id_embedding': kwargs['id_embedding'],
                'extend_dims': kwargs['extend_dims']
            }

            use_ids = ((i > 0 and kwargs['inject_ids']) or
                       (i == 0)) and (self.model_name == 'GSN_edge_sparse_ogb')

            if use_ids:
                filter_fn = GSN_edge_sparse_ogb
                kwargs_filter['d_id'] = d_id[i] if self.inject_ids else d_id[0]
                kwargs_filter['id_scope'] = id_scope
            else:
                filter_fn = MPNN_edge_sparse_ogb
            self.conv.append(filter_fn(**kwargs_filter))

            bn_layer = nn.BatchNorm1d(d_out[i]) if self.bn[i] else None
            self.batch_norms.append(bn_layer)

            d_in = d_out[i]

        self.conv = nn.ModuleList(self.conv)
        self.batch_norms = nn.ModuleList(self.batch_norms)
        if kwargs['vn']:
            self.mlp_vn = nn.ModuleList(self.mlp_vn)

        #-------------- Readout
        if self.readout == 'sum':
            self.global_pool = global_add_pool_sparse
        elif self.readout == 'mean':
            self.global_pool = global_mean_pool_sparse
        else:
            raise ValueError("Invalid graph pooling type.")

        #-------------- Virtual node aggregation operator
        if self.vn:
            if kwargs['vn_pooling'] == 'sum':
                self.global_vn_pool = global_add_pool_sparse
            elif kwargs['vn_pooling'] == 'mean':
                self.global_vn_pool = global_mean_pool_sparse
            else:
                raise ValueError("Invalid graph virtual node pooling type.")

        self.lin_proj = nn.Linear(d_out[-1], out_features)

        #-------------- Activation fn (same across the network)

        self.activation = choose_activation(kwargs['activation'])

        return
    def __init__(self, encoder_name, d_in_features, d_in_encoder,
                 d_out_encoder, **kwargs):

        super(DiscreteEmbedding, self).__init__()

        #-------------- various different embedding layers
        kwargs['init'] = None if 'init' not in kwargs else kwargs['init']

        self.encoder_name = encoder_name
        # d_in_features: input feature size (e.g. if already one hot encoded),
        # d_in_encoder: number of unique values that will be encoded (size of embedding vocabulary)

        #-------------- fill embedding with zeros
        if encoder_name == 'zero_encoder':
            self.encoder = zero_encoder(d_out_encoder)
            d_out = d_out_encoder

        #-------------- linear pojection
        elif encoder_name == 'linear':
            self.encoder = nn.Linear(d_in_features, d_out_encoder, bias=True)
            d_out = d_out_encoder

        #-------------- mlp
        elif encoder_name == 'mlp':
            self.encoder = mlp(d_in_features, d_out_encoder, d_out_encoder,
                               kwargs['seed'], kwargs['activation_mlp'],
                               kwargs['bn_mlp'])
            d_out = d_out_encoder

        #-------------- multi hot encoding of categorical data
        elif encoder_name == 'one_hot_encoder':
            self.encoder = one_hot_encoder(d_in_encoder)
            d_out = sum(d_in_encoder)

        #-------------- embedding of categorical data (linear projection without bias of one hot encodings)
        elif encoder_name == 'embedding':
            self.encoder = multi_embedding(d_in_encoder, d_out_encoder,
                                           kwargs['aggr'], kwargs['init'])
            if kwargs['aggr'] == 'concat':
                d_out = len(d_in_encoder) * d_out_encoder
            else:
                d_out = d_out_encoder

        #-------------- for ogb: multi hot encoding of node features
        elif encoder_name == 'atom_one_hot_encoder':
            full_atom_feature_dims = get_atom_feature_dims(
            ) if kwargs['features_scope'] == 'full' else get_atom_feature_dims(
            )[:2]
            self.encoder = one_hot_encoder(full_atom_feature_dims)
            d_out = sum(full_atom_feature_dims)

        #-------------- for ogb: multi hot encoding of edge features
        elif encoder_name == 'bond_one_hot_encoder':
            full_bond_feature_dims = get_bond_feature_dims(
            ) if kwargs['features_scope'] == 'full' else get_bond_feature_dims(
            )[:2]
            self.encoder = one_hot_encoder(full_bond_feature_dims)
            d_out = sum(full_bond_feature_dims)

        #-------------- for ogb: embedding of node features
        elif encoder_name == 'atom_encoder':
            self.encoder = AtomEncoder(d_out_encoder)
            d_out = d_out_encoder

        #-------------- for ogb: embedding of edge features
        elif encoder_name == 'bond_encoder':
            self.encoder = BondEncoder(emb_dim=d_out_encoder)
            d_out = d_out_encoder

        #-------------- no embedding, use as is
        elif encoder_name == 'None':
            self.encoder = None
            d_out = d_in_features

        else:
            raise NotImplementedError(
                'Encoder {} is not currently supported.'.format(encoder_name))

        self.d_out = d_out

        return
Ejemplo n.º 6
0
    def __init__(self,
                 in_features,
                 out_features,
                 encoder_ids,
                 d_in_id,
                 in_edge_features=None,
                 d_in_node_encoder=None,
                 d_in_edge_encoder=None,
                 encoder_degrees=None,
                 d_degree=None,
                 **kwargs):

        super(MLPSubstructures, self).__init__()

        seed = kwargs['seed']

        #-------------- Initializations

        self.model_name = kwargs['model_name']
        self.readout = kwargs['readout'] if kwargs[
            'readout'] is not None else 'sum'
        self.dropout_features = kwargs['dropout_features']
        self.bn = kwargs['bn']
        self.degree_as_tag = kwargs['degree_as_tag']
        self.retain_features = kwargs['retain_features']
        self.id_scope = kwargs['id_scope']

        d_out = kwargs['d_out']
        d_h = kwargs['d_h']
        activation_mlp = kwargs['activation_mlp']
        bn_mlp = kwargs['bn_mlp']
        jk_mlp = kwargs['jk_mlp']
        degree_embedding = kwargs['degree_embedding'] if kwargs[
            'degree_as_tag'][0] else 'None'

        encoders_kwargs = {
            'seed': seed,
            'activation_mlp': activation_mlp,
            'bn_mlp': bn_mlp,
            'aggr': kwargs['multi_embedding_aggr']
        }

        #-------------- Input node embedding
        self.input_node_encoder = DiscreteEmbedding(
            kwargs['input_node_encoder'], in_features, d_in_node_encoder,
            kwargs['d_out_node_encoder'], **encoders_kwargs)
        d_in = self.input_node_encoder.d_out

        #-------------- Edge embedding (for each GNN layer)
        self.edge_encoder = DiscreteEmbedding(kwargs['edge_encoder'],
                                              in_edge_features,
                                              d_in_edge_encoder,
                                              kwargs['d_out_edge_encoder'][0],
                                              **encoders_kwargs)
        d_ef = self.edge_encoder.d_out

        #-------------- Identifier embedding (for each GNN layer)
        self.id_encoder = DiscreteEmbedding(kwargs['id_embedding'],
                                            len(d_in_id), d_in_id,
                                            kwargs['d_out_id_embedding'],
                                            **encoders_kwargs)
        d_id = self.id_encoder.d_out

        #-------------- Degree embedding
        self.degree_encoder = DiscreteEmbedding(
            degree_embedding, 1, d_degree, kwargs['d_out_degree_embedding'],
            **encoders_kwargs)
        d_degree = self.degree_encoder.d_out

        #-------------- edge-wise MLP w/ bn

        if self.degree_as_tag[0] and self.retain_features[0] == True:
            mlp_input_dim = 2 * (d_in + d_degree)
        elif self.degree_as_tag[0] and self.retain_features[0] == False:
            mlp_input_dim = 2 * d_degree
        else:
            mlp_input_dim = 2 * d_in

        if self.id_scope == 'global':
            mlp_input_dim += 2 * d_id
        else:
            mlp_input_dim += d_id

        if kwargs['edge_encoder'] != 'None':
            mlp_input_dim += d_ef

        filter_fn = mlp(mlp_input_dim, d_out[0], d_h[0], seed, activation_mlp,
                        bn_mlp)
        self.conv = filter_fn
        self.batch_norms = nn.BatchNorm1d(d_out[0]) if self.bn[0] else None

        if jk_mlp:
            final_jk_layer = mlp(d_out[0], out_features, d_h[0], seed,
                                 activation_mlp, bn_mlp)
        else:
            final_jk_layer = nn.Linear(d_out[0], out_features)

        self.lin_proj = final_jk_layer

        #-------------- Readout
        if self.readout == 'sum':
            self.global_pool = global_add_pool_sparse
        elif self.readout == 'mean':
            self.global_pool = global_mean_pool_sparse
        else:
            raise ValueError("Invalid graph pooling type.")

        #-------------- Activation fn (same across the network)
        self.activation = choose_activation(kwargs['activation'])

        return
    def __init__(self,
                 d_in,
                 d_ef,
                 d_degree,
                 degree_as_tag,
                 retain_features,
                 d_msg,
                 d_up,
                 d_h,
                 seed,
                 activation_name,
                 bn,
                 aggr='add',
                 msg_kind='general',
                 eps=0,
                 train_eps=False,
                 flow='source_to_target',
                 **kwargs):

        super(MPNN_edge_sparse, self).__init__()

        d_msg = d_in if d_msg is None else d_msg

        self.flow = flow
        self.aggr = aggr
        self.msg_kind = msg_kind

        self.degree_as_tag = degree_as_tag
        self.retain_features = retain_features

        if degree_as_tag:
            d_in = d_in + d_degree if retain_features else d_degree

        # INPUT_DIMS
        if msg_kind == 'gin':

            # dummy variable for self loop edge features
            self.central_node_edge_encoder = central_encoder(
                kwargs['edge_embedding'], d_ef, extend=kwargs['extend_dims'])
            d_ef = self.central_node_edge_encoder.d_out

            msg_input_dim = None
            self.initial_eps = eps
            if train_eps:
                self.eps = torch.nn.Parameter(torch.Tensor([eps]))
            else:
                self.register_buffer('eps', torch.Tensor([eps]))
            self.eps.data.fill_(self.initial_eps)
            self.msg_fn = None
            update_input_dim = d_in + d_ef

        elif msg_kind == 'general':
            msg_input_dim = 2 * d_in + d_ef
            self.msg_fn = mlp(msg_input_dim, d_msg, d_h, seed, activation_name,
                              bn)
            update_input_dim = d_in + d_msg

        self.update_fn = mlp(update_input_dim, d_up, d_h, seed,
                             activation_name, bn)

        return
Ejemplo n.º 8
0
    def __init__(self,
                 in_features,
                 out_features,
                 encoder_ids,
                 d_in_id,
                 in_edge_features=None,
                 d_in_node_encoder=None,
                 d_in_edge_encoder=None,
                 encoder_degrees=None,
                 d_degree=None,
                 **kwargs):

        super(GNNSubstructures, self).__init__()

        seed = kwargs['seed']

        #-------------- Initializations

        self.model_name = kwargs['model_name']
        self.readout = kwargs['readout'] if kwargs[
            'readout'] is not None else 'sum'
        self.dropout_features = kwargs['dropout_features']
        self.bn = kwargs['bn']
        self.final_projection = kwargs['final_projection']
        self.inject_ids = kwargs['inject_ids']
        self.inject_edge_features = kwargs['inject_edge_features']
        self.random_features = kwargs['random_features']

        id_scope = kwargs['id_scope']
        d_msg = kwargs['d_msg']
        d_out = kwargs['d_out']
        d_h = kwargs['d_h']
        aggr = kwargs['aggr'] if kwargs['aggr'] is not None else 'add'
        flow = kwargs['flow'] if kwargs[
            'flow'] is not None else 'target_to_source'
        msg_kind = kwargs['msg_kind'] if kwargs[
            'msg_kind'] is not None else 'general'
        train_eps = kwargs['train_eps'] if kwargs[
            'train_eps'] is not None else [False for _ in range(len(d_out))]
        activation_mlp = kwargs['activation_mlp']
        bn_mlp = kwargs['bn_mlp']
        jk_mlp = kwargs['jk_mlp']
        degree_embedding = kwargs['degree_embedding'] if kwargs[
            'degree_as_tag'][0] else 'None'
        degree_as_tag = kwargs['degree_as_tag']
        retain_features = kwargs['retain_features']

        encoders_kwargs = {
            'seed': seed,
            'activation_mlp': activation_mlp,
            'bn_mlp': bn_mlp,
            'aggr': kwargs['multi_embedding_aggr']
        }

        #-------------- Input node embedding
        self.input_node_encoder = DiscreteEmbedding(
            kwargs['input_node_encoder'], in_features, d_in_node_encoder,
            kwargs['d_out_node_encoder'], **encoders_kwargs)
        d_in = self.input_node_encoder.d_out
        if self.random_features:
            self.r_d_out = d_out[0]
            d_in = d_in + self.r_d_out

        #-------------- Edge embedding (for each GNN layer)
        self.edge_encoder = []
        d_ef = []
        num_edge_encoders = len(d_out) if kwargs['inject_edge_features'] else 1
        for i in range(num_edge_encoders):
            edge_encoder_layer = DiscreteEmbedding(
                kwargs['edge_encoder'], in_edge_features, d_in_edge_encoder,
                kwargs['d_out_edge_encoder'][i], **encoders_kwargs)
            self.edge_encoder.append(edge_encoder_layer)
            d_ef.append(edge_encoder_layer.d_out)

        self.edge_encoder = nn.ModuleList(self.edge_encoder)

        #-------------- Identifier embedding (for each GNN layer)
        self.id_encoder = []
        d_id = []
        num_id_encoders = len(d_out) if kwargs['inject_ids'] else 1
        for i in range(num_id_encoders):
            id_encoder_layer = DiscreteEmbedding(kwargs['id_embedding'],
                                                 len(d_in_id), d_in_id,
                                                 kwargs['d_out_id_embedding'],
                                                 **encoders_kwargs)
            self.id_encoder.append(id_encoder_layer)
            d_id.append(id_encoder_layer.d_out)

        self.id_encoder = nn.ModuleList(self.id_encoder)

        #-------------- Degree embedding
        self.degree_encoder = DiscreteEmbedding(
            degree_embedding, 1, d_degree, kwargs['d_out_degree_embedding'],
            **encoders_kwargs)
        d_degree = self.degree_encoder.d_out

        #-------------- GNN layers w/ bn and jk
        self.conv = []
        self.batch_norms = []
        self.lin_proj = []
        for i in range(len(d_out)):

            kwargs_filter = {
                'd_in': d_in,
                'd_degree': d_degree,
                'degree_as_tag': degree_as_tag[i],
                'retain_features': retain_features[i],
                'd_msg': d_msg[i],
                'd_up': d_out[i],
                'd_h': d_h[i],
                'd_ef': d_ef[i] if self.inject_edge_features else d_ef[0],
                'seed': seed,
                'activation_name': activation_mlp,
                'bn': bn_mlp,
                'aggr': aggr,
                'msg_kind': msg_kind,
                'eps': 0,
                'train_eps': train_eps[i],
                'flow': flow,
                'edge_embedding': kwargs['edge_encoder'],
                'id_embedding': kwargs['id_embedding'],
                'extend_dims': kwargs['extend_dims']
            }

            use_ids = ((i > 0 and kwargs['inject_ids']) or
                       (i == 0)) and (self.model_name
                                      in {'GSN_sparse', 'GSN_edge_sparse'})
            use_efs = ((i > 0 and kwargs['inject_edge_features']) or
                       (i == 0)) and (self.model_name in {
                           'GSN_edge_sparse', 'MPNN_edge_sparse'
                       })
            if use_ids:
                filter_fn = GSN_edge_sparse if use_efs else GSN_sparse
                kwargs_filter['d_id'] = d_id[i] if self.inject_ids else d_id[0]
                kwargs_filter['id_scope'] = id_scope
            else:
                filter_fn = MPNN_edge_sparse if use_efs else MPNN_sparse
            self.conv.append(filter_fn(**kwargs_filter))

            if self.final_projection[i]:
                # if desired, jk projections can be performed
                # by an mlp instead of a simple linear layer;
                if jk_mlp:
                    jk_layer = mlp(d_in, out_features, d_h[i], seed,
                                   activation_mlp, bn_mlp)
                else:
                    jk_layer = nn.Linear(d_in, out_features)
            else:
                jk_layer = None
            self.lin_proj.append(jk_layer)

            bn_layer = nn.BatchNorm1d(d_out[i]) if self.bn[i] else None
            self.batch_norms.append(bn_layer)

            d_in = d_out[i]

        if self.final_projection[-1]:
            # if desired, jk projections can be performed
            # by an mlp instead of a simple linear layer;
            if jk_mlp:
                final_jk_layer = mlp(d_in, out_features, d_h[-1], seed,
                                     activation_mlp, bn_mlp)
            else:
                final_jk_layer = nn.Linear(d_in, out_features)
        else:
            final_jk_layer = None
        self.lin_proj.append(final_jk_layer)

        self.conv = nn.ModuleList(self.conv)
        self.lin_proj = nn.ModuleList(self.lin_proj)
        self.batch_norms = nn.ModuleList(self.batch_norms)

        #-------------- Readout
        if self.readout == 'sum':
            self.global_pool = global_add_pool_sparse
        elif self.readout == 'mean':
            self.global_pool = global_mean_pool_sparse
        else:
            raise ValueError("Invalid graph pooling type.")

        #-------------- Activation fn (same across the network)
        self.activation = choose_activation(kwargs['activation'])

        return