code: def run_train(
      dataset,
      model,
      runs,
      epochs,
      lr,
      weight_decay,
      early_stopping,
      profiling,
      use_compile,
      permute_masks=None,
      logger=None,
  ):
      val_losses, accs, durations = [], [], []
      if use_compile:
          model = torch.compile(model)
  
      for run in range(runs):
          data = dataset[0]
          if permute_masks is not None:
              data = permute_masks(data, dataset.num_classes)
          data = data.to(device)
  
          model.to(device).reset_parameters()
          optimizer = Adam(
              model.parameters(), lr=lr, weight_decay=weight_decay
          )
  
          if torch.cuda.is_available():
              torch.cuda.synchronize()
          elif (
              hasattr(torch.backends, "mps")
              and torch.backends.mps.is_available()
          ):
              try:
                  import torch.mps
  
                  torch.mps.synchronize()
              except ImportError:
                  pass
  
          t_start = time.perf_counter()
  
          best_val_loss = float("inf")
          test_acc = 0
          val_loss_history = []
  
          for epoch in range(1, epochs + 1):
              if run == runs - 1 and epoch == epochs:
                  with timeit():
                      train(model, optimizer, data)
              else:
                  train(model, optimizer, data)
              eval_info = evaluate(model, data)
              eval_info["epoch"] = epoch
  
              if logger is not None:
                  logger(eval_info)
  
              if eval_info["val_loss"] < best_val_loss:
                  best_val_loss = eval_info["val_loss"]
                  test_acc = eval_info["test_acc"]
  
              val_loss_history.append(eval_info["val_loss"])
              if early_stopping > 0 and epoch > epochs // 2:
                  tmp = tensor(
                      val_loss_history[-(early_stopping + 1) : -1]
                  )
                  if eval_info["val_loss"] > tmp.mean().item():
                      break
  
          if torch.cuda.is_available():
              torch.cuda.synchronize()
          elif (
              hasattr(torch.backends, "mps")
              and torch.backends.mps.is_available()
          ):
              try:
                  import torch.mps
  
                  torch.mps.synchronize()
              except ImportError:
                  pass
  
          t_end = time.perf_counter()
  
          val_losses.append(best_val_loss)
          accs.append(test_acc)
          durations.append(t_end - t_start)
      loss, acc, duration = (
          tensor(val_losses),
          tensor(accs),
          tensor(durations),
      )
  
      print(
          f"Val Loss: {float(loss.mean()):.4f}, "
          f"Test Accuracy: {float(acc.mean()):.3f} ± {float(acc.std()):.3f}, "
          f"Duration: {float(duration.mean()):.3f}s"
      )
  
      if profiling:
          with torch_profile():
              train(model, optimizer, data)
  
file_path: pytorch_geometric/benchmark/citation/train_eval.py
imports: import time
  import torch
  import torch.nn.functional
  from torch import tensor
  from torch.optim import Adam
  from torch_geometric.profile import timeit, torch_profile
  from torch_geometric.utils import index_to_mask
label_desc: Use image node_id 2 for calling a global function with example usage: run_train(dataset, model, runs, epochs, lr, weight_decay, early_stopping, profiling, use_compile, permute_masks, logger) without return types