code: def train(args, device):
      elliptic_dataset = EllipticDataset(
          raw_dir=args.raw_dir,
          processed_dir=args.processed_dir,
          self_loop=True,
          reverse_edge=True,
      )
  
      g, node_mask_by_time = elliptic_dataset.process()
      num_classes = elliptic_dataset.num_classes
  
      cached_subgraph = []
      cached_labeled_node_mask = []
      for i in range(len(node_mask_by_time)):
          # we add self loop edge when we construct full graph, not here
          node_subgraph = dgl.node_subgraph(
              graph=g, nodes=node_mask_by_time[i]
          )
          cached_subgraph.append(node_subgraph.to(device))
          valid_node_mask = node_subgraph.ndata["label"] >= 0
          cached_labeled_node_mask.append(valid_node_mask)
  
      if args.model == "EvolveGCN-O":
          model = EvolveGCNO(
              in_feats=int(g.ndata["feat"].shape[1]),
              n_hidden=args.n_hidden,
              num_layers=args.n_layers,
          )
      elif args.model == "EvolveGCN-H":
          model = EvolveGCNH(
              in_feats=int(g.ndata["feat"].shape[1]),
              num_layers=args.n_layers,
          )
      else:
          return NotImplementedError(
              "Unsupported model {}".format(args.model)
          )
      model = model.to(device)
      optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)
  
      # split train, valid, test(0-30,31-35,36-48)
      # train/valid/test split follow the paper.
      train_max_index = 30
      valid_max_index = 35
      test_max_index = 48
      time_window_size = args.n_hist_steps
      loss_class_weight = [
          float(w) for w in args.loss_class_weight.split(",")
      ]
      loss_class_weight = torch.Tensor(loss_class_weight).to(device)
  
      train_measure = Measure(
          num_classes=num_classes, target_class=args.eval_class_id
      )
      valid_measure = Measure(
          num_classes=num_classes, target_class=args.eval_class_id
      )
      test_measure = Measure(
          num_classes=num_classes, target_class=args.eval_class_id
      )
  
      test_res_f1 = 0
      for epoch in range(args.num_epochs):
          model.train()
          for i in range(time_window_size, train_max_index + 1):
              g_list = cached_subgraph[i - time_window_size : i + 1]
              predictions = model(g_list)
              # get predictions which has label
              predictions = predictions[cached_labeled_node_mask[i]]
              labels = (
                  cached_subgraph[i]
                  .ndata["label"][cached_labeled_node_mask[i]]
                  .long()
              )
              loss = F.cross_entropy(
                  predictions, labels, weight=loss_class_weight
              )
              optimizer.zero_grad()
              loss.backward()
              optimizer.step()
  
              train_measure.append_measures(predictions, labels)
  
          # get each epoch measures during training.
          (
              cl_precision,
              cl_recall,
              cl_f1,
          ) = train_measure.get_total_measure()
          train_measure.update_best_f1(cl_f1, epoch)
          # reset measures for next epoch
          train_measure.reset_info()
  
          print(
              "Train Epoch {} | class {} | precision:{:.4f} | recall: {:.4f} | f1: {:.4f}".format(
                  epoch,
                  args.eval_class_id,
                  cl_precision,
                  cl_recall,
                  cl_f1,
              )
          )
  
          # eval
          model.eval()
          for i in range(train_max_index + 1, valid_max_index + 1):
              g_list = cached_subgraph[i - time_window_size : i + 1]
              predictions = model(g_list)
              # get node predictions which has label
              predictions = predictions[cached_labeled_node_mask[i]]
              labels = (
                  cached_subgraph[i]
                  .ndata["label"][cached_labeled_node_mask[i]]
                  .long()
              )
  
              valid_measure.append_measures(predictions, labels)
  
          # get each epoch measure during eval.
          (
              cl_precision,
              cl_recall,
              cl_f1,
          ) = valid_measure.get_total_measure()
          valid_measure.update_best_f1(cl_f1, epoch)
          # reset measures for next epoch
          valid_measure.reset_info()
  
          print(
              "Eval Epoch {} | class {} | precision:{:.4f} | recall: {:.4f} | f1: {:.4f}".format(
                  epoch,
                  args.eval_class_id,
                  cl_precision,
                  cl_recall,
                  cl_f1,
              )
          )
  
          # early stop
          if (
              epoch - valid_measure.target_best_f1_epoch
              >= args.patience
          ):
              print(
                  "Best eval Epoch {}, Cur Epoch {}".format(
                      valid_measure.target_best_f1_epoch, epoch
                  )
              )
              break
          # if cur valid f1 score is best, do test
          if epoch == valid_measure.target_best_f1_epoch:
              print(
                  "###################Epoch {} Test###################".format(
                      epoch
                  )
              )
              for i in range(valid_max_index + 1, test_max_index + 1):
                  g_list = cached_subgraph[i - time_window_size : i + 1]
                  predictions = model(g_list)
                  # get predictions which has label
                  predictions = predictions[cached_labeled_node_mask[i]]
                  labels = (
                      cached_subgraph[i]
                      .ndata["label"][cached_labeled_node_mask[i]]
                      .long()
                  )
  
                  test_measure.append_measures(predictions, labels)
  
              # we get each subgraph measure when testing to match fig 4 in EvolveGCN paper.
              (
                  cl_precisions,
                  cl_recalls,
                  cl_f1s,
              ) = test_measure.get_each_timestamp_measure()
              for index, (sub_p, sub_r, sub_f1) in enumerate(
                  zip(cl_precisions, cl_recalls, cl_f1s)
              ):
                  print(
                      "  Test | Time {} | precision:{:.4f} | recall: {:.4f} | f1: {:.4f}".format(
                          valid_max_index + index + 2,
                          sub_p,
                          sub_r,
                          sub_f1,
                      )
                  )
  
              # get each epoch measure during test.
              (
                  cl_precision,
                  cl_recall,
                  cl_f1,
              ) = test_measure.get_total_measure()
              test_measure.update_best_f1(cl_f1, epoch)
              # reset measures for next test
              test_measure.reset_info()
  
              test_res_f1 = cl_f1
  
              print(
                  "  Test | Epoch {} | class {} | precision:{:.4f} | recall: {:.4f} | f1: {:.4f}".format(
                      epoch,
                      args.eval_class_id,
                      cl_precision,
                      cl_recall,
                      cl_f1,
                  )
              )
  
      print(
          "Best test f1 is {}, in Epoch {}".format(
              test_measure.target_best_f1,
              test_measure.target_best_f1_epoch,
          )
      )
      if (
          test_measure.target_best_f1_epoch
          != valid_measure.target_best_f1_epoch
      ):
          print(
              "The Epoch get best Valid measure not get the best Test measure, "
              "please checkout the test result in Epoch {}, which f1 is {}".format(
                  valid_measure.target_best_f1_epoch, test_res_f1
              )
          )
  
file_path: dgl/examples/pytorch/evolveGCN/train.py
imports: import argparse
  import time
  import dgl
  import torch
  import torch.nn.functional
  from dataset import EllipticDataset
  from model import EvolveGCNH, EvolveGCNO
  from utils import Measure
label_desc: Use image node_id 1 for calling a global function with example usage: train(args, device) and returns: NotImplementedError