class_docstr: ProcessGroup
  Process Group indicates how processes are organized in groups for parallel execution using Tensor Parallelism and Data Parallelism.
  
  NOTE, the ProcessGroup must be used after `torch.distributed.initialize()`
  
  
  Args:
      rank: the global rank of the current process.
      ranks: List[int], a list of rank id belongings to this process group.
      backend: str, the backend of the process group.
      tp_degree: Optional[int], tensor parallelism degree. How many processes are inside a tp process group. default None means 1.
      dp_degree: Optional[int], data parallelism degree. How many processes are inside a dp process group. . default None means len(ranks).
code: def __init__(
      self,
      rank: Optional[int] = None,
      ranks: Optional[List[int]] = None,
      tp_degree: Optional[int] = None,
      dp_degree: Optional[int] = None,
  ) -> None:
      if not torch.distributed.is_initialized():
          self.is_init = False
          return
      global PYTORCHPGDICT_
      if PYTORCHPGDICT_ is None:
          PYTORCHPGDICT_ = PyTorchProcessGroupDict()
  
      assert (
          torch.distributed.is_initialized()
      ), f"ProcessGroup must be used after distributed initialized"
  
      self._rank = torch.distributed.get_rank()
      if rank is not None:
          assert (
              self._rank == rank
          )  # make sure that the global rank is correct
  
      if ranks is None:
          self._rank_list = list(
              range(torch.distributed.get_world_size())
          )
      else:
          self._rank_list = ranks
          self._rank_list.sort()  # ensure that the list is in order
  
      self._world_size = len(self._rank_list)
  
      if dp_degree is None and tp_degree is None:
          self._dp_degree = self._world_size
          self._tp_degree = 1
      elif dp_degree and not tp_degree:
          self._dp_degree = dp_degree
          assert (
              self._world_size % self._dp_degree == 0
          ), f"DP degree {dp_degree} should be divisible by {self._world_size} hen DP degree is None"
          self._tp_degree = self._world_size // dp_degree
      elif not dp_degree and tp_degree:
          self._tp_degree = tp_degree
          assert (
              self._world_size % self._tp_degree == 0
          ), f"TP degree {tp_degree} should be divisible by {self._world_size} when DP degree is None"
          self._dp_degree = self._world_size // tp_degree
      else:
          self._dp_degree = dp_degree
          self._tp_degree = tp_degree
          assert (
              self._dp_degree * self._tp_degree == self._world_size
          ), (
              f"the world size {self._world_size} should equals to the product of DP degree {self._dp_degree}"
              f"and TP degree {self._tp_degree}"
          )
  
      self._tp_rank_list = None
      self._dp_rank_list = None
  
      for i in range(self._dp_degree):
          i_tp_list = [
              self._rank_list[i * self._tp_degree + j]
              for j in range(self._tp_degree)
          ]
          PYTORCHPGDICT_.get(i_tp_list, "nccl")
          if self._rank in i_tp_list:
              self._tp_rank_list = i_tp_list
  
      for j in range(self._tp_degree):
          j_dp_list = [
              self._rank_list[i * self._tp_degree + j]
              for i in range(self._dp_degree)
          ]
          PYTORCHPGDICT_.get(j_dp_list, "nccl")
          if self._rank in j_dp_list:
              self._dp_rank_list = j_dp_list
  
      self._has_cpu_groups = False
      self.is_init = True
  
imports: from typing import List, Optional
  import torch
  from colossalai.context.singleton_meta import SingletonMeta
  from colossalai.logging import get_dist_logger
label_desc: Use image node_id 1 to create a new ProcessGroup object  with example: obj = ProcessGroup(rank, ranks, tp_degree, dp_degree)