code: def check_dist_graph_hetero(g, num_clients, num_nodes, num_edges):
      # Test API
      for ntype in num_nodes:
          assert ntype in g.ntypes
          assert num_nodes[ntype] == g.num_nodes(ntype)
      for etype in num_edges:
          assert etype in g.etypes
          assert num_edges[etype] == g.num_edges(etype)
      etypes = [
          ("n1", "r1", "n2"),
          ("n1", "r2", "n3"),
          ("n2", "r3", "n3"),
      ]
      for i, etype in enumerate(g.canonical_etypes):
          assert etype[0] == etypes[i][0]
          assert etype[1] == etypes[i][1]
          assert etype[2] == etypes[i][2]
      assert g.num_nodes() == sum(
          [num_nodes[ntype] for ntype in num_nodes]
      )
      assert g.num_edges() == sum(
          [num_edges[etype] for etype in num_edges]
      )
  
      # Test reading node data
      ntype = "n1"
      nids = F.arange(0, g.num_nodes(ntype) // 2)
      for name in ["feat", ntype]:
          data = g.nodes[ntype].data[name][nids]
          data = F.squeeze(data, 1)
          assert np.all(F.asnumpy(data == nids))
      assert len(g.nodes["n2"].data) == 0
      expect_except = False
      try:
          g.nodes["xxx"].data["x"]
      except dgl.DGLError:
          expect_except = True
      assert expect_except
  
      # Test reading edge data
      etype = "r1"
      eids = F.arange(0, g.num_edges(etype) // 2)
      for name in ["feat", etype]:
          # access via etype
          data = g.edges[etype].data[name][eids]
          data = F.squeeze(data, 1)
          assert np.all(F.asnumpy(data == eids))
          # access via canonical etype
          c_etype = g.to_canonical_etype(etype)
          data = g.edges[c_etype].data[name][eids]
          data = F.squeeze(data, 1)
          assert np.all(F.asnumpy(data == eids))
      assert len(g.edges["r2"].data) == 0
      expect_except = False
      try:
          g.edges["xxx"].data["x"]
      except dgl.DGLError:
          expect_except = True
      assert expect_except
  
      # Test edge_subgraph
      sg = g.edge_subgraph({"r1": eids})
      assert sg.num_edges() == len(eids)
      assert F.array_equal(sg.edata[dgl.EID], eids)
      sg = g.edge_subgraph({("n1", "r1", "n2"): eids})
      assert sg.num_edges() == len(eids)
      assert F.array_equal(sg.edata[dgl.EID], eids)
  
      # Test init node data
      new_shape = (g.num_nodes("n1"), 2)
      g.nodes["n1"].data["test1"] = dgl.distributed.DistTensor(
          new_shape, F.int32
      )
      feats = g.nodes["n1"].data["test1"][nids]
      assert np.all(F.asnumpy(feats) == 0)
  
      # create a tensor and destroy a tensor and create it again.
      test3 = dgl.distributed.DistTensor(
          new_shape, F.float32, "test3", init_func=rand_init
      )
      del test3
      test3 = dgl.distributed.DistTensor(
          (g.num_nodes("n1"), 3), F.float32, "test3"
      )
      del test3
  
      # add tests for anonymous distributed tensor.
      test3 = dgl.distributed.DistTensor(
          new_shape, F.float32, init_func=rand_init
      )
      data = test3[0:10]
      test4 = dgl.distributed.DistTensor(
          new_shape, F.float32, init_func=rand_init
      )
      del test3
      test5 = dgl.distributed.DistTensor(
          new_shape, F.float32, init_func=rand_init
      )
      assert np.sum(F.asnumpy(test5[0:10] != data)) > 0
  
      # test a persistent tesnor
      test4 = dgl.distributed.DistTensor(
          new_shape,
          F.float32,
          "test4",
          init_func=rand_init,
          persistent=True,
      )
      del test4
      try:
          test4 = dgl.distributed.DistTensor(
              (g.num_nodes("n1"), 3), F.float32, "test4"
          )
          raise Exception("")
      except:
          pass
  
      # Test write data
      new_feats = F.ones((len(nids), 2), F.int32, F.cpu())
      g.nodes["n1"].data["test1"][nids] = new_feats
      feats = g.nodes["n1"].data["test1"][nids]
      assert np.all(F.asnumpy(feats) == 1)
  
      # Test metadata operations.
      assert len(g.nodes["n1"].data["feat"]) == g.num_nodes("n1")
      assert g.nodes["n1"].data["feat"].shape == (g.num_nodes("n1"), 1)
      assert g.nodes["n1"].data["feat"].dtype == F.int64
  
      selected_nodes = (
          np.random.randint(0, 100, size=g.num_nodes("n1")) > 30
      )
      # Test node split
      nodes = node_split(
          selected_nodes, g.get_partition_book(), ntype="n1"
      )
      nodes = F.asnumpy(nodes)
      # We only have one partition, so the local nodes are basically all nodes in the graph.
      local_nids = np.arange(g.num_nodes("n1"))
      for n in nodes:
          assert n in local_nids
  
      print("end")
  
imports: import os
  import math
  import multiprocessing
  import pickle
  import socket
  import sys
  import time
  import unittest
  from multiprocessing import Condition, Manager, Process, Value
  import backend
  import dgl
  import numpy
  import pytest
  import torch
  from dgl.data.utils import load_graphs, save_graphs
  from dgl.distributed import DistEmbedding, DistGraph, DistGraphServer, edge_split, load_partition, load_partition_book, node_split, partition_graph
  from dgl.distributed.optim import SparseAdagrad
  from dgl.heterograph_index import create_unitgraph_from_coo
  from numpy.testing import assert_almost_equal, assert_array_equal
  from scipy import sparse
  from utils import create_random_graph, generate_ip_config, reset_envs
label_desc: Use image node_id 19 for calling a global function with example usage: check_dist_graph_hetero(g, num_clients, num_nodes, num_edges) without return types