class_docstr: Execute an FX graph Node-by-Node and record the meta data of the result
  into the corresponding node.
  
  Usage:
      >>> model = MyModule()
      >>> x = torch.rand(10, 10)
      >>> gm = colossalai.fx.symbolic_trace(model, meta_args = {'x': x})
      >>> interp = ShapeProp(gm)
      >>> interp.propagate(x)
  
  Args:
      module (GraphModule): The module to be executed
  
  Hints:
      If you want to add a new shape propagation rule, you can do so by
      adding a new method to this class with the ``@register_shape_impl``
      decorator. The method should take (*args, **kwargs) instance as its
      input and generate output.
  
      For example, if you want to add a shape propagation rule for
      ``torch.nn.functional.linear``, you can do so by adding a new method
      to this class with the ``@register_shape_impl`` decorator (Since the
      ``MetaTensorMode`` is compatible with ``torch.nn.functional.linear``,
      in practice you don't have to do as follows):
  
      >>> @register_shape_impl(torch.nn.functional.linear)
      >>> def linear_shape_impl(*args, **kwargs):
      >>>     # do something here
      >>>     return torch.empty(output_shape, device=output_device)
code: def __init__(
      self,
      module: torch.fx.GraphModule,
      garbage_collect_values: bool = True,
  ):
      super().__init__(module, garbage_collect_values)
      self.global_hook = sim_env(module=self.module)
  
imports: from typing import Any, Callable, Dict, Optional, Tuple, Union
  import torch
  import torch.fx
  from torch.autograd.graph import saved_tensors_hooks
  from torch.utils._pytree import tree_map
  from colossalai._analyzer._subclasses import MetaTensor, MetaTensorMode
  from colossalai._analyzer.fx.node_util import MetaInfo
  from colossalai.fx._compatibility import compatibility
label_desc: Use image node_id 1 to create a new ShapeProp object from inherited base classes: torch.fx with example: obj = ShapeProp(module, garbage_collect_values)