code: def MeshGrid2DGraph(
      size: int, shape: Optional[Tuple[int, int]] = None
  ) -> nx.DiGraph:
      """Generate 2D MeshGrid structure of graph.
  
      Assume shape = (nrow, ncol), when shape is provided, a meshgrid of nrow*ncol will be generated.
      when shape is not provided, nrow and ncol will be the two closest factors of size.
  
      For example: size = 24, nrow and ncol will be 4 and 6, respectively.
      We assume  nrow will be equal to or smaller than ncol.
      If size is a prime number, nrow will be 1, and ncol will be size, which degrades the topology
      into a linear one.
  
      Example: A MeshGrid2DGraph with 16 nodes:
  
      .. plot::
          :context: close-figs
  
          >>> import networkx as nx
          >>> from bluefog.common import topology_util
          >>> G = topology_util.MeshGrid2DGraph(16)
          >>> nx.draw_spring(G)
      """
  
      assert size > 0
      if shape is None:
          i = int(np.sqrt(size))
          while size % i != 0:
              i -= 1
          shape = (i, size // i)
      nrow, ncol = shape
      assert (
          size == nrow * ncol
      ), "The shape doesn't match the size provided."
      topo = np.zeros((size, size))
      for i in range(size):
          topo[i][i] = 1.0
          if (i + 1) % ncol != 0:
              topo[i][i + 1] = 1.0
              topo[i + 1][i] = 1.0
          if i + ncol < size:
              topo[i][i + ncol] = 1.0
              topo[i + ncol][i] = 1.0
  
      # According to Hasting rule (Policy 1) in https://arxiv.org/pdf/1702.05122.pdf
      # The neighbor definition in the paper is different from our implementation,
      # which includes the self node.
      topo_neighbor_with_self = [
          np.nonzero(topo[i])[0] for i in range(size)
      ]
      for i in range(size):
          for j in topo_neighbor_with_self[i]:
              if i != j:
                  topo[i][j] = 1.0 / max(
                      len(topo_neighbor_with_self[i]),
                      len(topo_neighbor_with_self[j]),
                  )
          topo[i][i] = 2.0 - topo[i].sum()
      G = nx.from_numpy_array(topo, create_using=nx.DiGraph)
      return G
  
imports: from typing import List, Tuple, Dict, Iterator, Optional
  import math
  import numpy
  import networkx
label_desc: Use image node_id 8 for calling a global function with example usage: MeshGrid2DGraph(size, shape) and returns: G