class_docstr: This represents the cartesian product of arbitrary :class:`Discrete` spaces.
  
  It is useful to represent game controllers or keyboards where each key can be represented as a discrete action space.
  
  Note:
      Some environment wrappers assume a value of 0 always represents the NOOP action.
  
  e.g. Nintendo Game Controller - Can be conceptualized as 3 discrete action spaces:
  
  1. Arrow Keys: Discrete 5  - NOOP[0], UP[1], RIGHT[2], DOWN[3], LEFT[4]  - params: min: 0, max: 4
  2. Button A:   Discrete 2  - NOOP[0], Pressed[1] - params: min: 0, max: 1
  3. Button B:   Discrete 2  - NOOP[0], Pressed[1] - params: min: 0, max: 1
  
  It can be initialized as ``MultiDiscrete([ 5, 2, 2 ])`` such that a sample might be ``array([3, 1, 0])``.
  
  Although this feature is rarely used, :class:`MultiDiscrete` spaces may also have several axes
  if ``nvec`` has several axes:
  
  Example::
  
      >> d = MultiDiscrete(np.array([[1, 2], [3, 4]]))
      >> d.sample()
      array([[0, 0],
             [2, 3]])
class_docstr_tok: ["This","represents","the","cartesian","product","of","arbitrary",":","class",":","`","Discrete","`","spaces",".","It","is","useful","to","represent","game","controllers","or","keyboards","where","each","key","can","be","represented","as","a","discrete","action","space",".","Note",":","Some","environment","wrappers","assume","a","value","of","0","always","represents","the","NOOP","action",".","e.g",".","Nintendo","Game","Controller","-","Can","be","conceptualized","as","3","discrete","action","spaces",":","1",".","Arrow","Keys",":","Discrete","5","-","NOOP","[","0","]",",","UP","[","1","]",",","RIGHT","[","2","]",",","DOWN","[","3","]",",","LEFT","[","4","]","-","params",":","min",":","0",",","max",":","4","2",".","Button","A",":","Discrete","2","-","NOOP","[","0","]",",","Pressed","[","1","]","-","params",":","min",":","0",",","max",":","1","3",".","Button","B",":","Discrete","2","-","NOOP","[","0","]",",","Pressed","[","1","]","-","params",":","min",":","0",",","max",":","1","It","can","be","initialized","as","``","MultiDiscrete","(","[","5",",","2",",","2","]",")","``","such","that","a","sample","might","be","``","array","(","[","3",",","1",",","0","]",")","``",".","Although","this","feature","is","rarely","used",",",":","class",":","`","MultiDiscrete","`","spaces","may","also","have","several","axes","if","``","nvec","``","has","several","axes",":","Example",":",":",">",">","d","=","MultiDiscrete","(","np.array","(","[","[","1",",","2","]",",","[","3",",","4","]","]",")",")",">",">","d.sample","(",")","array","(","[","[","0",",","0","]",",","[","2",",","3","]","]",")"]
docstr: Generates a single random sample this space.
  
  Args:
      mask: An optional mask for multi-discrete, expects tuples with a `np.ndarray` mask in the position of each
          action with shape `(n,)` where `n` is the number of actions and `dtype=np.int8`.
          Only mask values == 1 are possible to sample unless all mask values for an action are 0 then the default action 0 is sampled.
  
  Returns:
      An `np.ndarray` of shape `space.shape`
docstr_tok: ["Generates","a","single","random","sample","this","space",".","Args",":","mask",":","An","optional","mask","for","multi-discrete",",","expects","tuples","with","a","`","np.ndarray","`","mask","in","the","position","of","each","action","with","shape","`","(","n",",",")","`","where","`","n","`","is","the","number","of","actions","and","`","dtype=np.int8","`",".","Only","mask","values","==","1","are","possible","to","sample","unless","all","mask","values","for","an","action","are","0","then","the","default","action","0","is","sampled",".","Returns",":","An","`","np.ndarray","`","of","shape","`","space.shape","`"]
code: def sample(self, mask: Optional[tuple] = None) -> np.ndarray:
      """Generates a single random sample this space.
  
      Args:
          mask: An optional mask for multi-discrete, expects tuples with a `np.ndarray` mask in the position of each
              action with shape `(n,)` where `n` is the number of actions and `dtype=np.int8`.
              Only mask values == 1 are possible to sample unless all mask values for an action are 0 then the default action 0 is sampled.
  
      Returns:
          An `np.ndarray` of shape `space.shape`
      """
      if mask is not None:
  
          def _apply_mask(
              sub_mask: Union[np.ndarray, tuple],
              sub_nvec: Union[np.ndarray, np.integer],
          ) -> Union[int, List[int]]:
              if isinstance(sub_nvec, np.ndarray):
                  assert isinstance(
                      sub_mask, tuple
                  ), f"Expects the mask to be a tuple for sub_nvec ({sub_nvec}), actual type: {type(sub_mask)}"
                  assert len(sub_mask) == len(
                      sub_nvec
                  ), f"Expects the mask length to be equal to the number of actions, mask length: {len(sub_mask)}, nvec length: {len(sub_nvec)}"
                  return [
                      _apply_mask(new_mask, new_nvec)
                      for new_mask, new_nvec in zip(sub_mask, sub_nvec)
                  ]
              else:
                  assert np.issubdtype(
                      type(sub_nvec), np.integer
                  ), f"Expects the sub_nvec to be an action, actually: {sub_nvec}, {type(sub_nvec)}"
                  assert isinstance(
                      sub_mask, np.ndarray
                  ), f"Expects the sub mask to be np.ndarray, actual type: {type(sub_mask)}"
                  assert (
                      len(sub_mask) == sub_nvec
                  ), f"Expects the mask length to be equal to the number of actions, mask length: {len(sub_mask)}, action: {sub_nvec}"
                  assert (
                      sub_mask.dtype == np.int8
                  ), f"Expects the mask dtype to be np.int8, actual dtype: {sub_mask.dtype}"
  
                  valid_action_mask = sub_mask == 1
                  assert np.all(
                      np.logical_or(sub_mask == 0, valid_action_mask)
                  ), f"Expects all masks values to 0 or 1, actual values: {sub_mask}"
  
                  if np.any(valid_action_mask):
                      return self.np_random.choice(
                          np.where(valid_action_mask)[0]
                      )
                  else:
                      return 0
  
          return np.array(
              _apply_mask(mask, self.nvec), dtype=self.dtype
          )
  
      return (
          self.np_random.random(self.nvec.shape) * self.nvec
      ).astype(self.dtype)
  
file_path: gym/gym/spaces/multi_discrete.py
imports: from typing import Iterable, List, Optional, Sequence, Tuple, Union
  import numpy
  from gym import logger
  from gym.spaces.discrete import Discrete
  from gym.spaces.space import Space
label_desc: Use image node_id 4 for calling the MultiDiscrete obj's underlying member method code with example usage: obj.sample(mask) and returns: unknown, np, unknown, self, int