class_docstr: A (possibly unbounded) box in :math:`\mathbb{R}^n`.
  
  Specifically, a Box represents the Cartesian product of n closed intervals.
  Each interval has the form of one of :math:`[a, b]`, :math:`(-\infty, b]`,
  :math:`[a, \infty)`, or :math:`(-\infty, \infty)`.
  
  There are two common use cases:
  
  * Identical bound for each dimension::
  
      >>> Box(low=-1.0, high=2.0, shape=(3, 4), dtype=np.float32)
      Box(3, 4)
  
  * Independent bound for each dimension::
  
      >>> Box(low=np.array([-1.0, -2.0]), high=np.array([2.0, 4.0]), dtype=np.float32)
      Box(2,)
docstr: Constructor of :class:`Box`.
  
  The argument ``low`` specifies the lower bound of each dimension and ``high`` specifies the upper bounds.
  I.e., the space that is constructed will be the product of the intervals :math:`[\text{low}[i], \text{high}[i]]`.
  
  If ``low`` (or ``high``) is a scalar, the lower bound (or upper bound, respectively) will be assumed to be
  this value across all dimensions.
  
  Args:
      low (Union[SupportsFloat, np.ndarray]): Lower bounds of the intervals.
      high (Union[SupportsFloat, np.ndarray]): Upper bounds of the intervals.
      shape (Optional[Sequence[int]]): The shape is inferred from the shape of `low` or `high` `np.ndarray`s with
          `low` and `high` scalars defaulting to a shape of (1,)
      dtype: The dtype of the elements of the space. If this is an integer type, the :class:`Box` is essentially a discrete space.
      seed: Optionally, you can use this argument to seed the RNG that is used to sample from the space.
  
  Raises:
      ValueError: If no shape information is provided (shape is None, low is None and high is None) then a
          value error is raised.
docstr_tok: ["Constructor","of",":","class",":","`","Box","`",".","The","argument","``","low","``","specifies","the","lower","bound","of","each","dimension","and","``","high","``","specifies","the","upper","bounds",".","I.e.",",","the","space","that","is","constructed","will","be","the","product","of","the","intervals",":","math",":","`","[","\\text","{","low","}","[","i","]",",","\\text","{","high","}","[","i","]","]","`",".","If","``","low","``","(","or","``","high","``",")","is","a","scalar",",","the","lower","bound","(","or","upper","bound",",","respectively",")","will","be","assumed","to","be","this","value","across","all","dimensions",".","Args",":","low","(","Union","[","SupportsFloat",",","np.ndarray","]",")",":","Lower","bounds","of","the","intervals",".","high","(","Union","[","SupportsFloat",",","np.ndarray","]",")",":","Upper","bounds","of","the","intervals",".","shape","(","Optional","[","Sequence","[","int","]","]",")",":","The","shape","is","inferred","from","the","shape","of","`","low","`","or","`","high","`","`","np.ndarray","`","s","with","`","low","`","and","`","high","`","scalars","defaulting","to","a","shape","of","(","1",",",")","dtype",":","The","dtype","of","the","elements","of","the","space",".","If","this","is","an","integer","type",",","the",":","class",":","`","Box","`","is","essentially","a","discrete","space",".","seed",":","Optionally",",","you","can","use","this","argument","to","seed","the","RNG","that","is","used","to","sample","from","the","space",".","Raises",":","ValueError",":","If","no","shape","information","is","provided","(","shape","is","None",",","low","is","None","and","high","is","None",")","then","a","value","error","is","raised","."]
code: def __init__(
      self,
      low: Union[SupportsFloat, np.ndarray],
      high: Union[SupportsFloat, np.ndarray],
      shape: Optional[Sequence[int]] = None,
      dtype: Type = np.float32,
      seed: Optional[Union[int, np.random.Generator]] = None,
  ):
      r"""Constructor of :class:`Box`.
  
      The argument ``low`` specifies the lower bound of each dimension and ``high`` specifies the upper bounds.
      I.e., the space that is constructed will be the product of the intervals :math:`[\text{low}[i], \text{high}[i]]`.
  
      If ``low`` (or ``high``) is a scalar, the lower bound (or upper bound, respectively) will be assumed to be
      this value across all dimensions.
  
      Args:
          low (Union[SupportsFloat, np.ndarray]): Lower bounds of the intervals.
          high (Union[SupportsFloat, np.ndarray]): Upper bounds of the intervals.
          shape (Optional[Sequence[int]]): The shape is inferred from the shape of `low` or `high` `np.ndarray`s with
              `low` and `high` scalars defaulting to a shape of (1,)
          dtype: The dtype of the elements of the space. If this is an integer type, the :class:`Box` is essentially a discrete space.
          seed: Optionally, you can use this argument to seed the RNG that is used to sample from the space.
  
      Raises:
          ValueError: If no shape information is provided (shape is None, low is None and high is None) then a
              value error is raised.
      """
      assert (
          dtype is not None
      ), "Box dtype must be explicitly provided, cannot be None."
      self.dtype = np.dtype(dtype)
  
      # determine shape if it isn't provided directly
      if shape is not None:
          assert all(
              np.issubdtype(type(dim), np.integer) for dim in shape
          ), f"Expect all shape elements to be an integer, actual type: {tuple(type(dim) for dim in shape)}"
          shape = tuple(
              int(dim) for dim in shape
          )  # This changes any np types to int
      elif isinstance(low, np.ndarray):
          shape = low.shape
      elif isinstance(high, np.ndarray):
          shape = high.shape
      elif is_float_integer(low) and is_float_integer(high):
          shape = (1,)
      else:
          raise ValueError(
              f"Box shape is inferred from low and high, expect their types to be np.ndarray, an integer or a float, actual type low: {type(low)}, high: {type(high)}"
          )
  
      # Capture the boundedness information before replacing np.inf with get_inf
      _low = (
          np.full(shape, low, dtype=float)
          if is_float_integer(low)
          else low
      )
      self.bounded_below = -np.inf < _low
      _high = (
          np.full(shape, high, dtype=float)
          if is_float_integer(high)
          else high
      )
      self.bounded_above = np.inf > _high
  
      low = _broadcast(low, dtype, shape, inf_sign="-")  # type: ignore
      high = _broadcast(high, dtype, shape, inf_sign="+")  # type: ignore
  
      assert isinstance(low, np.ndarray)
      assert (
          low.shape == shape
      ), f"low.shape doesn't match provided shape, low.shape: {low.shape}, shape: {shape}"
      assert isinstance(high, np.ndarray)
      assert (
          high.shape == shape
      ), f"high.shape doesn't match provided shape, high.shape: {high.shape}, shape: {shape}"
  
      self._shape: Tuple[int, ...] = shape
  
      low_precision = get_precision(low.dtype)
      high_precision = get_precision(high.dtype)
      dtype_precision = get_precision(self.dtype)
      if min(low_precision, high_precision) > dtype_precision:  # type: ignore
          logger.warn(
              f"Box bound precision lowered by casting to {self.dtype}"
          )
      self.low = low.astype(self.dtype)
      self.high = high.astype(self.dtype)
  
      self.low_repr = _short_repr(self.low)
      self.high_repr = _short_repr(self.high)
  
      super().__init__(self.shape, self.dtype, seed)
  
imports: from typing import Dict, List, Optional, Sequence, SupportsFloat, Tuple, Type, Union
  import numpy
  import gym.error
  from gym import logger
  from gym.spaces.space import Space
label_desc: Use image node_id 1 to create a new Box object from inherited base classes: Space with example: obj = Box(low, high, shape, dtype, seed)