import os import torch import numpy as np

import torch_geometric from torch import nn from torch.nn import Module, ModuleList, Sequential from torch_geometric.nn import EdgeConv, DenseGCNConv, DenseGraphConv, GCNConv, GATConv from torch.utils.data import DataLoader import torch_scatter

import pytorch_lightning as pl from argparse import Namespace

import torch import numpy import torch import pykeops from pykeops.torch import LazyTensor from torch.nn import Module, ModuleList, Sequential from torch import nn

#Euclidean distance def pairwise_euclidean_distances(x, dim=-1): dist = torch.cdist(x,x)**2 return dist, x

#Poincarè disk distance r=1 (Hyperbolic)

def pairwise_poincare_distances(x, dim=-1): x_norm = (x2).sum(dim,keepdim=True) x_norm = (x_norm.sqrt()-1).relu() + 1 x = x/(x_norm*(1+1e-2)) x_norm = (x2).sum(dim,keepdim=True)

pq = torch.cdist(x,x)**2
dist = torch.arccosh(1e-6+1+2*pq/((1-x_norm)*(1-x_norm.transpose(-1,-2))))**2
return dist, x

class DGM_d(nn.Module): def init(self, embed_f, k=5, distance="euclidean", sparse=True): super(DGM_d, self).init()

    self.sparse=sparse
    
    self.temperature = nn.Parameter(torch.tensor(1. if distance=="hyperbolic" else 4.).float())
    self.embed_f = embed_f
    self.centroid=None
    self.scale=None
    self.k = k
    self.distance = distance
    
    self.debug=False
    
