text: import os, glob, yaml, zipfile, shutil, wandb, subprocess, re
  osp = os.path
  import dill as pickle
  import numpy as np
  import pandas as pd
  import torch
  nn = torch.nn
  F = nn.functional
  
  base_proj_dir = "/data/vision/polina/users/clintonw/code/mmdet"
  base_job_dir = "/data/vision/polina/users/clintonw/code/mmdet/results"
  entity_project = "spatial_relations/main_project"
  
  def get_number_in_string(string, n=1):
      nums = re.findall(r"[\d.]+", string)
      if len(nums) < n:
          return
      if n > 0:
          n = n-1
      if "." in nums[n]:
          return float(nums[n])
      else:
          return int(nums[n])
  
  def get_all_regex_matches(pattern, string):
      return list(re.finditer(pattern, string))
  
  def sample_without_replacement(X, size=None):
      indices = np.random.choice(range(len(X)), size=size, replace=False)
      if size is None:
          return X[indices]
      return [X[ix] for ix in indices]
  
  def set_random_seed(seed):
      np.random.seed(seed)
      torch.manual_seed(seed)
  
  def get_num_labels(dataset):
      if dataset["name"] == "MNIST grid":
          return len(dataset["train label counts"])
      elif dataset["name"] == "pixels":
          return dataset["number of labels"]
      else:
          raise NotImplementedError
  
  def end_slurm_jobs(jobs=None):
      if jobs is None:
          jobs = get_my_slurm_ids()
      else:
          jobs = [slurm_id for name,slurm_id in zip(get_my_slurm_jobs(), get_my_slurm_ids()) if name in jobs]
  
      for job in jobs:
          run_bash_cmd_with_stdout(f"scancel {job}")
  
  def run_bash_cmd_with_stdout(command, grep=None):
      proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
      out_lines = [line.decode("utf-8").rstrip() for line in proc.stdout.readlines()]
      if grep is None:
          return out_lines
      else:
          return [line for line in out_lines if grep in line]
  
  def get_my_slurm_ids(ignore_name="jupyter"):
      lines = run_bash_cmd_with_stdout("squeue", grep="clintonw")
      lines = [line for line in lines if not ("PD" in line or ignore_name in line)]
      slurm_ids = [line.split()[0] for line in lines]
      return slurm_ids
  
  def get_my_slurm_jobs(ignore_name="jupyter"):
      lines = run_bash_cmd_with_stdout("squeue", grep="clintonw")
      lines = [line for line in lines if not ("PD" in line or ignore_name in line)]
      slurm_ids = [line.split()[2] for line in lines]
      return slurm_ids
  
  def rename_job(job, new_name):
      os.rename(f"{base_job_dir}/{job}", f"{base_job_dir}/{new_name}")
  
  def get_code_directory():
      return f"{base_proj_dir}/mmdet"
  
  def get_config_path_for_job(job):
      matches = glob.glob(f"{base_job_dir}/{job}/*.py")
      assert len(matches) == 1, "cannot determine config path"
      return matches[0]
  
  def get_hyperparameter_for_job(hyperparameter, job):
      config_path = get_config_path_for_job(job)
      cfg = Config.fromfile(config_path)
      return get_nested_attr(args, hyperparameter)
  
  # def get_run_id_for_job(job):
  #     return get_hyperparameter_for_job("run_id", job)
  
  # def get_run_path_for_job(job):
  #     return get_hyperparameter_for_job("run_path", job)
  
  def to_numpy(tensor):
      return tensor.detach().cpu().numpy()
  
  def get_nested_attr(x, keys):
      if isinstance(keys, str):
          return x[keys]
      ret = x
      for k in keys:
          if isinstance(ret, dict):
              ret = ret[k]
          else:
              ret = getattr(ret, k)
      return ret
  
  def print_job_log(job_name):
      path = f"{base_job_dir}/{job_name}/logs.out"
      if not osp.exists(path):
          return
      with open(path, "r") as f:
          return f.readlines()
  
  def print_job_err(job_name):
      path = f"{base_job_dir}/{job_name}/logs.err"
      if not osp.exists(path):
          return
      with open(path, "r") as f:
          return f.readlines()
  
  
  # def delete_job_outputs(job_name=None):
  #     api = wandb.Api()
  #     if job_name is None:
  #         return delete_job_outputs("*")
  #     elif "*" in job_name:
  #         for path in glob.glob(f"{base_job_dir}/{job_name}"):
  #             print(f"deleting {path}")
  #             delete_job_outputs(os.path.basename(path))
  #     else:
  #         path = f"{base_job_dir}/{job_name}"
  #         try:
  #             run_path = get_run_path_for_job(job_name)
  #             if run_path is not None:
  #                 run = api.run(run_path)
  #                 run.delete(delete_artifacts=True)
  
  #             shutil.rmtree(path)
  #         except:
  #             pass
  
  def parse_int_list(x):
      # converts string to a list of ints
      if not isinstance(x, str):
          return [x]
      try:
          return [int(x)]
      except ValueError:
          return [int(s.strip()) for s in x.split(',')]
  # def parse_int_or_list(x):
  #     # converts string to an int or list of ints
  #     if not isinstance(x, str):
  #         return x
  #     try:
  #         return int(x)
  #     except ValueError:
  #         return [int(s.strip()) for s in x.split(',')]
  
  
  def save_code_dir(code_dir, save_dir):
      root_path = os.path.dirname(code_dir)
      zipf = zipfile.ZipFile(os.path.join(save_dir, 'code.zip'), 'w', zipfile.ZIP_DEFLATED)
      for root, dirs, files in os.walk(code_dir):
          # if root.endswith("job_outputs"):
          #     continue
          for file in files:
              file_path = os.path.join(root, file)
              relative_path = os.path.join('code/',os.path.relpath(file_path, root_path))
              zipf.write(file_path, arcname=relative_path) 
      zipf.close()
  
  def get_job_output_folder(job):
      return f"{base_job_dir}/{job}"
  
  def save_binary_file(path, data):
      with open(path, "wb") as opened_file:
          pickle.dump(data, opened_file)
  
  def load_binary_file(path):
      with open(path, "rb") as opened_file:
          return pickle.load(opened_file)
  
  
  def collate_metrics_for_jobs(metrics, jobs):
      from collections import OrderedDict
      table = pd.DataFrame(index=jobs, columns=metrics)
      for job in jobs:
          logs = print_job_log(job+"_01_test")
          if logs is None:
              logs = print_job_log(job+"_test")
              if logs is None:
                  continue
          for line in logs:
              if line.startswith("OrderedDict([("):
                  job_dict = eval(line)
                  break
          try:
              table.loc[job] = [job_dict[m] if m in job_dict else np.nan for m in metrics]
          except NameError:
              continue
      return table
  
  def show_sample_result(job_name):
      path = f"{base_job_dir}/{job_name}/imgs"
      if not osp.exists(path):
          raise ValueError(f"{path} does not exist")
      img_paths = glob.glob(path+"/*/*.jpg")
      if len(img_paths) == 0:
          raise ValueError("no outputs")
      img = plt.imread(img_paths[0])
      plt.imshow(img)
      return img
  
  