def forward(self, x, A, not_used=None, fixedges=None):
    if x.shape[0]==1:
        x = x[0]
    x = self.embed_f(x,A)
    if x.dim()==2:
        x = x[None,...]

    if self.training:
        if fixedges is not None:                
            return x, fixedges, torch.zeros(fixedges.shape[0],fixedges.shape[-1]//self.k,self.k,dtype=torch.float,device=x.device)
        #sampling here
        edges_hat, logprobs = self.sample_without_replacement(x)
            
    else:
        with torch.no_grad():
            if fixedges is not None:                
                return x, fixedges, torch.zeros(fixedges.shape[0],fixedges.shape[-1]//self.k,self.k,dtype=torch.float,device=x.device)
            #sampling here
            edges_hat, logprobs = self.sample_without_replacement(x)
          
    if self.debug:
        if self.distance=="euclidean":
            D, _x = pairwise_euclidean_distances(x)
        if self.distance=="hyperbolic":
            D, _x = pairwise_poincare_distances(x)
            
        self.D = (D * torch.exp(torch.clamp(self.temperature,-5,5))).detach().cpu()
        self.edges_hat=edges_hat.detach().cpu()
        self.logprobs=logprobs.detach().cpu()

self.x=x

    return x, edges_hat, logprobs


def sample_without_replacement(self, x):
    
    b,n,_ = x.shape
    
    if self.distance=="euclidean":
        G_i = LazyTensor(x[:, :, None, :])    # (M**2, 1, 2)
        X_j = LazyTensor(x[:, None, :, :])    # (1, N, 2)
    
        mD = ((G_i - X_j) ** 2).sum(-1)

        #argKmin already add gumbel noise
        lq = mD * torch.exp(torch.clamp(self.temperature,-5,5))
        indices = lq.argKmin(self.k, dim=1)

        x1 = torch.gather(x, -2, indices.view(indices.shape[0],-1)[...,None].repeat(1,1,x.shape[-1]))
        x2 = x[:,:,None,:].repeat(1,1,self.k,1).view(x.shape[0],-1,x.shape[-1])
        logprobs = (-(x1-x2).pow(2).sum(-1) * torch.exp(torch.clamp(self.temperature,-5,5))).reshape(x.shape[0],-1,self.k)

    if self.distance=="hyperbolic":
        pass
        x_norm = (x**2).sum(-1,keepdim=True)
        x_norm = (x_norm.sqrt()-1).relu() + 1 
        x = x/(x_norm*(1+1e-2)) #safe distance to the margin
        x_norm = (x**2).sum(-1,keepdim=True)
            
        G_i = LazyTensor(x[:, :, None, :])    # (M**2, 1, 2)
        X_j = LazyTensor(x[:, None, :, :])    # (1, N, 2)

        G_i2 = LazyTensor(1-x_norm[:, :, None, :])    # (M**2, 1, 2)
        X_j2 = LazyTensor(1-x_norm[:, None, :, :])    # (1, N, 2)

        pq = ((G_i - X_j) ** 2).sum(-1)
        N = (G_i2*X_j2)
        XX = (1e-6+1+2*pq/N)
        mD = (XX+(XX**2-1).sqrt()).log()**2

        lq = mD * torch.exp(torch.clamp(self.temperature,-5,5))
        indices = lq.argKmin(self.k, dim=1)

        x1 = torch.gather(x, -2, indices.view(indices.shape[0],-1)[...,None].repeat(1,1,x.shape[-1]))
        x2 = x[:,:,None,:].repeat(1,1,self.k,1).view(x.shape[0],-1,x.shape[-1])

        x1_n = torch.gather(x_norm, -2, indices.view(indices.shape[0],-1)[...,None].repeat(1,1,x_norm.shape[-1]))
        x2_n = x_norm[:,:,None,:].repeat(1,1,self.k,1).view(x.shape[0],-1,x_norm.shape[-1])

        pq = (x1-x2).pow(2).sum(-1)
        pqn = ((1-x1_n)*(1-x2_n)).sum(-1)
        XX = 1e-6+1+2*pq/pqn
        dist = torch.log(XX+(XX**2-1).sqrt())**2
        logprobs = (-dist * torch.exp(torch.clamp(self.temperature,-5,5))).reshape(x.shape[0],-1,self.k)

        if self.debug:
            self._x=x.detach().cpu()+0

    
    rows = torch.arange(n).view(1,n,1).to(x.device).repeat(b,1,self.k)
    edges = torch.stack((indices.view(b,-1),rows.view(b,-1)),-2)

    if self.sparse:
        return (edges+(torch.arange(b).to(x.device)*n)[:,None,None]).transpose(0,1).reshape(2,-1), logprobs
    return edges, logprobs

class DGM_c(nn.Module): input_dim = 4 debug=False

def __init__(self, embed_f, k=None, distance="euclidean"):
    super(DGM_c, self).__init__()
    self.temperature = nn.Parameter(torch.tensor(1).float())
    self.threshold = nn.Parameter(torch.tensor(0.5).float())
    self.embed_f = embed_f
    self.centroid=None
    self.scale=None
    self.distance = distance
    
    self.scale = nn.Parameter(torch.tensor(-1).float(),requires_grad=False)
    self.centroid = nn.Parameter(torch.zeros((1,1,DGM_c.input_dim)).float(),requires_grad=False)
    
    
def forward(self, x, A, not_used=None, fixedges=None):
    
    x = self.embed_f(x,A)  
    
    # estimate normalization parameters
    if self.scale <0:            
        self.centroid.data = x.mean(-2,keepdim=True).detach()
        self.scale.data = (0.9/(x-self.centroid).abs().max()).detach()
    
    if self.distance=="hyperbolic":
        D, _x = pairwise_poincare_distances((x-self.centroid)*self.scale)
    else:
        D, _x = pairwise_euclidean_distances((x-self.centroid)*self.scale)
        
    A = torch.sigmoid(self.temperature*(self.threshold.abs()-D))
    
    if DGM_c.debug:
        self.A = A.data.cpu()
        self._x = _x.data.cpu()

self.A=A

A = A/A.sum(-1,keepdim=True)

    return x, A, None

class MLP(nn.Module): def init(self, layers_size,final_activation=False, dropout=0): super(MLP, self).init() layers = [] for li in range(1,len(layers_size)): if dropout>0: layers.append(nn.Dropout(dropout)) layers.append(nn.Linear(layers_size[li-1],layers_size[li])) if li==len(layers_size)-1 and not final_activation: continue layers.append(nn.LeakyReLU(0.1))

    self.MLP = nn.Sequential(*layers)
    
def forward(self, x, e=None):
    x = self.MLP(x)
    return x

class Identity(nn.Module): def init(self,retparam=None): self.retparam=retparam super(Identity, self).init()

def forward(self, *params):
    if self.retparam is not None:
        return params[self.retparam]
    return params

from torch.nn import Module, ModuleList, Sequential from torch import nn

class DGM_d(nn.Module): def init(self, embed_f, k=5, distance=pairwise_euclidean_distances, sparse=True): super(DGM_d, self).init()

    self.sparse=sparse
    
    self.temperature = nn.Parameter(torch.tensor(1. if distance=="hyperbolic" else 4.).float())
    self.embed_f = embed_f
    self.centroid=None
    self.scale=None
    self.k = k
    
    self.debug=False
    if distance == 'euclidean':
        self.distance = pairwise_euclidean_distances
    else:
        self.distance = pairwise_poincare_distances
    
def forward(self, x, A, not_used=None, fixedges=None):
    x = self.embed_f(x,A)  
    
    if self.training:
        if fixedges is not None:                
            return x, fixedges, torch.zeros(fixedges.shape[0],fixedges.shape[-1]//self.k,self.k,dtype=torch.float,device=x.device)
        
        D, _x = self.distance(x)
       
        #sampling here
        edges_hat, logprobs = self.sample_without_replacement(D)
            
    else:
        with torch.no_grad():
            if fixedges is not None:                
                return x, fixedges, torch.zeros(fixedges.shape[0],fixedges.shape[-1]//self.k,self.k,dtype=torch.float,device=x.device)
            D, _x = self.distance(x)

            #sampling here
            edges_hat, logprobs = self.sample_without_replacement(D)

          
    if self.debug:
        self.D = D
        self.edges_hat=edges_hat
        self.logprobs=logprobs
        self.x=x

    return x, edges_hat, logprobs


def sample_without_replacement(self, logits):
    b,n,_ = logits.shape

logits = logits * torch.exp(self.temperature*10)

    logits = logits * torch.exp(torch.clamp(self.temperature,-5,5))
    
    q = torch.rand_like(logits) + 1e-8
    lq = (logits-torch.log(-torch.log(q)))
    logprobs, indices = torch.topk(-lq,self.k)  

    rows = torch.arange(n).view(1,n,1).to(logits.device).repeat(b,1,self.k)
    edges = torch.stack((indices.view(b,-1),rows.view(b,-1)),-2)
    
    if self.sparse:
        return (edges+(torch.arange(b).to(logits.device)*n)[:,None,None]).transpose(0,1).reshape(2,-1), logprobs
    return edges, logprobs

class DGM_Model(pl.LightningModule): def init(self, hparams): super(DGM_Model,self).init()

    if type(hparams) is not Namespace:
        hparams = Namespace(**hparams)

self.hparams=hparams

    self.save_hyperparameters(hparams)
    conv_layers = hparams.conv_layers
    fc_layers = hparams.fc_layers
    dgm_layers = hparams.dgm_layers
    k = hparams.k

        
    self.graph_f = ModuleList() 
    self.node_g = ModuleList() 
    for i,(dgm_l,conv_l) in enumerate(zip(dgm_layers,conv_layers)):
        if len(dgm_l)>0:
            if 'ffun' not in hparams or hparams.ffun == 'gcn':
                self.graph_f.append(DGM_d(GCNConv(dgm_l[0],dgm_l[-1]),k=hparams.k,distance=hparams.distance))
            if hparams.ffun == 'gat':
                self.graph_f.append(DGM_d(GATConv(dgm_l[0],dgm_l[-1]),k=hparams.k,distance=hparams.distance))
            if hparams.ffun == 'mlp':
                self.graph_f.append(DGM_d(MLP(dgm_l),k=hparams.k,distance=hparams.distance))
            if hparams.ffun == 'knn':
                self.graph_f.append(DGM_d(Identity(retparam=0),k=hparams.k,distance=hparams.distance))

self.graph_f.append(DGM_d(GCNConv(dgm_l[0],dgm_l[-1]),k=hparams.k,distance=hparams.distance))

        else:
            self.graph_f.append(Identity())
        
        if hparams.gfun == 'edgeconv':
            conv_l=conv_l.copy()
            conv_l[0]=conv_l[0]*2
            self.node_g.append(EdgeConv(MLP(conv_l), hparams.pooling))
        if hparams.gfun == 'gcn':
            self.node_g.append(GCNConv(conv_l[0],conv_l[1]))
        if hparams.gfun == 'gat':
            self.node_g.append(GATConv(conv_l[0],conv_l[1]))
    
    self.fc = MLP(fc_layers, final_activation=False)
    if hparams.pre_fc is not None and len(hparams.pre_fc)>0:
        self.pre_fc = MLP(hparams.pre_fc, final_activation=True)
    self.avg_accuracy = None
    
    
    #torch lightning specific
    self.automatic_optimization = False
    self.debug=False
    
def forward(self,x, edges=None):
    if self.hparams.pre_fc is not None and len(self.hparams.pre_fc)>0:
        x = self.pre_fc(x)
        
    graph_x = x.detach()
    lprobslist = []
    for f,g in zip(self.graph_f, self.node_g):
        graph_x,edges,lprobs = f(graph_x,edges,None)
        b,n,d = x.shape

edges,_ = torch_geometric.utils.remove_self_loops(edges)

edges,_ = torch_geometric.utils.add_self_loops(edges)

        self.edges=edges
        x = torch.nn.functional.relu(g(torch.dropout(x.view(-1,d), self.hparams.dropout, train=self.training), edges)).view(b,n,-1)
        graph_x = torch.cat([graph_x,x.detach()],-1)
        if lprobs is not None:
            lprobslist.append(lprobs)
            
    return self.fc(x),torch.stack(lprobslist,-1) if len(lprobslist)>0 else None

def configure_optimizers(self):
    optimizer = torch.optim.Adam(self.parameters(), lr=self.hparams.lr)
    return optimizer

def training_step(self, train_batch, batch_idx):
    
    optimizer = self.optimizers(use_pl_optimizer=True)
    optimizer.zero_grad()
    
    X, y, mask, edges = train_batch
    edges = edges[0]
    
    assert(X.shape[0]==1) #only works in transductive setting
    mask=mask[0]
    
    pred,logprobs = self(X,edges)
    
    train_pred = pred[:,mask.to(torch.bool),:]
    train_lab = y[:,mask.to(torch.bool),:]

train_w = weight[None,mask.to(torch.bool)]

    #loss = torch.nn.functional.cross_entropy(train_pred.view(-1,train_pred.shape[-1]),train_lab.argmax(-1).flatten())
    loss = torch.nn.functional.binary_cross_entropy_with_logits(train_pred,train_lab)
    loss.backward()

    correct_t = (train_pred.argmax(-1) == train_lab.argmax(-1)).float().mean().item()

    #GRAPH LOSS
    if logprobs is not None: 
        corr_pred = (train_pred.argmax(-1)==train_lab.argmax(-1)).float().detach()
        wron_pred = (1-corr_pred)

        if self.avg_accuracy is None:
            self.avg_accuracy = torch.ones_like(corr_pred)*0.5

        point_w = (self.avg_accuracy-corr_pred)#*(1*corr_pred + self.k*(1-corr_pred))
        graph_loss = point_w * logprobs[:,mask.to(torch.bool),:].exp().mean([-1,-2])

        graph_loss = graph_loss.mean()# + self.graph_f[0].Pr.abs().sum()*1e-3
        graph_loss.backward()
        
        self.log('train_graph_loss', graph_loss.detach().cpu())
        if(self.debug):
            self.point_w = point_w.detach().cpu()
            
        self.avg_accuracy = self.avg_accuracy.to(corr_pred.device)*0.95 +  0.05*corr_pred
        
    optimizer.step()

    self.log('train_acc', correct_t)
    self.log('train_loss', loss.detach().cpu())
    

def test_step(self, train_batch, batch_idx):
    X, y, mask, edges = train_batch
    edges = edges[0]
    
    assert(X.shape[0]==1) #only works in transductive setting
    mask=mask[0]
    pred,logprobs = self(X,edges)
    pred = pred.softmax(-1)
    for i in range(1,self.hparams.test_eval):
        pred_,logprobs = self(X,edges)
        pred+=pred_.softmax(-1)
    test_pred = pred[:,mask.to(torch.bool),:]
    test_lab = y[:,mask.to(torch.bool),:]
    correct_t = (test_pred.argmax(-1) == test_lab.argmax(-1)).float().mean().item()
    loss = torch.nn.functional.binary_cross_entropy_with_logits(test_pred,test_lab)
    self.log('test_loss', loss.detach().cpu())

self.log('test_graph_loss', loss.detach().cpu())

    self.log('test_acc', 100*correct_t)

def validation_step(self, train_batch, batch_idx):
    X, y, mask, edges = train_batch
    edges = edges[0]
    
    
    assert(X.shape[0]==1) #only works in transductive setting
    mask=mask[0]
    
    pred,logprobs = self(X,edges)
    pred = pred.softmax(-1)
    for i in range(1,self.hparams.test_eval):
        pred_,logprobs = self(X,edges)
        pred+=pred_.softmax(-1)
    
    test_pred = pred[:,mask.to(torch.bool),:]
    test_lab = y[:,mask.to(torch.bool),:]
    correct_t = (test_pred.argmax(-1) == test_lab.argmax(-1)).float().mean().item()
    loss = torch.nn.functional.binary_cross_entropy_with_logits(test_pred,test_lab)
    
    self.log('val_loss', loss.detach())
    self.log('val_acc', 100*correct_t)

####### visualizations

try:

self.graph_f[0].debug=True

pred,logprobs = self(X)

self.graph_f[0].debug=False

x = self.graph_f[0].x[0].detach()

c = torch.argmax(y,-1)

D = self.graph_f[0].distance(x)[0]

D.diagonal().fill_(0)

sidx = torch.argsort( (c[0]+1)*10 + (mask+1)*1)

P = torch.exp(-D[sidx,:][:,sidx]*torch.clamp(self.graph_f[0].temperature.detach().cpu(),-5,5).exp())#>0.001

img = PIL.Image.fromarray((P*255).byte().detach().cpu().numpy())

img = img.resize((512,512), PIL.Image.ANTIALIAS)

I = wandb.Image(img, caption="adj")

self.logger.experiment.log({'adj': [I]})

except:

pass

import sys import torch import pickle import numpy as np import os.path as osp import torch from torch_geometric.datasets import Planetoid import torch_geometric.transforms as T

class UKBBAgeDataset(torch.utils.data.Dataset): """Face Landmarks dataset."""

def __init__(self, fold=0, train=True, samples_per_epoch=100, device='cpu'):
    with open('data/UKBB.pickle', 'rb') as f:
        X_,y_,train_mask_,test_mask_, weight_ = pickle.load(f) # Load the data

    self.X = torch.from_numpy(X_[:,:,fold]).float().to(device)
    self.y = torch.from_numpy(y_[:,:,fold]).float().to(device)
    self.weight = torch.from_numpy(np.squeeze(weight_[:1,fold])).float().to(device)
    if train:
        self.mask = torch.from_numpy(train_mask_[:,fold]).to(device)
    else:
        self.mask = torch.from_numpy(test_mask_[:,fold]).to(device)
        
    self.samples_per_epoch = samples_per_epoch

def __len__(self):
    return self.samples_per_epoch

def __getitem__(self, idx):
    return self.X,self.y,self.mask

class TadpoleDataset(torch.utils.data.Dataset): """Face Landmarks dataset."""

def __init__(self, fold=0, train=True, samples_per_epoch=100, device='cpu',full=False):
    with open('data/tadpole_data.pickle', 'rb') as f:
        X_,y_,train_mask_,test_mask_, weight_ = pickle.load(f) # Load the data
    
    if not full:
        X_ = X_[...,:30,:] # For DGM we use modality 1 (M1) for both node representation and graph learning.

    
    self.n_features = X_.shape[-2]
    self.num_classes = y_.shape[-2]
    
    self.X = torch.from_numpy(X_[:,:,fold]).float().to(device)
    self.y = torch.from_numpy(y_[:,:,fold]).float().to(device)
    self.weight = torch.from_numpy(np.squeeze(weight_[:1,fold])).float().to(device)
    if train:
        self.mask = torch.from_numpy(train_mask_[:,fold]).to(device)
    else:
        self.mask = torch.from_numpy(test_mask_[:,fold]).to(device)
        
    self.samples_per_epoch = samples_per_epoch

def __len__(self):
    return self.samples_per_epoch

def __getitem__(self, idx):
    return self.X,self.y,self.mask, [[]]

class TadpoleDataset(torch.utils.data.Dataset):

"""Face Landmarks dataset."""

def init(self, fold=0, split='train', samples_per_epoch=100, device='cpu'):

with open('data/train_data.pickle', 'rb') as f:

X_,y_,train_mask_,test_mask_, weight_ = pickle.load(f) # Load the data

X_ = X_[...,:30,:] # For DGM we use modality 1 (M1) for both node representation and graph learning.

self.X = torch.from_numpy(X_[:,:,fold]).float().to(device)

self.y = torch.from_numpy(y_[:,:,fold]).float().to(device)

self.weight = torch.from_numpy(np.squeeze(weight_[:1,fold])).float().to(device)

# split train set in train/val

train_mask = train_mask_[:,fold]

nval = int(train_mask.sum()*0.2)

val_idxs = np.random.RandomState(1).choice(np.nonzero(train_mask.flatten())[0],(nval,),replace=False)

train_mask[val_idxs] = 0;

val_mask = train_mask*0

val_mask[val_idxs] = 1

print('DATA STATS: train: %d val: %d' % (train_mask.sum(),val_mask.sum()))

if split=='train':

self.mask = torch.from_numpy(train_mask).to(device)

if split=='val':

self.mask = torch.from_numpy(val_mask).to(device)

if split=='test':

self.mask = torch.from_numpy(test_mask_[:,fold]).to(device)

self.samples_per_epoch = samples_per_epoch

def len(self):

return self.samples_per_epoch

def getitem(self, idx):

return self.X,self.y,self.mask

def get_planetoid_dataset(name, normalize_features=True, transform=None, split="complete"): path = osp.join('.', 'data', name) if split == 'complete': dataset = Planetoid(path, name) dataset[0].train_mask.fill_(False) dataset[0].train_mask[:dataset[0].num_nodes - 1000] = 1 dataset[0].val_mask.fill_(False) dataset[0].val_mask[dataset[0].num_nodes - 1000:dataset[0].num_nodes - 500] = 1 dataset[0].test_mask.fill_(False) dataset[0].test_mask[dataset[0].num_nodes - 500:] = 1 else: dataset = Planetoid(path, name, split=split) if transform is not None and normalize_features: dataset.transform = T.Compose([T.NormalizeFeatures(), transform]) elif normalize_features: dataset.transform = T.NormalizeFeatures() elif transform is not None: dataset.transform = transform return dataset

def one_hot_embedding(labels, num_classes): y = torch.eye(num_classes) return y[labels]

class PlanetoidDataset(torch.utils.data.Dataset): def init(self, split='train', samples_per_epoch=100, name='Cora', device='cpu'): dataset = get_planetoid_dataset(name) self.X = dataset[0].x.float().to(device) self.y = one_hot_embedding(dataset[0].y,dataset.num_classes).float().to(device) self.edge_index = dataset[0].edge_index.to(device) self.n_features = dataset[0].num_node_features self.num_classes = dataset.num_classes

    if split=='train':
        self.mask = dataset[0].train_mask.to(device)
    if split=='val':
        self.mask = dataset[0].val_mask.to(device)
    if split=='test':
        self.mask = dataset[0].test_mask.to(device)
     
    self.samples_per_epoch = samples_per_epoch

def __len__(self):
    return self.samples_per_epoch

def __getitem__(self, idx):
    return self.X,self.y,self.mask,self.edge_index

os.environ["CUDA_VISIBLE_DEVICES"]="0";

import pickle import numpy as np

import torch from torch.utils.data import DataLoader import pytorch_lightning as pl

from argparse import ArgumentParser from pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping from pytorch_lightning.loggers import TensorBoardLogger

def run_training_process(run_params):

train_data = None
test_data = None

if run_params.dataset in ['Cora', 'CiteSeer', 'PubMed']:
    train_data = PlanetoidDataset(split='train', name=run_params.dataset, device='cuda')
    val_data = PlanetoidDataset(split='val', name=run_params.dataset, samples_per_epoch=1)
    test_data = PlanetoidDataset(split='test', name=run_params.dataset, samples_per_epoch=1)
    
if run_params.dataset == 'tadpole':
    train_data = TadpoleDataset(fold=run_params.fold,train=True, device='cuda')
    val_data = test_data = TadpoleDataset(fold=run_params.fold, train=False,samples_per_epoch=1)
                               
if train_data is None:
    raise Exception("Dataset %s not supported" % run_params.dataset)
    
train_loader = DataLoader(train_data, batch_size=1,num_workers=0)
val_loader = DataLoader(val_data, batch_size=1)
test_loader = DataLoader(test_data, batch_size=1)

class MyDataModule(pl.LightningDataModule):
    def setup(self,stage=None):
        pass
    def train_dataloader(self):
        return train_loader
    def val_dataloader(self):
        return val_loader
    def test_dataloader(self):
        return test_loader


#configure input feature size
if run_params.pre_fc is None or len(run_params.pre_fc)==0: 
    if len(run_params.dgm_layers[0])>0:
        run_params.dgm_layers[0][0]=train_data.n_features
    run_params.conv_layers[0][0]=train_data.n_features
else:
    run_params.pre_fc[0]=train_data.n_features
run_params.fc_layers[-1] = train_data.num_classes

model = DGM_Model(run_params)

checkpoint_callback = ModelCheckpoint(
    save_last=True,
    save_top_k=1,
    verbose=True,
    monitor='val_loss',
    mode='min'
)
early_stop_callback = EarlyStopping(
    monitor='val_loss',
    min_delta=0.00,
    patience=20,
    verbose=False,
    mode='min')
callbacks = [checkpoint_callback,early_stop_callback]

if val_data==test_data:
    callbacks = None
    
logger = TensorBoardLogger("logs/")
trainer = pl.Trainer.from_argparse_args(run_params,logger=logger,
                                        callbacks=callbacks)

trainer.fit(model, datamodule=MyDataModule())
trainer.test()

if name == "main":

parser = ArgumentParser()
parser = pl.Trainer.add_argparse_args(parser)
params = parser.parse_args(['--gpus','1',                         
                          '--log_every_n_steps','100',                          
                          '--max_epochs','100',
                          '--progress_bar_refresh_rate','10',                         
                          '--check_val_every_n_epoch','1'])
parser.add_argument("--num_gpus", default=10, type=int)

parser.add_argument("--dataset", default='Cora')
parser.add_argument("--fold", default='0', type=int) #Used for k-fold cross validation in tadpole/ukbb


parser.add_argument("--conv_layers", default=[[32,32],[32,16],[16,8]], type=lambda x :eval(x))
parser.add_argument("--dgm_layers", default= [[32,16,4],[],[]], type=lambda x :eval(x))
parser.add_argument("--fc_layers", default=[8,8,3], type=lambda x :eval(x))
parser.add_argument("--pre_fc", default=[-1,32], type=lambda x :eval(x))

parser.add_argument("--gfun", default='gcn')
parser.add_argument("--ffun", default='gcn')
parser.add_argument("--k", default=5, type=int) 
parser.add_argument("--pooling", default='add')
parser.add_argument("--distance", default='euclidean')

parser.add_argument("--dropout", default=0.0, type=float)
parser.add_argument("--lr", default=1e-2, type=float)
parser.add_argument("--test_eval", default=10, type=int)

parser.set_defaults(default_root_path='./log')
params = parser.parse_args(namespace=params)

run_training_process(params)